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,382 @@
|
|
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 SQLAlchemy integration with MatrixOne (without database connection)
|
17
|
+
"""
|
18
|
+
|
19
|
+
import unittest
|
20
|
+
from unittest.mock import Mock, patch
|
21
|
+
from datetime import datetime
|
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
|
+
|
33
|
+
# Store original modules
|
34
|
+
_original_modules['pymysql'] = sys.modules.get('pymysql')
|
35
|
+
_original_modules['sqlalchemy'] = sys.modules.get('sqlalchemy')
|
36
|
+
_original_modules['sqlalchemy.engine'] = sys.modules.get('sqlalchemy.engine')
|
37
|
+
_original_modules['sqlalchemy.orm'] = sys.modules.get('sqlalchemy.orm')
|
38
|
+
|
39
|
+
# Mock the external dependencies
|
40
|
+
sys.modules['pymysql'] = Mock()
|
41
|
+
sys.modules['sqlalchemy'] = Mock()
|
42
|
+
sys.modules['sqlalchemy.engine'] = Mock()
|
43
|
+
sys.modules['sqlalchemy.engine'].Engine = Mock()
|
44
|
+
sys.modules['sqlalchemy.orm'] = Mock()
|
45
|
+
sys.modules['sqlalchemy.orm'].sessionmaker = Mock()
|
46
|
+
sys.modules['sqlalchemy.orm'].declarative_base = Mock()
|
47
|
+
sys.modules['sqlalchemy'].create_engine = Mock()
|
48
|
+
sys.modules['sqlalchemy'].text = Mock()
|
49
|
+
sys.modules['sqlalchemy'].Column = Mock()
|
50
|
+
sys.modules['sqlalchemy'].Integer = Mock()
|
51
|
+
sys.modules['sqlalchemy'].String = Mock()
|
52
|
+
sys.modules['sqlalchemy'].DateTime = Mock()
|
53
|
+
|
54
|
+
|
55
|
+
def teardown_sqlalchemy_mocks():
|
56
|
+
"""Restore original modules"""
|
57
|
+
global _original_modules
|
58
|
+
|
59
|
+
for module_name, original_module in _original_modules.items():
|
60
|
+
if original_module is not None:
|
61
|
+
sys.modules[module_name] = original_module
|
62
|
+
elif module_name in sys.modules:
|
63
|
+
del sys.modules[module_name]
|
64
|
+
|
65
|
+
|
66
|
+
# Add the matrixone package to the path
|
67
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'matrixone'))
|
68
|
+
|
69
|
+
from matrixone.snapshot import SnapshotLevel, Snapshot, SnapshotManager, CloneManager
|
70
|
+
from matrixone.client import (
|
71
|
+
Client,
|
72
|
+
TransactionWrapper,
|
73
|
+
TransactionSnapshotManager,
|
74
|
+
TransactionCloneManager,
|
75
|
+
)
|
76
|
+
from matrixone.exceptions import SnapshotError, CloneError
|
77
|
+
|
78
|
+
|
79
|
+
class TestSQLAlchemyIntegration(unittest.TestCase):
|
80
|
+
"""Test SQLAlchemy integration concepts"""
|
81
|
+
|
82
|
+
@classmethod
|
83
|
+
def setUpClass(cls):
|
84
|
+
"""Setup mocks for the entire test class"""
|
85
|
+
setup_sqlalchemy_mocks()
|
86
|
+
|
87
|
+
@classmethod
|
88
|
+
def tearDownClass(cls):
|
89
|
+
"""Restore original modules after tests"""
|
90
|
+
teardown_sqlalchemy_mocks()
|
91
|
+
|
92
|
+
def test_enum_usage(self):
|
93
|
+
"""Test SnapshotLevel enum usage"""
|
94
|
+
# Test enum values
|
95
|
+
self.assertEqual(SnapshotLevel.CLUSTER.value, "cluster")
|
96
|
+
self.assertEqual(SnapshotLevel.ACCOUNT.value, "account")
|
97
|
+
self.assertEqual(SnapshotLevel.DATABASE.value, "database")
|
98
|
+
self.assertEqual(SnapshotLevel.TABLE.value, "table")
|
99
|
+
|
100
|
+
# Test enum creation from string
|
101
|
+
self.assertEqual(SnapshotLevel("cluster"), SnapshotLevel.CLUSTER)
|
102
|
+
self.assertEqual(SnapshotLevel("table"), SnapshotLevel.TABLE)
|
103
|
+
|
104
|
+
def test_snapshot_with_enum(self):
|
105
|
+
"""Test Snapshot creation with enum"""
|
106
|
+
now = datetime.now()
|
107
|
+
snapshot = Snapshot("test_snap", SnapshotLevel.TABLE, now, database="test_db", table="test_table")
|
108
|
+
|
109
|
+
self.assertEqual(snapshot.name, "test_snap")
|
110
|
+
self.assertEqual(snapshot.level, SnapshotLevel.TABLE)
|
111
|
+
self.assertEqual(snapshot.database, "test_db")
|
112
|
+
self.assertEqual(snapshot.table, "test_table")
|
113
|
+
|
114
|
+
def test_snapshot_manager_with_enum(self):
|
115
|
+
"""Test SnapshotManager with enum"""
|
116
|
+
mock_client = Mock()
|
117
|
+
mock_client._connection = Mock()
|
118
|
+
snapshot_manager = SnapshotManager(mock_client)
|
119
|
+
|
120
|
+
# Mock the execute method
|
121
|
+
mock_client.execute = Mock()
|
122
|
+
mock_client.execute.return_value = Mock()
|
123
|
+
|
124
|
+
# Mock get method to return a snapshot
|
125
|
+
mock_snapshot = Snapshot("test_snap", SnapshotLevel.TABLE, datetime.now(), database="test_db", table="test_table")
|
126
|
+
with patch.object(snapshot_manager, 'get', return_value=mock_snapshot):
|
127
|
+
result = snapshot_manager.create("test_snap", SnapshotLevel.TABLE, database="test_db", table="test_table")
|
128
|
+
|
129
|
+
# Verify the correct SQL was generated
|
130
|
+
expected_sql = "CREATE SNAPSHOT test_snap FOR TABLE test_db test_table"
|
131
|
+
mock_client.execute.assert_called_once_with(expected_sql)
|
132
|
+
self.assertEqual(result, mock_snapshot)
|
133
|
+
|
134
|
+
def test_clone_manager_with_enum(self):
|
135
|
+
"""Test CloneManager with enum"""
|
136
|
+
mock_client = Mock()
|
137
|
+
mock_client._connection = Mock()
|
138
|
+
clone_manager = CloneManager(mock_client)
|
139
|
+
|
140
|
+
# Mock the execute method
|
141
|
+
mock_client.execute = Mock()
|
142
|
+
|
143
|
+
# Test database clone
|
144
|
+
clone_manager.clone_database("target_db", "source_db")
|
145
|
+
|
146
|
+
expected_sql = "CREATE DATABASE target_db CLONE source_db"
|
147
|
+
mock_client.execute.assert_called_once_with(expected_sql)
|
148
|
+
|
149
|
+
def test_transaction_wrapper(self):
|
150
|
+
"""Test TransactionWrapper with snapshots and clones"""
|
151
|
+
mock_client = Mock()
|
152
|
+
mock_client._connection = Mock()
|
153
|
+
mock_tx = TransactionWrapper(mock_client._connection, mock_client)
|
154
|
+
|
155
|
+
# Test that transaction wrapper has snapshot and clone managers
|
156
|
+
self.assertIsInstance(mock_tx.snapshots, TransactionSnapshotManager)
|
157
|
+
self.assertIsInstance(mock_tx.clone, TransactionCloneManager)
|
158
|
+
|
159
|
+
def test_transaction_snapshot_manager(self):
|
160
|
+
"""Test TransactionSnapshotManager"""
|
161
|
+
mock_client = Mock()
|
162
|
+
mock_client._connection = Mock()
|
163
|
+
mock_tx = Mock()
|
164
|
+
mock_tx.execute = Mock()
|
165
|
+
|
166
|
+
tx_snapshots = TransactionSnapshotManager(mock_client, mock_tx)
|
167
|
+
|
168
|
+
# Mock get method
|
169
|
+
mock_snapshot = Snapshot("test_snap", SnapshotLevel.CLUSTER, datetime.now())
|
170
|
+
with patch.object(tx_snapshots, 'get', return_value=mock_snapshot):
|
171
|
+
result = tx_snapshots.create("test_snap", SnapshotLevel.CLUSTER)
|
172
|
+
|
173
|
+
# Verify that the transaction's execute was called
|
174
|
+
mock_tx.execute.assert_called_once_with("CREATE SNAPSHOT test_snap FOR CLUSTER")
|
175
|
+
|
176
|
+
def test_transaction_clone_manager(self):
|
177
|
+
"""Test TransactionCloneManager"""
|
178
|
+
mock_client = Mock()
|
179
|
+
mock_client._connection = Mock()
|
180
|
+
mock_tx = Mock()
|
181
|
+
mock_tx.execute = Mock()
|
182
|
+
|
183
|
+
tx_clone = TransactionCloneManager(mock_client, mock_tx)
|
184
|
+
|
185
|
+
# Test database clone
|
186
|
+
tx_clone.clone_database("target_db", "source_db")
|
187
|
+
|
188
|
+
# Verify that the transaction's execute was called
|
189
|
+
mock_tx.execute.assert_called_once_with("CREATE DATABASE target_db CLONE source_db")
|
190
|
+
|
191
|
+
def test_sqlalchemy_integration_pattern(self):
|
192
|
+
"""Test the integration pattern without actual SQLAlchemy"""
|
193
|
+
|
194
|
+
# Simulate SQLAlchemy session
|
195
|
+
class MockSession:
|
196
|
+
def __init__(self):
|
197
|
+
self.data = []
|
198
|
+
self.committed = False
|
199
|
+
self.rolled_back = False
|
200
|
+
|
201
|
+
def add(self, item):
|
202
|
+
self.data.append(item)
|
203
|
+
|
204
|
+
def commit(self):
|
205
|
+
self.committed = True
|
206
|
+
|
207
|
+
def rollback(self):
|
208
|
+
self.rolled_back = True
|
209
|
+
self.data.clear()
|
210
|
+
|
211
|
+
def close(self):
|
212
|
+
pass
|
213
|
+
|
214
|
+
# Simulate MatrixOne client
|
215
|
+
mock_client = Mock()
|
216
|
+
mock_client._connection = Mock()
|
217
|
+
|
218
|
+
# Test the integration pattern
|
219
|
+
session = MockSession()
|
220
|
+
|
221
|
+
try:
|
222
|
+
# Simulate MatrixOne transaction
|
223
|
+
with patch.object(mock_client, 'transaction') as mock_transaction:
|
224
|
+
mock_tx = Mock()
|
225
|
+
mock_tx.execute = Mock()
|
226
|
+
mock_tx.snapshots = Mock()
|
227
|
+
mock_tx.clone = Mock()
|
228
|
+
|
229
|
+
mock_transaction.return_value.__enter__ = Mock(return_value=mock_tx)
|
230
|
+
mock_transaction.return_value.__exit__ = Mock(return_value=None)
|
231
|
+
|
232
|
+
with mock_client.transaction() as tx:
|
233
|
+
# Simulate SQLAlchemy operations
|
234
|
+
session.add("user1")
|
235
|
+
session.add("user2")
|
236
|
+
session.commit()
|
237
|
+
|
238
|
+
# Simulate MatrixOne operations
|
239
|
+
tx.snapshots.create("test_snapshot", SnapshotLevel.DATABASE, database="test")
|
240
|
+
tx.clone.clone_database("backup", "test")
|
241
|
+
|
242
|
+
# Verify SQLAlchemy operations
|
243
|
+
self.assertTrue(session.committed)
|
244
|
+
self.assertEqual(len(session.data), 2)
|
245
|
+
|
246
|
+
# Verify MatrixOne operations were called
|
247
|
+
tx.snapshots.create.assert_called_once()
|
248
|
+
tx.clone.clone_database.assert_called_once()
|
249
|
+
|
250
|
+
except Exception as e:
|
251
|
+
session.rollback()
|
252
|
+
raise
|
253
|
+
finally:
|
254
|
+
session.close()
|
255
|
+
|
256
|
+
def test_error_handling_pattern(self):
|
257
|
+
"""Test error handling pattern"""
|
258
|
+
|
259
|
+
class MockSession:
|
260
|
+
def __init__(self):
|
261
|
+
self.data = []
|
262
|
+
self.committed = False
|
263
|
+
self.rolled_back = False
|
264
|
+
|
265
|
+
def add(self, item):
|
266
|
+
if item == "error_item":
|
267
|
+
raise Exception("Simulated error")
|
268
|
+
self.data.append(item)
|
269
|
+
|
270
|
+
def commit(self):
|
271
|
+
self.committed = True
|
272
|
+
|
273
|
+
def rollback(self):
|
274
|
+
self.rolled_back = True
|
275
|
+
self.data.clear()
|
276
|
+
|
277
|
+
def close(self):
|
278
|
+
pass
|
279
|
+
|
280
|
+
mock_client = Mock()
|
281
|
+
mock_client._connection = Mock()
|
282
|
+
|
283
|
+
session = MockSession()
|
284
|
+
|
285
|
+
try:
|
286
|
+
with patch.object(mock_client, 'transaction') as mock_transaction:
|
287
|
+
mock_tx = Mock()
|
288
|
+
mock_tx.execute = Mock()
|
289
|
+
mock_tx.snapshots = Mock()
|
290
|
+
|
291
|
+
mock_transaction.return_value.__enter__ = Mock(return_value=mock_tx)
|
292
|
+
mock_transaction.return_value.__exit__ = Mock(return_value=None)
|
293
|
+
|
294
|
+
with mock_client.transaction() as tx:
|
295
|
+
# This will cause an error
|
296
|
+
session.add("error_item")
|
297
|
+
session.commit()
|
298
|
+
|
299
|
+
# This should not be reached
|
300
|
+
tx.snapshots.create("should_not_exist", SnapshotLevel.DATABASE, database="test")
|
301
|
+
|
302
|
+
except Exception as e:
|
303
|
+
session.rollback()
|
304
|
+
# Verify rollback occurred
|
305
|
+
self.assertTrue(session.rolled_back)
|
306
|
+
self.assertEqual(len(session.data), 0)
|
307
|
+
|
308
|
+
finally:
|
309
|
+
session.close()
|
310
|
+
|
311
|
+
def test_batch_operation_pattern(self):
|
312
|
+
"""Test batch operation pattern"""
|
313
|
+
|
314
|
+
class MockSession:
|
315
|
+
def __init__(self):
|
316
|
+
self.data = []
|
317
|
+
self.committed = False
|
318
|
+
|
319
|
+
def bulk_insert_mappings(self, model, data_list):
|
320
|
+
self.data.extend(data_list)
|
321
|
+
|
322
|
+
def commit(self):
|
323
|
+
self.committed = True
|
324
|
+
|
325
|
+
def close(self):
|
326
|
+
pass
|
327
|
+
|
328
|
+
mock_client = Mock()
|
329
|
+
mock_client._connection = Mock()
|
330
|
+
|
331
|
+
session = MockSession()
|
332
|
+
|
333
|
+
try:
|
334
|
+
# Simulate batch data
|
335
|
+
batch_data = [{"name": f"User_{i}", "email": f"user{i}@example.com"} for i in range(1, 11)] # 10 users
|
336
|
+
|
337
|
+
# Batch insert
|
338
|
+
session.bulk_insert_mappings("User", batch_data)
|
339
|
+
session.commit()
|
340
|
+
|
341
|
+
# Create snapshot
|
342
|
+
mock_client.execute = Mock()
|
343
|
+
snapshot_manager = SnapshotManager(mock_client)
|
344
|
+
mock_snapshot = Snapshot("batch_snapshot", SnapshotLevel.DATABASE, datetime.now(), database="test")
|
345
|
+
with patch.object(snapshot_manager, 'get', return_value=mock_snapshot):
|
346
|
+
result = snapshot_manager.create("batch_snapshot", SnapshotLevel.DATABASE, database="test")
|
347
|
+
|
348
|
+
# Verify batch operation
|
349
|
+
self.assertTrue(session.committed)
|
350
|
+
self.assertEqual(len(session.data), 10)
|
351
|
+
|
352
|
+
finally:
|
353
|
+
session.close()
|
354
|
+
|
355
|
+
|
356
|
+
if __name__ == '__main__':
|
357
|
+
# Create a test suite
|
358
|
+
test_suite = unittest.TestSuite()
|
359
|
+
|
360
|
+
# Add test cases using TestLoader
|
361
|
+
loader = unittest.TestLoader()
|
362
|
+
test_suite.addTests(loader.loadTestsFromTestCase(TestSQLAlchemyIntegration))
|
363
|
+
|
364
|
+
# Run the tests
|
365
|
+
runner = unittest.TextTestRunner(verbosity=2)
|
366
|
+
result = runner.run(test_suite)
|
367
|
+
|
368
|
+
# Print summary
|
369
|
+
print(f"\n{'='*50}")
|
370
|
+
print(f"Tests run: {result.testsRun}")
|
371
|
+
print(f"Failures: {len(result.failures)}")
|
372
|
+
print(f"Errors: {len(result.errors)}")
|
373
|
+
if result.testsRun > 0:
|
374
|
+
success_rate = (result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100
|
375
|
+
print(f"Success rate: {success_rate:.1f}%")
|
376
|
+
print(f"{'='*50}")
|
377
|
+
|
378
|
+
# Exit with appropriate code
|
379
|
+
if result.failures or result.errors:
|
380
|
+
sys.exit(1)
|
381
|
+
else:
|
382
|
+
sys.exit(0)
|