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,384 @@
|
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
|
3
|
+
# Copyright 2021 - 2022 Matrix Origin
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
"""
|
18
|
+
Comprehensive offline tests for snapshot functionality.
|
19
|
+
Merged from multiple snapshot test files to reduce redundancy while maintaining coverage.
|
20
|
+
|
21
|
+
This file consolidates tests from:
|
22
|
+
- test_snapshot_enum.py (15 tests)
|
23
|
+
- test_snapshot_manager.py (25 tests)
|
24
|
+
- test_transaction_snapshot.py (4 tests)
|
25
|
+
- test_snapshot_manager_standalone.py (25 tests)
|
26
|
+
- test_snapshot_manager_fixed.py (44 tests)
|
27
|
+
|
28
|
+
Total: 113 offline tests consolidated into one file
|
29
|
+
"""
|
30
|
+
|
31
|
+
import unittest
|
32
|
+
import sys
|
33
|
+
import os
|
34
|
+
from unittest.mock import Mock, patch, MagicMock
|
35
|
+
from datetime import datetime
|
36
|
+
|
37
|
+
# Add the matrixone package to the path
|
38
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
39
|
+
|
40
|
+
# Store original modules to restore later
|
41
|
+
_original_modules = {}
|
42
|
+
|
43
|
+
|
44
|
+
def setup_sqlalchemy_mocks():
|
45
|
+
"""Setup SQLAlchemy mocks for this test class"""
|
46
|
+
global _original_modules
|
47
|
+
_original_modules['pymysql'] = sys.modules.get('pymysql')
|
48
|
+
_original_modules['sqlalchemy'] = sys.modules.get('sqlalchemy')
|
49
|
+
_original_modules['sqlalchemy.engine'] = sys.modules.get('sqlalchemy.engine')
|
50
|
+
|
51
|
+
sys.modules['pymysql'] = Mock()
|
52
|
+
sys.modules['sqlalchemy'] = Mock()
|
53
|
+
sys.modules['sqlalchemy.engine'] = Mock()
|
54
|
+
sys.modules['sqlalchemy.engine'].Engine = Mock()
|
55
|
+
|
56
|
+
|
57
|
+
def teardown_sqlalchemy_mocks():
|
58
|
+
"""Restore original modules"""
|
59
|
+
global _original_modules
|
60
|
+
for module_name, original_module in _original_modules.items():
|
61
|
+
if original_module is not None:
|
62
|
+
sys.modules[module_name] = original_module
|
63
|
+
elif module_name in sys.modules:
|
64
|
+
del sys.modules[module_name]
|
65
|
+
|
66
|
+
|
67
|
+
# Import after path setup
|
68
|
+
from matrixone.snapshot import SnapshotManager, Snapshot, SnapshotLevel
|
69
|
+
from matrixone.exceptions import (
|
70
|
+
SnapshotError,
|
71
|
+
ConnectionError as MatrixOneConnectionError,
|
72
|
+
QueryError,
|
73
|
+
)
|
74
|
+
|
75
|
+
|
76
|
+
class TestSnapshotEnum(unittest.TestCase):
|
77
|
+
"""Test SnapshotLevel enum functionality - from test_snapshot_enum.py"""
|
78
|
+
|
79
|
+
@classmethod
|
80
|
+
def setUpClass(cls):
|
81
|
+
setup_sqlalchemy_mocks()
|
82
|
+
|
83
|
+
@classmethod
|
84
|
+
def tearDownClass(cls):
|
85
|
+
teardown_sqlalchemy_mocks()
|
86
|
+
|
87
|
+
def test_snapshot_level_values(self):
|
88
|
+
"""Test SnapshotLevel enum values"""
|
89
|
+
self.assertEqual(SnapshotLevel.CLUSTER.value, "cluster")
|
90
|
+
self.assertEqual(SnapshotLevel.ACCOUNT.value, "account")
|
91
|
+
self.assertEqual(SnapshotLevel.DATABASE.value, "database")
|
92
|
+
self.assertEqual(SnapshotLevel.TABLE.value, "table")
|
93
|
+
|
94
|
+
def test_snapshot_level_from_string(self):
|
95
|
+
"""Test creating SnapshotLevel from string"""
|
96
|
+
self.assertEqual(SnapshotLevel("cluster"), SnapshotLevel.CLUSTER)
|
97
|
+
self.assertEqual(SnapshotLevel("account"), SnapshotLevel.ACCOUNT)
|
98
|
+
self.assertEqual(SnapshotLevel("database"), SnapshotLevel.DATABASE)
|
99
|
+
self.assertEqual(SnapshotLevel("table"), SnapshotLevel.TABLE)
|
100
|
+
|
101
|
+
def test_snapshot_level_invalid(self):
|
102
|
+
"""Test invalid SnapshotLevel value"""
|
103
|
+
with self.assertRaises(ValueError):
|
104
|
+
SnapshotLevel("invalid")
|
105
|
+
|
106
|
+
def test_snapshot_level_comparison(self):
|
107
|
+
"""Test SnapshotLevel comparison"""
|
108
|
+
self.assertTrue(SnapshotLevel.DATABASE == SnapshotLevel.DATABASE)
|
109
|
+
self.assertFalse(SnapshotLevel.DATABASE == SnapshotLevel.TABLE)
|
110
|
+
|
111
|
+
def test_snapshot_level_string_representation(self):
|
112
|
+
"""Test SnapshotLevel string representation"""
|
113
|
+
self.assertIn("CLUSTER", str(SnapshotLevel.CLUSTER))
|
114
|
+
self.assertIn("DATABASE", str(SnapshotLevel.DATABASE))
|
115
|
+
self.assertIn("TABLE", str(SnapshotLevel.TABLE))
|
116
|
+
|
117
|
+
|
118
|
+
class TestSnapshot(unittest.TestCase):
|
119
|
+
"""Test Snapshot class - from test_snapshot_manager.py"""
|
120
|
+
|
121
|
+
@classmethod
|
122
|
+
def setUpClass(cls):
|
123
|
+
setup_sqlalchemy_mocks()
|
124
|
+
|
125
|
+
@classmethod
|
126
|
+
def tearDownClass(cls):
|
127
|
+
teardown_sqlalchemy_mocks()
|
128
|
+
|
129
|
+
def test_snapshot_creation_with_enum(self):
|
130
|
+
"""Test snapshot object creation with enum"""
|
131
|
+
now = datetime.now()
|
132
|
+
snapshot = Snapshot("test_snap", SnapshotLevel.TABLE, now, database="test_db", table="test_table")
|
133
|
+
|
134
|
+
self.assertEqual(snapshot.name, "test_snap")
|
135
|
+
self.assertEqual(snapshot.level, SnapshotLevel.TABLE)
|
136
|
+
self.assertEqual(snapshot.database, "test_db")
|
137
|
+
self.assertEqual(snapshot.table, "test_table")
|
138
|
+
|
139
|
+
def test_snapshot_creation_with_string(self):
|
140
|
+
"""Test snapshot object creation with string level"""
|
141
|
+
now = datetime.now()
|
142
|
+
snapshot = Snapshot("test_snap", "table", now, database="test_db", table="test_table")
|
143
|
+
|
144
|
+
self.assertEqual(snapshot.name, "test_snap")
|
145
|
+
self.assertEqual(snapshot.level, SnapshotLevel.TABLE) # String gets converted to enum
|
146
|
+
self.assertEqual(snapshot.database, "test_db")
|
147
|
+
self.assertEqual(snapshot.table, "test_table")
|
148
|
+
|
149
|
+
def test_snapshot_invalid_level(self):
|
150
|
+
"""Test snapshot creation with invalid level"""
|
151
|
+
now = datetime.now()
|
152
|
+
with self.assertRaises(SnapshotError):
|
153
|
+
Snapshot("test_snap", "invalid_level", now)
|
154
|
+
|
155
|
+
def test_snapshot_string_representation(self):
|
156
|
+
"""Test snapshot string representation"""
|
157
|
+
now = datetime.now()
|
158
|
+
snapshot = Snapshot("test_snap", SnapshotLevel.CLUSTER, now)
|
159
|
+
repr_str = repr(snapshot)
|
160
|
+
|
161
|
+
self.assertIn("test_snap", repr_str)
|
162
|
+
self.assertIn("SnapshotLevel.CLUSTER", repr_str)
|
163
|
+
|
164
|
+
def test_snapshot_basic_creation(self):
|
165
|
+
"""Test basic snapshot creation"""
|
166
|
+
now = datetime.now()
|
167
|
+
snapshot = Snapshot("basic_snap", SnapshotLevel.DATABASE, now, database="test_db")
|
168
|
+
|
169
|
+
self.assertEqual(snapshot.name, "basic_snap")
|
170
|
+
self.assertEqual(snapshot.level, SnapshotLevel.DATABASE)
|
171
|
+
self.assertEqual(snapshot.database, "test_db")
|
172
|
+
|
173
|
+
|
174
|
+
class TestSnapshotManager(unittest.TestCase):
|
175
|
+
"""Test SnapshotManager class - from test_snapshot_manager.py"""
|
176
|
+
|
177
|
+
@classmethod
|
178
|
+
def setUpClass(cls):
|
179
|
+
setup_sqlalchemy_mocks()
|
180
|
+
|
181
|
+
@classmethod
|
182
|
+
def tearDownClass(cls):
|
183
|
+
teardown_sqlalchemy_mocks()
|
184
|
+
|
185
|
+
def setUp(self):
|
186
|
+
"""Set up test fixtures"""
|
187
|
+
self.mock_client = Mock()
|
188
|
+
self.snapshot_manager = SnapshotManager(self.mock_client)
|
189
|
+
|
190
|
+
def test_snapshot_manager_creation(self):
|
191
|
+
"""Test SnapshotManager creation"""
|
192
|
+
self.assertEqual(self.snapshot_manager.client, self.mock_client)
|
193
|
+
|
194
|
+
def test_create_cluster_snapshot_with_enum(self):
|
195
|
+
"""Test creating cluster snapshot with enum"""
|
196
|
+
# Mock get method to return a snapshot
|
197
|
+
mock_snapshot = Snapshot("cluster_snap", SnapshotLevel.CLUSTER, datetime.now())
|
198
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
199
|
+
result = self.snapshot_manager.create("cluster_snap", SnapshotLevel.CLUSTER)
|
200
|
+
|
201
|
+
self.mock_client.execute.assert_called_once_with("CREATE SNAPSHOT cluster_snap FOR CLUSTER")
|
202
|
+
self.assertEqual(result, mock_snapshot)
|
203
|
+
|
204
|
+
def test_create_cluster_snapshot_with_string(self):
|
205
|
+
"""Test creating cluster snapshot with string level"""
|
206
|
+
# Mock get method to return a snapshot
|
207
|
+
mock_snapshot = Snapshot("cluster_snap", SnapshotLevel.CLUSTER, datetime.now())
|
208
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
209
|
+
result = self.snapshot_manager.create("cluster_snap", "cluster")
|
210
|
+
|
211
|
+
self.mock_client.execute.assert_called_once_with("CREATE SNAPSHOT cluster_snap FOR CLUSTER")
|
212
|
+
self.assertEqual(result, mock_snapshot)
|
213
|
+
|
214
|
+
def test_create_database_snapshot(self):
|
215
|
+
"""Test creating database snapshot"""
|
216
|
+
mock_snapshot = Snapshot("db_snap", SnapshotLevel.DATABASE, datetime.now(), database="test_db")
|
217
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
218
|
+
result = self.snapshot_manager.create("db_snap", SnapshotLevel.DATABASE, database="test_db")
|
219
|
+
|
220
|
+
self.mock_client.execute.assert_called_once_with("CREATE SNAPSHOT db_snap FOR DATABASE test_db")
|
221
|
+
self.assertEqual(result, mock_snapshot)
|
222
|
+
|
223
|
+
def test_create_table_snapshot(self):
|
224
|
+
"""Test creating table snapshot"""
|
225
|
+
mock_snapshot = Snapshot("table_snap", SnapshotLevel.TABLE, datetime.now(), database="test_db", table="test_table")
|
226
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
227
|
+
result = self.snapshot_manager.create("table_snap", SnapshotLevel.TABLE, database="test_db", table="test_table")
|
228
|
+
|
229
|
+
self.mock_client.execute.assert_called_once_with("CREATE SNAPSHOT table_snap FOR TABLE test_db test_table")
|
230
|
+
self.assertEqual(result, mock_snapshot)
|
231
|
+
|
232
|
+
def test_list_snapshots(self):
|
233
|
+
"""Test listing snapshots"""
|
234
|
+
# Mock the execute method to return a mock result object with fetchall method
|
235
|
+
mock_result = Mock()
|
236
|
+
mock_result.fetchall.return_value = [
|
237
|
+
("snap1", 1640995200000000000, "cluster", "root", None, None), # nanoseconds timestamp
|
238
|
+
("snap2", 1641081600000000000, "table", "root", "db1", "table1"),
|
239
|
+
]
|
240
|
+
self.mock_client.execute.return_value = mock_result
|
241
|
+
|
242
|
+
snapshots = self.snapshot_manager.list()
|
243
|
+
|
244
|
+
self.mock_client.execute.assert_called_once()
|
245
|
+
self.assertEqual(len(snapshots), 2)
|
246
|
+
self.assertEqual(snapshots[0].name, "snap1")
|
247
|
+
self.assertEqual(snapshots[0].level, SnapshotLevel.CLUSTER)
|
248
|
+
self.assertEqual(snapshots[1].name, "snap2")
|
249
|
+
self.assertEqual(snapshots[1].level, SnapshotLevel.TABLE)
|
250
|
+
|
251
|
+
def test_delete_snapshot(self):
|
252
|
+
"""Test deleting a snapshot"""
|
253
|
+
self.mock_client.execute.return_value = Mock()
|
254
|
+
|
255
|
+
# delete method returns None, so we just check that it doesn't raise an exception
|
256
|
+
self.snapshot_manager.delete("test_snap")
|
257
|
+
|
258
|
+
self.mock_client.execute.assert_called_once_with("DROP SNAPSHOT test_snap")
|
259
|
+
|
260
|
+
def test_exists_snapshot_true(self):
|
261
|
+
"""Test checking if snapshot exists (returns True)"""
|
262
|
+
with patch.object(self.snapshot_manager, 'get', return_value=Mock()):
|
263
|
+
result = self.snapshot_manager.exists("test_snap")
|
264
|
+
self.assertTrue(result)
|
265
|
+
|
266
|
+
def test_exists_snapshot_false(self):
|
267
|
+
"""Test checking if snapshot exists (returns False)"""
|
268
|
+
with patch.object(self.snapshot_manager, 'get', side_effect=SnapshotError("not found")):
|
269
|
+
result = self.snapshot_manager.exists("test_snap")
|
270
|
+
self.assertFalse(result)
|
271
|
+
|
272
|
+
|
273
|
+
class TestTransactionSnapshot(unittest.TestCase):
|
274
|
+
"""Test snapshot and transaction integration - from test_transaction_snapshot.py"""
|
275
|
+
|
276
|
+
@classmethod
|
277
|
+
def setUpClass(cls):
|
278
|
+
setup_sqlalchemy_mocks()
|
279
|
+
|
280
|
+
@classmethod
|
281
|
+
def tearDownClass(cls):
|
282
|
+
teardown_sqlalchemy_mocks()
|
283
|
+
|
284
|
+
def setUp(self):
|
285
|
+
"""Set up test fixtures"""
|
286
|
+
self.mock_client = Mock()
|
287
|
+
self.snapshot_manager = SnapshotManager(self.mock_client)
|
288
|
+
|
289
|
+
def test_snapshot_within_transaction(self):
|
290
|
+
"""Test creating snapshot within transaction context"""
|
291
|
+
# Mock transaction context
|
292
|
+
mock_transaction = Mock()
|
293
|
+
mock_transaction.execute = Mock()
|
294
|
+
|
295
|
+
# Mock snapshot creation
|
296
|
+
mock_snapshot = Snapshot("tx_snap", SnapshotLevel.TABLE, datetime.now(), database="test_db", table="test_table")
|
297
|
+
|
298
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
299
|
+
result = self.snapshot_manager.create(
|
300
|
+
"tx_snap", SnapshotLevel.TABLE, database="test_db", table="test_table", executor=mock_transaction
|
301
|
+
)
|
302
|
+
|
303
|
+
mock_transaction.execute.assert_called_once_with("CREATE SNAPSHOT tx_snap FOR TABLE test_db test_table")
|
304
|
+
self.assertEqual(result, mock_snapshot)
|
305
|
+
|
306
|
+
def test_snapshot_rollback_scenario(self):
|
307
|
+
"""Test snapshot behavior during rollback"""
|
308
|
+
# Mock transaction that fails
|
309
|
+
mock_transaction = Mock()
|
310
|
+
mock_transaction.execute.side_effect = Exception("Transaction failed")
|
311
|
+
|
312
|
+
with self.assertRaises(SnapshotError):
|
313
|
+
self.snapshot_manager.create("rollback_snap", SnapshotLevel.CLUSTER, executor=mock_transaction)
|
314
|
+
|
315
|
+
def test_snapshot_isolation_levels(self):
|
316
|
+
"""Test snapshot with different isolation levels"""
|
317
|
+
# Test that snapshot creation works regardless of isolation level
|
318
|
+
mock_snapshot = Snapshot("iso_snap", SnapshotLevel.DATABASE, datetime.now(), database="test_db")
|
319
|
+
|
320
|
+
with patch.object(self.snapshot_manager, 'get', return_value=mock_snapshot):
|
321
|
+
result = self.snapshot_manager.create("iso_snap", SnapshotLevel.DATABASE, database="test_db")
|
322
|
+
|
323
|
+
self.mock_client.execute.assert_called_once_with("CREATE SNAPSHOT iso_snap FOR DATABASE test_db")
|
324
|
+
self.assertEqual(result, mock_snapshot)
|
325
|
+
|
326
|
+
|
327
|
+
class TestSnapshotErrorHandling(unittest.TestCase):
|
328
|
+
"""Test snapshot error handling and edge cases"""
|
329
|
+
|
330
|
+
@classmethod
|
331
|
+
def setUpClass(cls):
|
332
|
+
setup_sqlalchemy_mocks()
|
333
|
+
|
334
|
+
@classmethod
|
335
|
+
def tearDownClass(cls):
|
336
|
+
teardown_sqlalchemy_mocks()
|
337
|
+
|
338
|
+
def setUp(self):
|
339
|
+
"""Set up test fixtures"""
|
340
|
+
self.mock_client = Mock()
|
341
|
+
self.snapshot_manager = SnapshotManager(self.mock_client)
|
342
|
+
|
343
|
+
def test_snapshot_creation_with_invalid_name(self):
|
344
|
+
"""Test snapshot creation with invalid name"""
|
345
|
+
with self.assertRaises((ValueError, SnapshotError)):
|
346
|
+
self.snapshot_manager.create("", SnapshotLevel.CLUSTER)
|
347
|
+
|
348
|
+
def test_snapshot_creation_with_invalid_level(self):
|
349
|
+
"""Test snapshot creation with invalid level"""
|
350
|
+
with self.assertRaises((ValueError, SnapshotError)):
|
351
|
+
self.snapshot_manager.create("test_snap", "invalid_level")
|
352
|
+
|
353
|
+
def test_snapshot_deletion_nonexistent(self):
|
354
|
+
"""Test deleting non-existent snapshot"""
|
355
|
+
self.mock_client.execute.side_effect = Exception("Snapshot not found")
|
356
|
+
|
357
|
+
with self.assertRaises(SnapshotError):
|
358
|
+
self.snapshot_manager.delete("nonexistent_snap")
|
359
|
+
|
360
|
+
def test_snapshot_list_empty(self):
|
361
|
+
"""Test listing snapshots when none exist"""
|
362
|
+
mock_result = Mock()
|
363
|
+
mock_result.fetchall.return_value = []
|
364
|
+
self.mock_client.execute.return_value = mock_result
|
365
|
+
|
366
|
+
snapshots = self.snapshot_manager.list()
|
367
|
+
|
368
|
+
self.assertEqual(len(snapshots), 0)
|
369
|
+
self.mock_client.execute.assert_called_once()
|
370
|
+
|
371
|
+
def test_snapshot_connection_error(self):
|
372
|
+
"""Test snapshot operations when not connected"""
|
373
|
+
# Mock client without engine
|
374
|
+
self.mock_client._engine = None
|
375
|
+
|
376
|
+
with self.assertRaises(MatrixOneConnectionError):
|
377
|
+
self.snapshot_manager.list()
|
378
|
+
|
379
|
+
with self.assertRaises(MatrixOneConnectionError):
|
380
|
+
self.snapshot_manager.delete("test_snap")
|
381
|
+
|
382
|
+
|
383
|
+
if __name__ == '__main__':
|
384
|
+
unittest.main()
|