gsctl 0.29.0a20250114__py2.py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. graphscope/flex/rest/__init__.py +106 -0
  2. graphscope/flex/rest/api/__init__.py +12 -0
  3. graphscope/flex/rest/api/alert_api.py +2790 -0
  4. graphscope/flex/rest/api/data_source_api.py +1177 -0
  5. graphscope/flex/rest/api/deployment_api.py +1323 -0
  6. graphscope/flex/rest/api/graph_api.py +2813 -0
  7. graphscope/flex/rest/api/job_api.py +1408 -0
  8. graphscope/flex/rest/api/service_api.py +1316 -0
  9. graphscope/flex/rest/api/stored_procedure_api.py +1454 -0
  10. graphscope/flex/rest/api/utils_api.py +310 -0
  11. graphscope/flex/rest/api_client.py +789 -0
  12. graphscope/flex/rest/api_response.py +21 -0
  13. graphscope/flex/rest/configuration.py +451 -0
  14. graphscope/flex/rest/exceptions.py +200 -0
  15. graphscope/flex/rest/models/__init__.py +82 -0
  16. graphscope/flex/rest/models/base_edge_type.py +102 -0
  17. graphscope/flex/rest/models/base_edge_type_vertex_type_pair_relations_inner.py +108 -0
  18. graphscope/flex/rest/models/base_edge_type_vertex_type_pair_relations_inner_x_csr_params.py +98 -0
  19. graphscope/flex/rest/models/base_property_meta.py +105 -0
  20. graphscope/flex/rest/models/base_vertex_type.py +96 -0
  21. graphscope/flex/rest/models/base_vertex_type_x_csr_params.py +88 -0
  22. graphscope/flex/rest/models/column_mapping.py +94 -0
  23. graphscope/flex/rest/models/column_mapping_column.py +90 -0
  24. graphscope/flex/rest/models/create_alert_receiver_request.py +103 -0
  25. graphscope/flex/rest/models/create_alert_rule_request.py +112 -0
  26. graphscope/flex/rest/models/create_dataloading_job_response.py +88 -0
  27. graphscope/flex/rest/models/create_edge_type.py +114 -0
  28. graphscope/flex/rest/models/create_graph_request.py +106 -0
  29. graphscope/flex/rest/models/create_graph_response.py +88 -0
  30. graphscope/flex/rest/models/create_graph_schema_request.py +106 -0
  31. graphscope/flex/rest/models/create_property_meta.py +105 -0
  32. graphscope/flex/rest/models/create_stored_proc_request.py +101 -0
  33. graphscope/flex/rest/models/create_stored_proc_response.py +88 -0
  34. graphscope/flex/rest/models/create_vertex_type.py +108 -0
  35. graphscope/flex/rest/models/dataloading_job_config.py +136 -0
  36. graphscope/flex/rest/models/dataloading_job_config_edges_inner.py +92 -0
  37. graphscope/flex/rest/models/dataloading_job_config_loading_config.py +104 -0
  38. graphscope/flex/rest/models/dataloading_job_config_loading_config_format.py +90 -0
  39. graphscope/flex/rest/models/dataloading_job_config_vertices_inner.py +88 -0
  40. graphscope/flex/rest/models/dataloading_mr_job_config.py +88 -0
  41. graphscope/flex/rest/models/date_type.py +88 -0
  42. graphscope/flex/rest/models/edge_mapping.py +122 -0
  43. graphscope/flex/rest/models/edge_mapping_type_triplet.py +92 -0
  44. graphscope/flex/rest/models/error.py +90 -0
  45. graphscope/flex/rest/models/get_alert_message_response.py +123 -0
  46. graphscope/flex/rest/models/get_alert_receiver_response.py +107 -0
  47. graphscope/flex/rest/models/get_alert_rule_response.py +114 -0
  48. graphscope/flex/rest/models/get_edge_type.py +116 -0
  49. graphscope/flex/rest/models/get_graph_response.py +139 -0
  50. graphscope/flex/rest/models/get_graph_schema_response.py +106 -0
  51. graphscope/flex/rest/models/get_pod_log_response.py +88 -0
  52. graphscope/flex/rest/models/get_property_meta.py +107 -0
  53. graphscope/flex/rest/models/get_resource_usage_response.py +105 -0
  54. graphscope/flex/rest/models/get_storage_usage_response.py +88 -0
  55. graphscope/flex/rest/models/get_stored_proc_response.py +130 -0
  56. graphscope/flex/rest/models/get_vertex_type.py +110 -0
  57. graphscope/flex/rest/models/gs_data_type.py +152 -0
  58. graphscope/flex/rest/models/job_status.py +107 -0
  59. graphscope/flex/rest/models/long_text.py +93 -0
  60. graphscope/flex/rest/models/node_status.py +94 -0
  61. graphscope/flex/rest/models/parameter.py +96 -0
  62. graphscope/flex/rest/models/pod_status.py +108 -0
  63. graphscope/flex/rest/models/primitive_type.py +95 -0
  64. graphscope/flex/rest/models/resource_usage.py +92 -0
  65. graphscope/flex/rest/models/running_deployment_info.py +128 -0
  66. graphscope/flex/rest/models/running_deployment_status.py +124 -0
  67. graphscope/flex/rest/models/schema_mapping.py +106 -0
  68. graphscope/flex/rest/models/service_status.py +112 -0
  69. graphscope/flex/rest/models/service_status_sdk_endpoints.py +94 -0
  70. graphscope/flex/rest/models/start_service_request.py +88 -0
  71. graphscope/flex/rest/models/stored_procedure_meta.py +126 -0
  72. graphscope/flex/rest/models/string_type.py +92 -0
  73. graphscope/flex/rest/models/string_type_string.py +124 -0
  74. graphscope/flex/rest/models/temporal_type.py +92 -0
  75. graphscope/flex/rest/models/temporal_type_temporal.py +138 -0
  76. graphscope/flex/rest/models/time_stamp_type.py +88 -0
  77. graphscope/flex/rest/models/update_alert_message_status_request.py +97 -0
  78. graphscope/flex/rest/models/update_stored_proc_request.py +88 -0
  79. graphscope/flex/rest/models/upload_file_response.py +90 -0
  80. graphscope/flex/rest/models/vertex_mapping.py +100 -0
  81. graphscope/flex/rest/py.typed +0 -0
  82. graphscope/flex/rest/rest.py +258 -0
  83. graphscope/gsctl/V6D_VERSION +1 -0
  84. graphscope/gsctl/VERSION +1 -0
  85. graphscope/gsctl/__init__.py +22 -0
  86. graphscope/gsctl/commands/__init__.py +148 -0
  87. graphscope/gsctl/commands/common.py +200 -0
  88. graphscope/gsctl/commands/dev.py +448 -0
  89. graphscope/gsctl/commands/insight/__init__.py +17 -0
  90. graphscope/gsctl/commands/insight/glob.py +234 -0
  91. graphscope/gsctl/commands/insight/graph.py +205 -0
  92. graphscope/gsctl/commands/interactive/__init__.py +17 -0
  93. graphscope/gsctl/commands/interactive/glob.py +280 -0
  94. graphscope/gsctl/commands/interactive/graph.py +259 -0
  95. graphscope/gsctl/config.py +221 -0
  96. graphscope/gsctl/gsctl.py +51 -0
  97. graphscope/gsctl/impl/__init__.py +64 -0
  98. graphscope/gsctl/impl/alert.py +135 -0
  99. graphscope/gsctl/impl/common.py +53 -0
  100. graphscope/gsctl/impl/datasource.py +80 -0
  101. graphscope/gsctl/impl/deployment.py +62 -0
  102. graphscope/gsctl/impl/graph.py +150 -0
  103. graphscope/gsctl/impl/job.py +63 -0
  104. graphscope/gsctl/impl/service.py +62 -0
  105. graphscope/gsctl/impl/stored_procedure.py +92 -0
  106. graphscope/gsctl/impl/utils.py +38 -0
  107. graphscope/gsctl/scripts/install_deps.sh +969 -0
  108. graphscope/gsctl/tests/__init__.py +17 -0
  109. graphscope/gsctl/tests/test_graphscope_insight.py +401 -0
  110. graphscope/gsctl/tests/test_interactive.py +516 -0
  111. graphscope/gsctl/utils.py +337 -0
  112. graphscope/gsctl/version.py +31 -0
  113. gsctl-0.29.0a20250114.dist-info/METADATA +20 -0
  114. gsctl-0.29.0a20250114.dist-info/RECORD +117 -0
  115. gsctl-0.29.0a20250114.dist-info/WHEEL +6 -0
  116. gsctl-0.29.0a20250114.dist-info/entry_points.txt +3 -0
  117. gsctl-0.29.0a20250114.dist-info/top_level.txt +1 -0
