matrixone-python-sdk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- matrixone/__init__.py +155 -0
- matrixone/account.py +723 -0
- matrixone/async_client.py +3913 -0
- matrixone/async_metadata_manager.py +311 -0
- matrixone/async_orm.py +123 -0
- matrixone/async_vector_index_manager.py +633 -0
- matrixone/base_client.py +208 -0
- matrixone/client.py +4672 -0
- matrixone/config.py +452 -0
- matrixone/connection_hooks.py +286 -0
- matrixone/exceptions.py +89 -0
- matrixone/logger.py +782 -0
- matrixone/metadata.py +820 -0
- matrixone/moctl.py +219 -0
- matrixone/orm.py +2277 -0
- matrixone/pitr.py +646 -0
- matrixone/pubsub.py +771 -0
- matrixone/restore.py +411 -0
- matrixone/search_vector_index.py +1176 -0
- matrixone/snapshot.py +550 -0
- matrixone/sql_builder.py +844 -0
- matrixone/sqlalchemy_ext/__init__.py +161 -0
- matrixone/sqlalchemy_ext/adapters.py +163 -0
- matrixone/sqlalchemy_ext/dialect.py +534 -0
- matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
- matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
- matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
- matrixone/sqlalchemy_ext/ivf_config.py +252 -0
- matrixone/sqlalchemy_ext/table_builder.py +351 -0
- matrixone/sqlalchemy_ext/vector_index.py +1721 -0
- matrixone/sqlalchemy_ext/vector_type.py +948 -0
- matrixone/version.py +580 -0
- matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
- matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
- matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
- matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
- matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
- matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +19 -0
- tests/offline/__init__.py +20 -0
- tests/offline/conftest.py +77 -0
- tests/offline/test_account.py +703 -0
- tests/offline/test_async_client_query_comprehensive.py +1218 -0
- tests/offline/test_basic.py +54 -0
- tests/offline/test_case_sensitivity.py +227 -0
- tests/offline/test_connection_hooks_offline.py +287 -0
- tests/offline/test_dialect_schema_handling.py +609 -0
- tests/offline/test_explain_methods.py +346 -0
- tests/offline/test_filter_logical_in.py +237 -0
- tests/offline/test_fulltext_search_comprehensive.py +795 -0
- tests/offline/test_ivf_config.py +249 -0
- tests/offline/test_join_methods.py +281 -0
- tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
- tests/offline/test_logical_in_method.py +237 -0
- tests/offline/test_matrixone_version_parsing.py +264 -0
- tests/offline/test_metadata_offline.py +557 -0
- tests/offline/test_moctl.py +300 -0
- tests/offline/test_moctl_simple.py +251 -0
- tests/offline/test_model_support_offline.py +359 -0
- tests/offline/test_model_support_simple.py +225 -0
- tests/offline/test_pinecone_filter_offline.py +377 -0
- tests/offline/test_pitr.py +585 -0
- tests/offline/test_pubsub.py +712 -0
- tests/offline/test_query_update.py +283 -0
- tests/offline/test_restore.py +445 -0
- tests/offline/test_snapshot_comprehensive.py +384 -0
- tests/offline/test_sql_escaping_edge_cases.py +551 -0
- tests/offline/test_sqlalchemy_integration.py +382 -0
- tests/offline/test_sqlalchemy_vector_integration.py +434 -0
- tests/offline/test_table_builder.py +198 -0
- tests/offline/test_unified_filter.py +398 -0
- tests/offline/test_unified_transaction.py +495 -0
- tests/offline/test_vector_index.py +238 -0
- tests/offline/test_vector_operations.py +688 -0
- tests/offline/test_vector_type.py +174 -0
- tests/offline/test_version_core.py +328 -0
- tests/offline/test_version_management.py +372 -0
- tests/offline/test_version_standalone.py +652 -0
- tests/online/__init__.py +20 -0
- tests/online/conftest.py +216 -0
- tests/online/test_account_management.py +194 -0
- tests/online/test_advanced_features.py +344 -0
- tests/online/test_async_client_interfaces.py +330 -0
- tests/online/test_async_client_online.py +285 -0
- tests/online/test_async_model_insert_online.py +293 -0
- tests/online/test_async_orm_online.py +300 -0
- tests/online/test_async_simple_query_online.py +802 -0
- tests/online/test_async_transaction_simple_query.py +300 -0
- tests/online/test_basic_connection.py +130 -0
- tests/online/test_client_online.py +238 -0
- tests/online/test_config.py +90 -0
- tests/online/test_config_validation.py +123 -0
- tests/online/test_connection_hooks_new_online.py +217 -0
- tests/online/test_dialect_schema_handling_online.py +331 -0
- tests/online/test_filter_logical_in_online.py +374 -0
- tests/online/test_fulltext_comprehensive.py +1773 -0
- tests/online/test_fulltext_label_online.py +433 -0
- tests/online/test_fulltext_search_online.py +842 -0
- tests/online/test_ivf_stats_online.py +506 -0
- tests/online/test_logger_integration.py +311 -0
- tests/online/test_matrixone_query_orm.py +540 -0
- tests/online/test_metadata_online.py +579 -0
- tests/online/test_model_insert_online.py +255 -0
- tests/online/test_mysql_driver_validation.py +213 -0
- tests/online/test_orm_advanced_features.py +2022 -0
- tests/online/test_orm_cte_integration.py +269 -0
- tests/online/test_orm_online.py +270 -0
- tests/online/test_pinecone_filter.py +708 -0
- tests/online/test_pubsub_operations.py +352 -0
- tests/online/test_query_methods.py +225 -0
- tests/online/test_query_update_online.py +433 -0
- tests/online/test_search_vector_index.py +557 -0
- tests/online/test_simple_fulltext_online.py +915 -0
- tests/online/test_snapshot_comprehensive.py +998 -0
- tests/online/test_sqlalchemy_engine_integration.py +336 -0
- tests/online/test_sqlalchemy_integration.py +425 -0
- tests/online/test_transaction_contexts.py +1219 -0
- tests/online/test_transaction_insert_methods.py +356 -0
- tests/online/test_transaction_query_methods.py +288 -0
- tests/online/test_unified_filter_online.py +529 -0
- tests/online/test_vector_comprehensive.py +706 -0
- tests/online/test_version_management.py +291 -0
@@ -0,0 +1,300 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""
|
16
|
+
Test MoCtlManager functionality
|
17
|
+
"""
|
18
|
+
|
19
|
+
import unittest
|
20
|
+
from unittest.mock import Mock, patch
|
21
|
+
import json
|
22
|
+
import sys
|
23
|
+
import os
|
24
|
+
|
25
|
+
# Store original modules to restore later
|
26
|
+
_original_modules = {}
|
27
|
+
|
28
|
+
|
29
|
+
def setup_sqlalchemy_mocks():
|
30
|
+
"""Setup SQLAlchemy mocks for this test class"""
|
31
|
+
global _original_modules
|
32
|
+
_original_modules['pymysql'] = sys.modules.get('pymysql')
|
33
|
+
_original_modules['sqlalchemy'] = sys.modules.get('sqlalchemy')
|
34
|
+
_original_modules['sqlalchemy.engine'] = sys.modules.get('sqlalchemy.engine')
|
35
|
+
_original_modules['sqlalchemy.orm'] = sys.modules.get('sqlalchemy.orm')
|
36
|
+
|
37
|
+
sys.modules['pymysql'] = Mock()
|
38
|
+
sys.modules['sqlalchemy'] = Mock()
|
39
|
+
sys.modules['sqlalchemy.engine'] = Mock()
|
40
|
+
sys.modules['sqlalchemy.engine'].Engine = Mock()
|
41
|
+
sys.modules['sqlalchemy.orm'] = Mock()
|
42
|
+
sys.modules['sqlalchemy.orm'].sessionmaker = Mock()
|
43
|
+
sys.modules['sqlalchemy.orm'].declarative_base = Mock()
|
44
|
+
sys.modules['sqlalchemy'].create_engine = Mock()
|
45
|
+
sys.modules['sqlalchemy'].text = Mock()
|
46
|
+
sys.modules['sqlalchemy'].Column = Mock()
|
47
|
+
sys.modules['sqlalchemy'].Integer = Mock()
|
48
|
+
sys.modules['sqlalchemy'].String = Mock()
|
49
|
+
sys.modules['sqlalchemy'].DateTime = Mock()
|
50
|
+
|
51
|
+
|
52
|
+
def teardown_sqlalchemy_mocks():
|
53
|
+
"""Restore original modules"""
|
54
|
+
global _original_modules
|
55
|
+
for module_name, original_module in _original_modules.items():
|
56
|
+
if original_module is not None:
|
57
|
+
sys.modules[module_name] = original_module
|
58
|
+
elif module_name in sys.modules:
|
59
|
+
del sys.modules[module_name]
|
60
|
+
|
61
|
+
|
62
|
+
# Add the matrixone package to the path
|
63
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'matrixone'))
|
64
|
+
|
65
|
+
from matrixone.moctl import MoCtlManager, MoCtlError
|
66
|
+
from matrixone.client import Client
|
67
|
+
|
68
|
+
|
69
|
+
class TestMoCtlManager(unittest.TestCase):
|
70
|
+
"""Test MoCtlManager functionality"""
|
71
|
+
|
72
|
+
@classmethod
|
73
|
+
def setUpClass(cls):
|
74
|
+
"""Setup mocks for the entire test class"""
|
75
|
+
setup_sqlalchemy_mocks()
|
76
|
+
|
77
|
+
@classmethod
|
78
|
+
def tearDownClass(cls):
|
79
|
+
"""Restore original modules after tests"""
|
80
|
+
teardown_sqlalchemy_mocks()
|
81
|
+
|
82
|
+
def setUp(self):
|
83
|
+
"""Set up test fixtures"""
|
84
|
+
self.mock_client = Mock()
|
85
|
+
self.mock_client.execute = Mock()
|
86
|
+
self.moctl_manager = MoCtlManager(self.mock_client)
|
87
|
+
|
88
|
+
def test_init(self):
|
89
|
+
"""Test MoCtlManager initialization"""
|
90
|
+
self.assertEqual(self.moctl_manager.client, self.mock_client)
|
91
|
+
|
92
|
+
def test_execute_moctl_success(self):
|
93
|
+
"""Test successful mo_ctl execution"""
|
94
|
+
# Mock successful result
|
95
|
+
mock_result = Mock()
|
96
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "OK"}]}',)]
|
97
|
+
self.mock_client.execute.return_value = mock_result
|
98
|
+
|
99
|
+
result = self.moctl_manager._execute_moctl('dn', 'flush', 'db1.t')
|
100
|
+
|
101
|
+
# Verify the SQL was called correctly
|
102
|
+
expected_sql = "SELECT mo_ctl('dn', 'flush', 'db1.t')"
|
103
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
104
|
+
|
105
|
+
# Verify the result was parsed correctly
|
106
|
+
self.assertEqual(result['method'], 'Flush')
|
107
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
108
|
+
|
109
|
+
def test_execute_moctl_no_params(self):
|
110
|
+
"""Test mo_ctl execution without parameters"""
|
111
|
+
# Mock successful result
|
112
|
+
mock_result = Mock()
|
113
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "OK"}]}',)]
|
114
|
+
self.mock_client.execute.return_value = mock_result
|
115
|
+
|
116
|
+
result = self.moctl_manager._execute_moctl('dn', 'checkpoint', '')
|
117
|
+
|
118
|
+
# Verify the SQL was called correctly
|
119
|
+
expected_sql = "SELECT mo_ctl('dn', 'checkpoint', '')"
|
120
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
121
|
+
|
122
|
+
# Verify the result was parsed correctly
|
123
|
+
self.assertEqual(result['method'], 'Checkpoint')
|
124
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
125
|
+
|
126
|
+
def test_execute_moctl_failure(self):
|
127
|
+
"""Test mo_ctl execution failure"""
|
128
|
+
# Mock failure result
|
129
|
+
mock_result = Mock()
|
130
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "ERROR: Table not found"}]}',)]
|
131
|
+
self.mock_client.execute.return_value = mock_result
|
132
|
+
|
133
|
+
# Should raise MoCtlError
|
134
|
+
with self.assertRaises(MoCtlError) as context:
|
135
|
+
self.moctl_manager._execute_moctl('dn', 'flush', 'db1.nonexistent')
|
136
|
+
|
137
|
+
self.assertIn("mo_ctl operation failed", str(context.exception))
|
138
|
+
|
139
|
+
def test_execute_moctl_no_rows(self):
|
140
|
+
"""Test mo_ctl execution with no rows returned"""
|
141
|
+
# Mock empty result
|
142
|
+
mock_result = Mock()
|
143
|
+
mock_result.rows = []
|
144
|
+
self.mock_client.execute.return_value = mock_result
|
145
|
+
|
146
|
+
# Should raise MoCtlError
|
147
|
+
with self.assertRaises(MoCtlError) as context:
|
148
|
+
self.moctl_manager._execute_moctl('dn', 'flush', 'db1.t')
|
149
|
+
|
150
|
+
self.assertIn("mo_ctl command returned no results", str(context.exception))
|
151
|
+
|
152
|
+
def test_execute_moctl_json_error(self):
|
153
|
+
"""Test mo_ctl execution with invalid JSON"""
|
154
|
+
# Mock invalid JSON result
|
155
|
+
mock_result = Mock()
|
156
|
+
mock_result.rows = [('invalid json',)]
|
157
|
+
self.mock_client.execute.return_value = mock_result
|
158
|
+
|
159
|
+
# Should raise MoCtlError
|
160
|
+
with self.assertRaises(MoCtlError) as context:
|
161
|
+
self.moctl_manager._execute_moctl('dn', 'flush', 'db1.t')
|
162
|
+
|
163
|
+
self.assertIn("Failed to parse mo_ctl result", str(context.exception))
|
164
|
+
|
165
|
+
def test_execute_moctl_exception(self):
|
166
|
+
"""Test mo_ctl execution with exception"""
|
167
|
+
# Mock exception
|
168
|
+
self.mock_client.execute.side_effect = Exception("Connection failed")
|
169
|
+
|
170
|
+
# Should raise MoCtlError
|
171
|
+
with self.assertRaises(MoCtlError) as context:
|
172
|
+
self.moctl_manager._execute_moctl('dn', 'flush', 'db1.t')
|
173
|
+
|
174
|
+
self.assertIn("mo_ctl operation failed", str(context.exception))
|
175
|
+
|
176
|
+
def test_flush_table(self):
|
177
|
+
"""Test flush_table method"""
|
178
|
+
# Mock successful result
|
179
|
+
mock_result = Mock()
|
180
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "OK"}]}',)]
|
181
|
+
self.mock_client.execute.return_value = mock_result
|
182
|
+
|
183
|
+
result = self.moctl_manager.flush_table('db1', 'users')
|
184
|
+
|
185
|
+
# Verify the SQL was called correctly
|
186
|
+
expected_sql = "SELECT mo_ctl('dn', 'flush', 'db1.users')"
|
187
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
188
|
+
|
189
|
+
# Verify the result
|
190
|
+
self.assertEqual(result['method'], 'Flush')
|
191
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
192
|
+
|
193
|
+
def test_increment_checkpoint(self):
|
194
|
+
"""Test increment_checkpoint method"""
|
195
|
+
# Mock successful result
|
196
|
+
mock_result = Mock()
|
197
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "OK"}]}',)]
|
198
|
+
self.mock_client.execute.return_value = mock_result
|
199
|
+
|
200
|
+
result = self.moctl_manager.increment_checkpoint()
|
201
|
+
|
202
|
+
# Verify the SQL was called correctly
|
203
|
+
expected_sql = "SELECT mo_ctl('dn', 'checkpoint', '')"
|
204
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
205
|
+
|
206
|
+
# Verify the result
|
207
|
+
self.assertEqual(result['method'], 'Checkpoint')
|
208
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
209
|
+
|
210
|
+
def test_global_checkpoint(self):
|
211
|
+
"""Test global_checkpoint method"""
|
212
|
+
# Mock successful result
|
213
|
+
mock_result = Mock()
|
214
|
+
mock_result.rows = [('{"method": "GlobalCheckpoint", "result": [{"returnStr": "OK"}]}',)]
|
215
|
+
self.mock_client.execute.return_value = mock_result
|
216
|
+
|
217
|
+
result = self.moctl_manager.global_checkpoint()
|
218
|
+
|
219
|
+
# Verify the SQL was called correctly
|
220
|
+
expected_sql = "SELECT mo_ctl('dn', 'globalcheckpoint', '')"
|
221
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
222
|
+
|
223
|
+
# Verify the result
|
224
|
+
self.assertEqual(result['method'], 'GlobalCheckpoint')
|
225
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
226
|
+
|
227
|
+
|
228
|
+
class TestMoCtlIntegration(unittest.TestCase):
|
229
|
+
"""Test MoCtlManager integration with Client"""
|
230
|
+
|
231
|
+
@classmethod
|
232
|
+
def setUpClass(cls):
|
233
|
+
"""Setup mocks for the entire test class"""
|
234
|
+
setup_sqlalchemy_mocks()
|
235
|
+
|
236
|
+
@classmethod
|
237
|
+
def tearDownClass(cls):
|
238
|
+
"""Restore original modules after tests"""
|
239
|
+
teardown_sqlalchemy_mocks()
|
240
|
+
|
241
|
+
def setUp(self):
|
242
|
+
"""Set up test fixtures"""
|
243
|
+
self.mock_client = Mock()
|
244
|
+
self.mock_client.execute = Mock()
|
245
|
+
self.moctl_manager = MoCtlManager(self.mock_client)
|
246
|
+
|
247
|
+
def test_flush_table_integration(self):
|
248
|
+
"""Test flush_table integration"""
|
249
|
+
# Mock successful result
|
250
|
+
mock_result = Mock()
|
251
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "OK"}]}',)]
|
252
|
+
self.mock_client.execute.return_value = mock_result
|
253
|
+
|
254
|
+
result = self.moctl_manager.flush_table('production', 'orders')
|
255
|
+
|
256
|
+
# Verify the SQL was called correctly
|
257
|
+
expected_sql = "SELECT mo_ctl('dn', 'flush', 'production.orders')"
|
258
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
259
|
+
|
260
|
+
# Verify the result
|
261
|
+
self.assertEqual(result['method'], 'Flush')
|
262
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
263
|
+
|
264
|
+
def test_increment_checkpoint_integration(self):
|
265
|
+
"""Test increment_checkpoint integration"""
|
266
|
+
# Mock successful result
|
267
|
+
mock_result = Mock()
|
268
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "OK"}]}',)]
|
269
|
+
self.mock_client.execute.return_value = mock_result
|
270
|
+
|
271
|
+
result = self.moctl_manager.increment_checkpoint()
|
272
|
+
|
273
|
+
# Verify the SQL was called correctly
|
274
|
+
expected_sql = "SELECT mo_ctl('dn', 'checkpoint', '')"
|
275
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
276
|
+
|
277
|
+
# Verify the result
|
278
|
+
self.assertEqual(result['method'], 'Checkpoint')
|
279
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
280
|
+
|
281
|
+
def test_global_checkpoint_integration(self):
|
282
|
+
"""Test global_checkpoint integration"""
|
283
|
+
# Mock successful result
|
284
|
+
mock_result = Mock()
|
285
|
+
mock_result.rows = [('{"method": "GlobalCheckpoint", "result": [{"returnStr": "OK"}]}',)]
|
286
|
+
self.mock_client.execute.return_value = mock_result
|
287
|
+
|
288
|
+
result = self.moctl_manager.global_checkpoint()
|
289
|
+
|
290
|
+
# Verify the SQL was called correctly
|
291
|
+
expected_sql = "SELECT mo_ctl('dn', 'globalcheckpoint', '')"
|
292
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
293
|
+
|
294
|
+
# Verify the result
|
295
|
+
self.assertEqual(result['method'], 'GlobalCheckpoint')
|
296
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
297
|
+
|
298
|
+
|
299
|
+
if __name__ == '__main__':
|
300
|
+
unittest.main()
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# Copyright 2021 - 2022 Matrix Origin
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
"""
|
16
|
+
Test MoCtlManager core functionality (flush_table, increment_checkpoint, global_checkpoint only)
|
17
|
+
"""
|
18
|
+
|
19
|
+
import unittest
|
20
|
+
from unittest.mock import Mock, patch
|
21
|
+
import json
|
22
|
+
import sys
|
23
|
+
import os
|
24
|
+
|
25
|
+
# Store original modules to restore later
|
26
|
+
_original_modules = {}
|
27
|
+
|
28
|
+
|
29
|
+
def setup_sqlalchemy_mocks():
|
30
|
+
"""Setup SQLAlchemy mocks for this test class"""
|
31
|
+
global _original_modules
|
32
|
+
_original_modules['pymysql'] = sys.modules.get('pymysql')
|
33
|
+
_original_modules['sqlalchemy'] = sys.modules.get('sqlalchemy')
|
34
|
+
_original_modules['sqlalchemy.engine'] = sys.modules.get('sqlalchemy.engine')
|
35
|
+
_original_modules['sqlalchemy.orm'] = sys.modules.get('sqlalchemy.orm')
|
36
|
+
|
37
|
+
sys.modules['pymysql'] = Mock()
|
38
|
+
sys.modules['sqlalchemy'] = Mock()
|
39
|
+
sys.modules['sqlalchemy.engine'] = Mock()
|
40
|
+
sys.modules['sqlalchemy.engine'].Engine = Mock()
|
41
|
+
sys.modules['sqlalchemy.orm'] = Mock()
|
42
|
+
sys.modules['sqlalchemy.orm'].sessionmaker = Mock()
|
43
|
+
sys.modules['sqlalchemy.orm'].declarative_base = Mock()
|
44
|
+
sys.modules['sqlalchemy'].create_engine = Mock()
|
45
|
+
sys.modules['sqlalchemy'].text = Mock()
|
46
|
+
sys.modules['sqlalchemy'].Column = Mock()
|
47
|
+
sys.modules['sqlalchemy'].Integer = Mock()
|
48
|
+
sys.modules['sqlalchemy'].String = Mock()
|
49
|
+
sys.modules['sqlalchemy'].DateTime = Mock()
|
50
|
+
|
51
|
+
|
52
|
+
def teardown_sqlalchemy_mocks():
|
53
|
+
"""Restore original modules"""
|
54
|
+
global _original_modules
|
55
|
+
for module_name, original_module in _original_modules.items():
|
56
|
+
if original_module is not None:
|
57
|
+
sys.modules[module_name] = original_module
|
58
|
+
elif module_name in sys.modules:
|
59
|
+
del sys.modules[module_name]
|
60
|
+
|
61
|
+
|
62
|
+
# Add the matrixone package to the path
|
63
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'matrixone'))
|
64
|
+
|
65
|
+
from matrixone.moctl import MoCtlManager, MoCtlError
|
66
|
+
from matrixone.client import Client
|
67
|
+
|
68
|
+
|
69
|
+
class TestMoCtlManager(unittest.TestCase):
|
70
|
+
"""Test MoCtlManager core functionality"""
|
71
|
+
|
72
|
+
@classmethod
|
73
|
+
def setUpClass(cls):
|
74
|
+
"""Setup mocks for the entire test class"""
|
75
|
+
setup_sqlalchemy_mocks()
|
76
|
+
|
77
|
+
@classmethod
|
78
|
+
def tearDownClass(cls):
|
79
|
+
"""Restore original modules after tests"""
|
80
|
+
teardown_sqlalchemy_mocks()
|
81
|
+
|
82
|
+
def setUp(self):
|
83
|
+
"""Set up test fixtures"""
|
84
|
+
self.mock_client = Mock()
|
85
|
+
self.mock_client.execute = Mock()
|
86
|
+
self.moctl_manager = MoCtlManager(self.mock_client)
|
87
|
+
|
88
|
+
def test_init(self):
|
89
|
+
"""Test MoCtlManager initialization"""
|
90
|
+
self.assertEqual(self.moctl_manager.client, self.mock_client)
|
91
|
+
|
92
|
+
def test_flush_table(self):
|
93
|
+
"""Test flush_table method"""
|
94
|
+
# Mock successful result
|
95
|
+
mock_result = Mock()
|
96
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "OK"}]}',)]
|
97
|
+
self.mock_client.execute.return_value = mock_result
|
98
|
+
|
99
|
+
result = self.moctl_manager.flush_table('db1', 'users')
|
100
|
+
|
101
|
+
# Verify the SQL was called correctly
|
102
|
+
expected_sql = "SELECT mo_ctl('dn', 'flush', 'db1.users')"
|
103
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
104
|
+
|
105
|
+
# Verify the result
|
106
|
+
self.assertEqual(result['method'], 'Flush')
|
107
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
108
|
+
|
109
|
+
def test_increment_checkpoint(self):
|
110
|
+
"""Test increment_checkpoint method"""
|
111
|
+
# Mock successful result
|
112
|
+
mock_result = Mock()
|
113
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "OK"}]}',)]
|
114
|
+
self.mock_client.execute.return_value = mock_result
|
115
|
+
|
116
|
+
result = self.moctl_manager.increment_checkpoint()
|
117
|
+
|
118
|
+
# Verify the SQL was called correctly
|
119
|
+
expected_sql = "SELECT mo_ctl('dn', 'checkpoint', '')"
|
120
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
121
|
+
|
122
|
+
# Verify the result
|
123
|
+
self.assertEqual(result['method'], 'Checkpoint')
|
124
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
125
|
+
|
126
|
+
def test_global_checkpoint(self):
|
127
|
+
"""Test global_checkpoint method"""
|
128
|
+
# Mock successful result
|
129
|
+
mock_result = Mock()
|
130
|
+
mock_result.rows = [('{"method": "GlobalCheckpoint", "result": [{"returnStr": "OK"}]}',)]
|
131
|
+
self.mock_client.execute.return_value = mock_result
|
132
|
+
|
133
|
+
result = self.moctl_manager.global_checkpoint()
|
134
|
+
|
135
|
+
# Verify the SQL was called correctly
|
136
|
+
expected_sql = "SELECT mo_ctl('dn', 'globalcheckpoint', '')"
|
137
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
138
|
+
|
139
|
+
# Verify the result
|
140
|
+
self.assertEqual(result['method'], 'GlobalCheckpoint')
|
141
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
142
|
+
|
143
|
+
def test_flush_table_failure(self):
|
144
|
+
"""Test flush_table method with failure"""
|
145
|
+
# Mock failure result
|
146
|
+
mock_result = Mock()
|
147
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "ERROR: Table not found"}]}',)]
|
148
|
+
self.mock_client.execute.return_value = mock_result
|
149
|
+
|
150
|
+
# Should raise MoCtlError
|
151
|
+
with self.assertRaises(MoCtlError):
|
152
|
+
self.moctl_manager.flush_table('db1', 'nonexistent')
|
153
|
+
|
154
|
+
def test_increment_checkpoint_failure(self):
|
155
|
+
"""Test increment_checkpoint method with failure"""
|
156
|
+
# Mock failure result
|
157
|
+
mock_result = Mock()
|
158
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "ERROR: Checkpoint failed"}]}',)]
|
159
|
+
self.mock_client.execute.return_value = mock_result
|
160
|
+
|
161
|
+
# Should raise MoCtlError
|
162
|
+
with self.assertRaises(MoCtlError):
|
163
|
+
self.moctl_manager.increment_checkpoint()
|
164
|
+
|
165
|
+
def test_global_checkpoint_failure(self):
|
166
|
+
"""Test global_checkpoint method with failure"""
|
167
|
+
# Mock failure result
|
168
|
+
mock_result = Mock()
|
169
|
+
mock_result.rows = [
|
170
|
+
('{"method": "GlobalCheckpoint", "result": [{"returnStr": "ERROR: Global checkpoint failed"}]}',)
|
171
|
+
]
|
172
|
+
self.mock_client.execute.return_value = mock_result
|
173
|
+
|
174
|
+
# Should raise MoCtlError
|
175
|
+
with self.assertRaises(MoCtlError):
|
176
|
+
self.moctl_manager.global_checkpoint()
|
177
|
+
|
178
|
+
|
179
|
+
class TestMoCtlIntegration(unittest.TestCase):
|
180
|
+
"""Test MoCtlManager integration with Client"""
|
181
|
+
|
182
|
+
@classmethod
|
183
|
+
def setUpClass(cls):
|
184
|
+
"""Setup mocks for the entire test class"""
|
185
|
+
setup_sqlalchemy_mocks()
|
186
|
+
|
187
|
+
@classmethod
|
188
|
+
def tearDownClass(cls):
|
189
|
+
"""Restore original modules after tests"""
|
190
|
+
teardown_sqlalchemy_mocks()
|
191
|
+
|
192
|
+
def setUp(self):
|
193
|
+
"""Set up test fixtures"""
|
194
|
+
self.mock_client = Mock()
|
195
|
+
self.mock_client.execute = Mock()
|
196
|
+
self.moctl_manager = MoCtlManager(self.mock_client)
|
197
|
+
|
198
|
+
def test_flush_table_integration(self):
|
199
|
+
"""Test flush_table integration"""
|
200
|
+
# Mock successful result
|
201
|
+
mock_result = Mock()
|
202
|
+
mock_result.rows = [('{"method": "Flush", "result": [{"returnStr": "OK"}]}',)]
|
203
|
+
self.mock_client.execute.return_value = mock_result
|
204
|
+
|
205
|
+
result = self.moctl_manager.flush_table('production', 'orders')
|
206
|
+
|
207
|
+
# Verify the SQL was called correctly
|
208
|
+
expected_sql = "SELECT mo_ctl('dn', 'flush', 'production.orders')"
|
209
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
210
|
+
|
211
|
+
# Verify the result
|
212
|
+
self.assertEqual(result['method'], 'Flush')
|
213
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
214
|
+
|
215
|
+
def test_increment_checkpoint_integration(self):
|
216
|
+
"""Test increment_checkpoint integration"""
|
217
|
+
# Mock successful result
|
218
|
+
mock_result = Mock()
|
219
|
+
mock_result.rows = [('{"method": "Checkpoint", "result": [{"returnStr": "OK"}]}',)]
|
220
|
+
self.mock_client.execute.return_value = mock_result
|
221
|
+
|
222
|
+
result = self.moctl_manager.increment_checkpoint()
|
223
|
+
|
224
|
+
# Verify the SQL was called correctly
|
225
|
+
expected_sql = "SELECT mo_ctl('dn', 'checkpoint', '')"
|
226
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
227
|
+
|
228
|
+
# Verify the result
|
229
|
+
self.assertEqual(result['method'], 'Checkpoint')
|
230
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
231
|
+
|
232
|
+
def test_global_checkpoint_integration(self):
|
233
|
+
"""Test global_checkpoint integration"""
|
234
|
+
# Mock successful result
|
235
|
+
mock_result = Mock()
|
236
|
+
mock_result.rows = [('{"method": "GlobalCheckpoint", "result": [{"returnStr": "OK"}]}',)]
|
237
|
+
self.mock_client.execute.return_value = mock_result
|
238
|
+
|
239
|
+
result = self.moctl_manager.global_checkpoint()
|
240
|
+
|
241
|
+
# Verify the SQL was called correctly
|
242
|
+
expected_sql = "SELECT mo_ctl('dn', 'globalcheckpoint', '')"
|
243
|
+
self.mock_client.execute.assert_called_once_with(expected_sql)
|
244
|
+
|
245
|
+
# Verify the result
|
246
|
+
self.assertEqual(result['method'], 'GlobalCheckpoint')
|
247
|
+
self.assertEqual(result['result'][0]['returnStr'], 'OK')
|
248
|
+
|
249
|
+
|
250
|
+
if __name__ == '__main__':
|
251
|
+
unittest.main()
|