@@ -0,0 +1,516 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ #
4
+ # Copyright 2020 Alibaba Group Holding Limited. All Rights Reserved.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+
19
+ import os
20
+ import warnings
21
+
22
+ # Disable warnings
23
+ warnings.filterwarnings("ignore", category=Warning)
24
+
25
+ import time
26
+
27
+ import pytest
28
+
29
+ from graphscope.gsctl.impl import bind_datasource_in_batch
30
+ from graphscope.gsctl.impl import connect_coordinator
31
+ from graphscope.gsctl.impl import create_graph
32
+ from graphscope.gsctl.impl import create_stored_procedure
33
+ from graphscope.gsctl.impl import delete_graph_by_id
34
+ from graphscope.gsctl.impl import delete_stored_procedure_by_id
35
+ from graphscope.gsctl.impl import disconnect_coordinator
36
+ from graphscope.gsctl.impl import get_datasource_by_id
37
+ from graphscope.gsctl.impl import get_job_by_id
38
+ from graphscope.gsctl.impl import get_stored_procedure_by_id
39
+ from graphscope.gsctl.impl import list_graphs
40
+ from graphscope.gsctl.impl import list_service_status
41
+ from graphscope.gsctl.impl import list_stored_procedures
42
+ from graphscope.gsctl.impl import restart_service
43
+ from graphscope.gsctl.impl import start_service
44
+ from graphscope.gsctl.impl import stop_service
45
+ from graphscope.gsctl.impl import submit_dataloading_job
46
+ from graphscope.gsctl.impl import unbind_edge_datasource
47
+ from graphscope.gsctl.impl import unbind_vertex_datasource
48
+ from graphscope.gsctl.impl import update_stored_procedure_by_id
49
+ from graphscope.gsctl.impl import upload_file
50
+
51
+ COORDINATOR_ENDPOINT = "http://127.0.0.1:8080"
52
+
53
+
54
+ sample_cc = """
55
+ #include "flex/engines/hqps_db/app/interactive_app_base.h"
56
+ #include "flex/engines/hqps_db/core/sync_engine.h"
57
+ #include "flex/utils/app_utils.h"
58
+
59
+ namespace gs {
60
+ class ExampleQuery : public CypherReadAppBase<int32_t> {
61
+ public:
62
+ using Engine = SyncEngine<gs::MutableCSRInterface>;
63
+ using label_id_t = typename gs::MutableCSRInterface::label_id_t;
64
+ using vertex_id_t = typename gs::MutableCSRInterface::vertex_id_t;
65
+ ExampleQuery() {}
66
+ // Query function for query class
67
+ results::CollectiveResults Query(const gs::GraphDBSession& sess,
68
+ int32_t param1) override {
69
+ LOG(INFO) << "param1: " << param1;
70
+ gs::MutableCSRInterface graph(sess);
71
+ auto ctx0 = Engine::template ScanVertex<gs::AppendOpt::Persist>(
72
+ graph, 0, Filter<TruePredicate>());
73
+
74
+ auto ctx1 = Engine::Project<PROJ_TO_NEW>(
75
+ graph, std::move(ctx0),
76
+ std::tuple{gs::make_mapper_with_variable<INPUT_COL_ID(0)>(
77
+ gs::PropertySelector<int64_t>("id"))});
78
+ auto ctx2 = Engine::Limit(std::move(ctx1), 0, 5);
79
+ auto res = Engine::Sink(graph, ctx2, std::array<int32_t, 1>{0});
80
+ LOG(INFO) << "res: " << res.DebugString();
81
+ return res;
82
+ }
83
+ };
84
+ } // namespace gs
85
+
86
+ extern "C" {
87
+ void* CreateApp(gs::GraphDBSession& db) {
88
+ gs::ExampleQuery* app = new gs::ExampleQuery();
89
+ return static_cast<void*>(app);
90
+ }
91
+
92
+ void DeleteApp(void* app) {
93
+ gs::ExampleQuery* casted = static_cast<gs::ExampleQuery*>(app);
94
+ delete casted;
95
+ }
96
+ }
97
+ """
98
+
99
+ modern_graph = {
100
+ "name": "modern_graph",
101
+ "description": "This is a test graph",
102
+ "schema": {
103
+ "vertex_types": [
104
+ {
105
+ "type_name": "person",
106
+ "properties": [
107
+ {
108
+ "property_name": "id",
109
+ "property_type": {"primitive_type": "DT_SIGNED_INT64"},
110
+ },
111
+ {
112
+ "property_name": "name",
113
+ "property_type": {"string": {"long_text": ""}},
114
+ },
115
+ {
116
+ "property_name": "age",
117
+ "property_type": {"primitive_type": "DT_SIGNED_INT32"},
118
+ },
119
+ {
120
+ "property_name": "birthday",
121
+ "property_type": {"temporal": {"timestamp": ""}},
122
+ },
123
+ ],
124
+ "primary_keys": ["id"],
125
+ }
126
+ ],
127
+ "edge_types": [
128
+ {
129
+ "type_name": "knows",
130
+ "vertex_type_pair_relations": [
131
+ {
132
+ "source_vertex": "person",
133
+ "destination_vertex": "person",
134
+ "relation": "MANY_TO_MANY",
135
+ }
136
+ ],
137
+ "properties": [
138
+ {
139
+ "property_name": "weight",
140
+ "property_type": {"primitive_type": "DT_DOUBLE"},
141
+ }
142
+ ],
143
+ "primary_keys": [],
144
+ }
145
+ ],
146
+ },
147
+ }
148
+
149
+
150
+ modern_graph_with_empty_edge_property = {
151
+ "name": "modern_graph",
152
+ "description": "This is a test graph",
153
+ "schema": {
154
+ "vertex_types": [
155
+ {
156
+ "type_name": "person",
157
+ "properties": [
158
+ {
159
+ "property_name": "id",
160
+ "property_type": {"primitive_type": "DT_SIGNED_INT64"},
161
+ },
162
+ {
163
+ "property_name": "name",
164
+ "property_type": {"string": {"long_text": ""}},
165
+ },
166
+ {
167
+ "property_name": "age",
168
+ "property_type": {"primitive_type": "DT_SIGNED_INT32"},
169
+ },
170
+ ],
171
+ "primary_keys": ["id"],
172
+ }
173
+ ],
174
+ "edge_types": [
175
+ {
176
+ "type_name": "knows",
177
+ "vertex_type_pair_relations": [
178
+ {
179
+ "source_vertex": "person",
180
+ "destination_vertex": "person",
181
+ "relation": "MANY_TO_MANY",
182
+ }
183
+ ],
184
+ "properties": [],
185
+ "primary_keys": [],
186
+ }
187
+ ],
188
+ },
189
+ }
190
+
191
+
192
+ modern_graph_vertex_only = {
193
+ "name": "modern_graph",
194
+ "description": "This is a test graph, only contains vertex",
195
+ "schema": {
196
+ "vertex_types": [
197
+ {
198
+ "type_name": "person",
199
+ "properties": [
200
+ {
201
+ "property_name": "id",
202
+ "property_type": {"primitive_type": "DT_SIGNED_INT64"},
203
+ },
204
+ {
205
+ "property_name": "name",
206
+ "property_type": {"string": {"long_text": ""}},
207
+ },
208
+ {
209
+ "property_name": "age",
210
+ "property_type": {"primitive_type": "DT_SIGNED_INT32"},
211
+ },
212
+ ],
213
+ "primary_keys": ["id"],
214
+ }
215
+ ],
216
+ "edge_types": [],
217
+ },
218
+ }
219
+
220
+
221
+ class TestE2EInteractive(object):
222
+ def setup_class(self):
223
+ if "COORDINATOR_ENDPOINT" in os.environ:
224
+ COORDINATOR_ENDPOINT = os.environ["COORDINATOR_ENDPOINT"]
225
+ self.deployment_info = connect_coordinator(COORDINATOR_ENDPOINT)
226
+
227
+ def test_deployment_info(self):
228
+ assert self.deployment_info.instance_name == "demo"
229
+ assert self.deployment_info.cluster_type == "HOSTS"
230
+ assert self.deployment_info.engine == "Hiactor"
231
+ assert self.deployment_info.storage == "MutableCSR"
232
+ assert self.deployment_info.frontend == "Cypher/Gremlin"
233
+ assert self.deployment_info.version is not None
234
+ assert self.deployment_info.creation_time is not None
235
+
236
+ def test_graph(self):
237
+ # test create a new graph
238
+ graph_id = create_graph(modern_graph)
239
+ assert graph_id is not None
240
+ new_graph_exist = False
241
+ graphs = list_graphs()
242
+ for g in graphs:
243
+ if g.name == "modern_graph" and g.id == graph_id:
244
+ new_graph_exist = True
245
+ assert new_graph_exist
246
+ # test delete a graph by id
247
+ delete_graph_by_id(graph_id)
248
+ new_graph_exist = False
249
+ graphs = list_graphs()
250
+ for g in graphs:
251
+ if g.name == "modern_graph" and g.id == graph_id:
252
+ new_graph_exist = True
253
+ assert not new_graph_exist
254
+
255
+ def test_bulk_loading(self, tmpdir):
256
+ # person
257
+ person = tmpdir.join("person.csv")
258
+ person.write(
259
+ "id|name|age|birthday\n"
260
+ + "1|marko|29|628646400000\n"
261
+ + "2|vadas|27|445910400000\n"
262
+ + "4|josh|32|491788800000\n"
263
+ + "6|peter|35|531273600000"
264
+ )
265
+ # person -> knows -> person
266
+ person_knows_person = tmpdir.join("person_knows_person.csv")
267
+ person_knows_person.write("person.id|person.id|weight\n1|2|0.5\n1|4|1.0")
268
+ # data source mapping
269
+ datasource = {
270
+ "vertex_mappings": [
271
+ {
272
+ "type_name": "person",
273
+ "inputs": [upload_file(str(person))],
274
+ "column_mappings": [
275
+ {"column": {"index": 0, "name": "id"}, "property": "id"},
276
+ {"column": {"index": 1, "name": "name"}, "property": "name"},
277
+ {"column": {"index": 2, "name": "age"}, "property": "age"},
278
+ {
279
+ "column": {"index": 3, "name": "birthday"},
280
+ "property": "birthday",
281
+ },
282
+ ],
283
+ }
284
+ ],
285
+ "edge_mappings": [
286
+ {
287
+ "type_triplet": {
288
+ "edge": "knows",
289
+ "source_vertex": "person",
290
+ "destination_vertex": "person",
291
+ },
292
+ "inputs": [upload_file(str(person_knows_person))],
293
+ "source_vertex_mappings": [
294
+ {"column": {"index": 0, "name": "person.id"}, "property": "id"}
295
+ ],
296
+ "destination_vertex_mappings": [
297
+ {"column": {"index": 1, "name": "person.id"}, "property": "id"}
298
+ ],
299
+ "column_mappings": [
300
+ {"column": {"index": 2, "name": "weight"}, "property": "weight"}
301
+ ],
302
+ }
303
+ ],
304
+ }
305
+ # test bind data source
306
+ graph_id = create_graph(modern_graph)
307
+ bind_datasource_in_batch(graph_id, datasource)
308
+ ds = get_datasource_by_id(graph_id)
309
+ assert ds.to_dict() == datasource
310
+ # test bulk loading
311
+ job_config = {
312
+ "loading_config": {
313
+ "import_option": "overwrite",
314
+ "format": {
315
+ "type": "csv",
316
+ "metadata": {
317
+ "delimiter": "|",
318
+ "header_row": "true",
319
+ },
320
+ },
321
+ },
322
+ "vertices": [
323
+ {"type_name": "person"},
324
+ ],
325
+ "edges": [
326
+ {
327
+ "type_name": "knows",
328
+ "source_vertex": "person",
329
+ "destination_vertex": "person",
330
+ },
331
+ ],
332
+ }
333
+ job_id = submit_dataloading_job(graph_id, job_config)
334
+ start_time = time.time()
335
+ # waiting for 30s
336
+ while True:
337
+ time.sleep(1)
338
+ status = get_job_by_id(job_id)
339
+ if status.status == "SUCCESS":
340
+ assert status.id == job_id
341
+ assert status.start_time is not None
342
+ assert status.end_time is not None
343
+ assert status.log is not None
344
+ assert status.detail is not None
345
+ break
346
+ if time.time() - start_time > 30:
347
+ raise TimeoutError(f"Waiting timeout for loading job {job_id}")
348
+ # test unbind data source
349
+ unbind_vertex_datasource(graph_id, "person")
350
+ unbind_edge_datasource(graph_id, "knows", "person", "person")
351
+ ds = get_datasource_by_id(graph_id).to_dict()
352
+ assert not ds["vertex_mappings"]
353
+ assert not ds["edge_mappings"]
354
+ delete_graph_by_id(graph_id)
355
+
356
+ def test_cypher_procedure(self):
357
+ stored_procedure_dict = {
358
+ "name": "procedure_name",
359
+ "description": "This is a test procedure",
360
+ "query": "MATCH (n) RETURN COUNT(n);",
361
+ "type": "cypher",
362
+ }
363
+ # test create a new procedure
364
+ graph_id = create_graph(modern_graph)
365
+ stored_procedure_id = create_stored_procedure(graph_id, stored_procedure_dict)
366
+ assert stored_procedure_id is not None
367
+ new_procedure_exist = False
368
+ procedures = list_stored_procedures(graph_id)
369
+ for p in procedures:
370
+ if p.id == stored_procedure_id and p.name == stored_procedure_dict["name"]:
371
+ new_procedure_exist = True
372
+ assert new_procedure_exist
373
+ # test update a procedure
374
+ description = "This is an updated description"
375
+ update_stored_procedure_by_id(
376
+ graph_id, stored_procedure_id, {"description": description}
377
+ )
378
+ procedure = get_stored_procedure_by_id(graph_id, stored_procedure_id)
379
+ assert procedure.description == description
380
+ # test delete a procedure
381
+ delete_stored_procedure_by_id(graph_id, stored_procedure_id)
382
+ new_procedure_exist = False
383
+ procedures = list_stored_procedures(graph_id)
384
+ for p in procedures:
385
+ if p.id == stored_procedure_id and p.name == "procedure_name":
386
+ new_procedure_exist = True
387
+ assert not new_procedure_exist
388
+ delete_graph_by_id(graph_id)
389
+
390
+ def test_cpp_procedure(self, tmpdir):
391
+ # generate sample_app.cc
392
+ cpp_procedure_file = tmpdir.join("sample_app.cc")
393
+ cpp_procedure_file.write(sample_cc)
394
+ # test create a new cpp stored procedure
395
+ stored_procedure_dict = {
396
+ "name": "cpp_stored_procedure_name",
397
+ "description": "This is a cpp test stored procedure",
398
+ "query": f"@{str(cpp_procedure_file)}",
399
+ "type": "cpp",
400
+ }
401
+ graph_id = create_graph(modern_graph)
402
+ stored_procedure_id = create_stored_procedure(graph_id, stored_procedure_dict)
403
+ assert stored_procedure_id is not None
404
+ new_procedure_exist = False
405
+ procedures = list_stored_procedures(graph_id)
406
+ for p in procedures:
407
+ if p.id == stored_procedure_id and p.name == stored_procedure_dict["name"]:
408
+ new_procedure_exist = True
409
+ assert new_procedure_exist
410
+ delete_graph_by_id(graph_id)
411
+
412
+ def test_service(self):
413
+ original_graph_id = None
414
+ status = list_service_status()
415
+ for s in status:
416
+ if s.status == "Running":
417
+ original_graph_id = s.graph_id
418
+ assert original_graph_id is not None
419
+ # start service on a new graph
420
+ new_graph_id = create_graph(modern_graph)
421
+ start_service(new_graph_id)
422
+ status = list_service_status()
423
+ for s in status:
424
+ if s.graph_id == new_graph_id:
425
+ assert s.status == "Running"
426
+ else:
427
+ assert s.status == "Stopped"
428
+ # restart the service
429
+ restart_service()
430
+ status = list_service_status()
431
+ for s in status:
432
+ if s.graph_id == new_graph_id:
433
+ assert s.status == "Running"
434
+ else:
435
+ assert s.status == "Stopped"
436
+ # stop the service
437
+ stop_service()
438
+ status = list_service_status()
439
+ for s in status:
440
+ assert s.status == "Stopped"
441
+ # delete graph and switch to original graph
442
+ delete_graph_by_id(new_graph_id)
443
+ start_service(original_graph_id)
444
+ status = list_service_status()
445
+ for s in status:
446
+ if s.graph_id == original_graph_id:
447
+ assert s.status == "Running"
448
+ else:
449
+ assert s.status == "Stopped"
450
+
451
+ def test_suit_case(self):
452
+ # case 1:
453
+ # during deleting a graph, make sure the stored procedures
454
+ # on that graph are deleted at the same time
455
+ stored_procedure_dict = {
456
+ "name": "procedure_name",
457
+ "description": "This is a test procedure",
458
+ "query": "MATCH (n) RETURN COUNT(n);",
459
+ "type": "cypher",
460
+ }
461
+ graph_id = create_graph(modern_graph)
462
+ graph_id_2 = create_graph(modern_graph)
463
+ # create a procedure on graph 1
464
+ stored_procedure_id = create_stored_procedure(graph_id, stored_procedure_dict)
465
+ assert stored_procedure_id == "procedure_name"
466
+ # delete the graph 1, then create a new procedure on graph 2
467
+ delete_graph_by_id(graph_id)
468
+ stored_procedure_id = create_stored_procedure(graph_id_2, stored_procedure_dict)
469
+ assert stored_procedure_id == "procedure_name"
470
+ delete_graph_by_id(graph_id_2)
471
+
472
+ def test_corner_case_on_starting_service(self):
473
+ original_graph_id = None
474
+ status = list_service_status()
475
+ for s in status:
476
+ if s.status == "Running":
477
+ original_graph_id = s.graph_id
478
+ assert original_graph_id is not None
479
+
480
+ # case 1:
481
+ # start service on vertex only graph
482
+ graph_id = create_graph(modern_graph_vertex_only)
483
+ start_service(graph_id)
484
+ status = list_service_status()
485
+ for s in status:
486
+ if s.graph_id == graph_id:
487
+ assert s.status == "Running"
488
+ else:
489
+ assert s.status == "Stopped"
490
+ stop_service()
491
+ delete_graph_by_id(graph_id)
492
+
493
+ # case 2:
494
+ # start service on graph with empty edge property
495
+ graph_id = create_graph(modern_graph_with_empty_edge_property)
496
+ start_service(original_graph_id)
497
+ status = list_service_status()
498
+ for s in status:
499
+ if s.graph_id == original_graph_id:
500
+ assert s.status == "Running"
501
+ else:
502
+ assert s.status == "Stopped"
503
+ stop_service()
504
+ delete_graph_by_id(graph_id)
505
+
506
+ # switch to original graph
507
+ start_service(original_graph_id)
508
+ status = list_service_status()
509
+ for s in status:
510
+ if s.graph_id == original_graph_id:
511
+ assert s.status == "Running"
512
+ else:
513
+ assert s.status == "Stopped"
514
+
515
+ def teardown_class(self):
516
+ disconnect_coordinator()