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.
Files changed (122) hide show
  1. matrixone/__init__.py +155 -0
  2. matrixone/account.py +723 -0
  3. matrixone/async_client.py +3913 -0
  4. matrixone/async_metadata_manager.py +311 -0
  5. matrixone/async_orm.py +123 -0
  6. matrixone/async_vector_index_manager.py +633 -0
  7. matrixone/base_client.py +208 -0
  8. matrixone/client.py +4672 -0
  9. matrixone/config.py +452 -0
  10. matrixone/connection_hooks.py +286 -0
  11. matrixone/exceptions.py +89 -0
  12. matrixone/logger.py +782 -0
  13. matrixone/metadata.py +820 -0
  14. matrixone/moctl.py +219 -0
  15. matrixone/orm.py +2277 -0
  16. matrixone/pitr.py +646 -0
  17. matrixone/pubsub.py +771 -0
  18. matrixone/restore.py +411 -0
  19. matrixone/search_vector_index.py +1176 -0
  20. matrixone/snapshot.py +550 -0
  21. matrixone/sql_builder.py +844 -0
  22. matrixone/sqlalchemy_ext/__init__.py +161 -0
  23. matrixone/sqlalchemy_ext/adapters.py +163 -0
  24. matrixone/sqlalchemy_ext/dialect.py +534 -0
  25. matrixone/sqlalchemy_ext/fulltext_index.py +895 -0
  26. matrixone/sqlalchemy_ext/fulltext_search.py +1686 -0
  27. matrixone/sqlalchemy_ext/hnsw_config.py +194 -0
  28. matrixone/sqlalchemy_ext/ivf_config.py +252 -0
  29. matrixone/sqlalchemy_ext/table_builder.py +351 -0
  30. matrixone/sqlalchemy_ext/vector_index.py +1721 -0
  31. matrixone/sqlalchemy_ext/vector_type.py +948 -0
  32. matrixone/version.py +580 -0
  33. matrixone_python_sdk-0.1.0.dist-info/METADATA +706 -0
  34. matrixone_python_sdk-0.1.0.dist-info/RECORD +122 -0
  35. matrixone_python_sdk-0.1.0.dist-info/WHEEL +5 -0
  36. matrixone_python_sdk-0.1.0.dist-info/entry_points.txt +5 -0
  37. matrixone_python_sdk-0.1.0.dist-info/licenses/LICENSE +200 -0
  38. matrixone_python_sdk-0.1.0.dist-info/top_level.txt +2 -0
  39. tests/__init__.py +19 -0
  40. tests/offline/__init__.py +20 -0
  41. tests/offline/conftest.py +77 -0
  42. tests/offline/test_account.py +703 -0
  43. tests/offline/test_async_client_query_comprehensive.py +1218 -0
  44. tests/offline/test_basic.py +54 -0
  45. tests/offline/test_case_sensitivity.py +227 -0
  46. tests/offline/test_connection_hooks_offline.py +287 -0
  47. tests/offline/test_dialect_schema_handling.py +609 -0
  48. tests/offline/test_explain_methods.py +346 -0
  49. tests/offline/test_filter_logical_in.py +237 -0
  50. tests/offline/test_fulltext_search_comprehensive.py +795 -0
  51. tests/offline/test_ivf_config.py +249 -0
  52. tests/offline/test_join_methods.py +281 -0
  53. tests/offline/test_join_sqlalchemy_compatibility.py +276 -0
  54. tests/offline/test_logical_in_method.py +237 -0
  55. tests/offline/test_matrixone_version_parsing.py +264 -0
  56. tests/offline/test_metadata_offline.py +557 -0
  57. tests/offline/test_moctl.py +300 -0
  58. tests/offline/test_moctl_simple.py +251 -0
  59. tests/offline/test_model_support_offline.py +359 -0
  60. tests/offline/test_model_support_simple.py +225 -0
  61. tests/offline/test_pinecone_filter_offline.py +377 -0
  62. tests/offline/test_pitr.py +585 -0
  63. tests/offline/test_pubsub.py +712 -0
  64. tests/offline/test_query_update.py +283 -0
  65. tests/offline/test_restore.py +445 -0
  66. tests/offline/test_snapshot_comprehensive.py +384 -0
  67. tests/offline/test_sql_escaping_edge_cases.py +551 -0
  68. tests/offline/test_sqlalchemy_integration.py +382 -0
  69. tests/offline/test_sqlalchemy_vector_integration.py +434 -0
  70. tests/offline/test_table_builder.py +198 -0
  71. tests/offline/test_unified_filter.py +398 -0
  72. tests/offline/test_unified_transaction.py +495 -0
  73. tests/offline/test_vector_index.py +238 -0
  74. tests/offline/test_vector_operations.py +688 -0
  75. tests/offline/test_vector_type.py +174 -0
  76. tests/offline/test_version_core.py +328 -0
  77. tests/offline/test_version_management.py +372 -0
  78. tests/offline/test_version_standalone.py +652 -0
  79. tests/online/__init__.py +20 -0
  80. tests/online/conftest.py +216 -0
  81. tests/online/test_account_management.py +194 -0
  82. tests/online/test_advanced_features.py +344 -0
  83. tests/online/test_async_client_interfaces.py +330 -0
  84. tests/online/test_async_client_online.py +285 -0
  85. tests/online/test_async_model_insert_online.py +293 -0
  86. tests/online/test_async_orm_online.py +300 -0
  87. tests/online/test_async_simple_query_online.py +802 -0
  88. tests/online/test_async_transaction_simple_query.py +300 -0
  89. tests/online/test_basic_connection.py +130 -0
  90. tests/online/test_client_online.py +238 -0
  91. tests/online/test_config.py +90 -0
  92. tests/online/test_config_validation.py +123 -0
  93. tests/online/test_connection_hooks_new_online.py +217 -0
  94. tests/online/test_dialect_schema_handling_online.py +331 -0
  95. tests/online/test_filter_logical_in_online.py +374 -0
  96. tests/online/test_fulltext_comprehensive.py +1773 -0
  97. tests/online/test_fulltext_label_online.py +433 -0
  98. tests/online/test_fulltext_search_online.py +842 -0
  99. tests/online/test_ivf_stats_online.py +506 -0
  100. tests/online/test_logger_integration.py +311 -0
  101. tests/online/test_matrixone_query_orm.py +540 -0
  102. tests/online/test_metadata_online.py +579 -0
  103. tests/online/test_model_insert_online.py +255 -0
  104. tests/online/test_mysql_driver_validation.py +213 -0
  105. tests/online/test_orm_advanced_features.py +2022 -0
  106. tests/online/test_orm_cte_integration.py +269 -0
  107. tests/online/test_orm_online.py +270 -0
  108. tests/online/test_pinecone_filter.py +708 -0
  109. tests/online/test_pubsub_operations.py +352 -0
  110. tests/online/test_query_methods.py +225 -0
  111. tests/online/test_query_update_online.py +433 -0
  112. tests/online/test_search_vector_index.py +557 -0
  113. tests/online/test_simple_fulltext_online.py +915 -0
  114. tests/online/test_snapshot_comprehensive.py +998 -0
  115. tests/online/test_sqlalchemy_engine_integration.py +336 -0
  116. tests/online/test_sqlalchemy_integration.py +425 -0
  117. tests/online/test_transaction_contexts.py +1219 -0
  118. tests/online/test_transaction_insert_methods.py +356 -0
  119. tests/online/test_transaction_query_methods.py +288 -0
  120. tests/online/test_unified_filter_online.py +529 -0
  121. tests/online/test_vector_comprehensive.py +706 -0
  122. tests/online/test_version_management.py +291 -0
@@ -0,0 +1,712 @@
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
+ Unit tests for PubSubManager
17
+ """
18
+
19
+ import unittest
20
+ from unittest.mock import Mock, patch, AsyncMock
21
+ import sys
22
+ import os
23
+ from datetime import datetime
24
+
25
+ # Add the matrixone package to the path
26
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'matrixone'))
27
+
28
+ from matrixone import Client, AsyncClient, PubSubError, Publication, Subscription
29
+ from matrixone.exceptions import PubSubError as PubSubErrorClass
30
+ from matrixone.pubsub import TransactionPubSubManager
31
+
32
+
33
+ class TestPubSubManager(unittest.TestCase):
34
+ """Test cases for PubSubManager"""
35
+
36
+ def setUp(self):
37
+ """Set up test fixtures"""
38
+ self.client = Client()
39
+ self.client._engine = Mock()
40
+ self.client._escape_identifier = lambda x: f"`{x}`"
41
+ self.client._escape_string = lambda x: f"'{x}'"
42
+ # Initialize managers manually for testing
43
+ from matrixone.pubsub import PubSubManager
44
+
45
+ self.pubsub_manager = PubSubManager(self.client)
46
+
47
+ def test_create_database_publication_success(self):
48
+ """Test successful database publication creation"""
49
+ # Mock successful execution
50
+ mock_result = Mock()
51
+ mock_result.rows = [('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None)]
52
+ self.client.execute = Mock(return_value=mock_result)
53
+
54
+ # Test create database publication
55
+ pub = self.pubsub_manager.create_database_publication("db_pub1", "central_db", "acc1")
56
+
57
+ # Verify
58
+ self.assertIsInstance(pub, Publication)
59
+ self.assertEqual(pub.name, "db_pub1")
60
+ self.assertEqual(pub.database, "central_db")
61
+ self.assertEqual(pub.tables, "*")
62
+ self.assertEqual(pub.sub_account, "acc1")
63
+ # Should be called twice: once for CREATE, once for SHOW
64
+ self.assertEqual(self.client.execute.call_count, 2)
65
+ self.client.execute.assert_any_call("CREATE PUBLICATION `db_pub1` DATABASE `central_db` ACCOUNT `acc1`")
66
+
67
+ def test_create_table_publication_success(self):
68
+ """Test successful table publication creation"""
69
+ # Mock successful execution
70
+ mock_result = Mock()
71
+ mock_result.rows = [('table_pub1', 'central_db', 'products', 'acc1', '', datetime.now(), None, None)]
72
+ self.client.execute = Mock(return_value=mock_result)
73
+
74
+ # Test create table publication
75
+ pub = self.pubsub_manager.create_table_publication("table_pub1", "central_db", "products", "acc1")
76
+
77
+ # Verify
78
+ self.assertIsInstance(pub, Publication)
79
+ self.assertEqual(pub.name, "table_pub1")
80
+ self.assertEqual(pub.database, "central_db")
81
+ self.assertEqual(pub.tables, "products")
82
+ self.assertEqual(pub.sub_account, "acc1")
83
+ # Should be called twice: once for CREATE, once for SHOW
84
+ self.assertEqual(self.client.execute.call_count, 2)
85
+ self.client.execute.assert_any_call(
86
+ "CREATE PUBLICATION `table_pub1` DATABASE `central_db` TABLE `products` ACCOUNT `acc1`"
87
+ )
88
+
89
+ def test_get_publication_success(self):
90
+ """Test successful publication retrieval"""
91
+ # Mock successful execution
92
+ mock_result = Mock()
93
+ mock_result.rows = [('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None)]
94
+ self.client.execute = Mock(return_value=mock_result)
95
+
96
+ # Test get publication
97
+ pub = self.pubsub_manager.get_publication("db_pub1")
98
+
99
+ # Verify
100
+ self.assertIsInstance(pub, Publication)
101
+ self.assertEqual(pub.name, "db_pub1")
102
+ self.client.execute.assert_called_with("SHOW PUBLICATIONS")
103
+
104
+ def test_get_publication_not_found(self):
105
+ """Test publication not found"""
106
+ # Mock empty result
107
+ mock_result = Mock()
108
+ mock_result.rows = []
109
+ self.client.execute = Mock(return_value=mock_result)
110
+
111
+ # Test get publication not found
112
+ with self.assertRaises(PubSubErrorClass):
113
+ self.pubsub_manager.get_publication("nonexistent_pub")
114
+
115
+ def test_list_publications_success(self):
116
+ """Test successful publication listing"""
117
+ # Mock successful execution
118
+ mock_result = Mock()
119
+ mock_result.rows = [
120
+ ('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None),
121
+ ('table_pub1', 'central_db', 'products', 'acc1', '', datetime.now(), None, None),
122
+ ]
123
+ self.client.execute = Mock(return_value=mock_result)
124
+
125
+ # Test list publications
126
+ pubs = self.pubsub_manager.list_publications()
127
+
128
+ # Verify
129
+ self.assertEqual(len(pubs), 2)
130
+ self.assertIsInstance(pubs[0], Publication)
131
+ self.assertIsInstance(pubs[1], Publication)
132
+ self.client.execute.assert_called_with("SHOW PUBLICATIONS")
133
+
134
+ def test_list_publications_with_filters(self):
135
+ """Test publication listing with filters"""
136
+ # Mock successful execution
137
+ mock_result = Mock()
138
+ mock_result.rows = [('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None)]
139
+ self.client.execute = Mock(return_value=mock_result)
140
+
141
+ # Test list publications with filters
142
+ pubs = self.pubsub_manager.list_publications(account="acc1", database="central_db")
143
+
144
+ # Verify
145
+ self.assertEqual(len(pubs), 1)
146
+ self.client.execute.assert_called_with("SHOW PUBLICATIONS")
147
+
148
+ def test_alter_publication_success(self):
149
+ """Test successful publication alteration"""
150
+ # Mock successful execution
151
+ mock_result = Mock()
152
+ mock_result.rows = [('db_pub1', 'sys', 'central_db', '*', None, datetime.now())]
153
+ self.client.execute = Mock(return_value=mock_result)
154
+
155
+ # Test alter publication
156
+ pub = self.pubsub_manager.alter_publication("db_pub1", account="acc2")
157
+
158
+ # Verify
159
+ self.assertIsInstance(pub, Publication)
160
+ # Should be called twice: once for ALTER, once for SHOW
161
+ self.assertEqual(self.client.execute.call_count, 2)
162
+ self.client.execute.assert_any_call("ALTER PUBLICATION `db_pub1` ACCOUNT `acc2`")
163
+
164
+ def test_drop_publication_success(self):
165
+ """Test successful publication deletion"""
166
+ # Mock successful execution
167
+ mock_result = Mock()
168
+ self.client.execute = Mock(return_value=mock_result)
169
+
170
+ # Test drop publication
171
+ result = self.pubsub_manager.drop_publication("db_pub1")
172
+
173
+ # Verify
174
+ self.assertTrue(result)
175
+ self.client.execute.assert_called_with("DROP PUBLICATION `db_pub1`")
176
+
177
+ def test_create_subscription_success(self):
178
+ """Test successful subscription creation"""
179
+ # Mock successful execution
180
+ mock_result = Mock()
181
+ mock_result.rows = [
182
+ (
183
+ 'db_pub1',
184
+ 'sys',
185
+ 'central_db',
186
+ '*',
187
+ None,
188
+ datetime.now(),
189
+ 'sub_db1',
190
+ datetime.now(),
191
+ 0,
192
+ )
193
+ ]
194
+ self.client.execute = Mock(return_value=mock_result)
195
+
196
+ # Test create subscription
197
+ sub = self.pubsub_manager.create_subscription("sub_db1", "db_pub1", "sys")
198
+
199
+ # Verify
200
+ self.assertIsInstance(sub, Subscription)
201
+ self.assertEqual(sub.sub_name, "sub_db1")
202
+ self.assertEqual(sub.pub_name, "db_pub1")
203
+ # Should be called twice: once for CREATE, once for SHOW
204
+ self.assertEqual(self.client.execute.call_count, 2)
205
+ self.client.execute.assert_any_call("CREATE DATABASE `sub_db1` FROM `sys` PUBLICATION `db_pub1`")
206
+
207
+ def test_get_subscription_success(self):
208
+ """Test successful subscription retrieval"""
209
+ # Mock successful execution
210
+ mock_result = Mock()
211
+ mock_result.rows = [
212
+ (
213
+ 'db_pub1',
214
+ 'sys',
215
+ 'central_db',
216
+ '*',
217
+ None,
218
+ datetime.now(),
219
+ 'sub_db1',
220
+ datetime.now(),
221
+ 0,
222
+ )
223
+ ]
224
+ self.client.execute = Mock(return_value=mock_result)
225
+
226
+ # Test get subscription
227
+ sub = self.pubsub_manager.get_subscription("sub_db1")
228
+
229
+ # Verify
230
+ self.assertIsInstance(sub, Subscription)
231
+ self.assertEqual(sub.sub_name, "sub_db1")
232
+ self.client.execute.assert_called_with("SHOW SUBSCRIPTIONS")
233
+
234
+ def test_list_subscriptions_success(self):
235
+ """Test successful subscription listing"""
236
+ # Mock successful execution
237
+ mock_result = Mock()
238
+ mock_result.rows = [
239
+ (
240
+ 'db_pub1',
241
+ 'sys',
242
+ 'central_db',
243
+ '*',
244
+ None,
245
+ datetime.now(),
246
+ 'sub_db1',
247
+ datetime.now(),
248
+ 0,
249
+ ),
250
+ (
251
+ 'table_pub1',
252
+ 'sys',
253
+ 'central_db',
254
+ 'products',
255
+ None,
256
+ datetime.now(),
257
+ 'sub_table1',
258
+ datetime.now(),
259
+ 0,
260
+ ),
261
+ ]
262
+ self.client.execute = Mock(return_value=mock_result)
263
+
264
+ # Test list subscriptions
265
+ subs = self.pubsub_manager.list_subscriptions()
266
+
267
+ # Verify
268
+ self.assertEqual(len(subs), 2)
269
+ self.assertIsInstance(subs[0], Subscription)
270
+ self.assertIsInstance(subs[1], Subscription)
271
+ self.client.execute.assert_called_with("SHOW SUBSCRIPTIONS")
272
+
273
+
274
+ class TestAsyncPubSubManager(unittest.IsolatedAsyncioTestCase):
275
+ """Test cases for AsyncPubSubManager"""
276
+
277
+ async def asyncSetUp(self):
278
+ """Set up async test fixtures"""
279
+ self.client = AsyncClient()
280
+ self.client._engine = Mock()
281
+ self.client._escape_identifier = lambda x: f"`{x}`"
282
+ self.client._escape_string = lambda x: f"'{x}'"
283
+ # Initialize managers manually for testing
284
+ from matrixone.async_client import AsyncPubSubManager
285
+
286
+ self.pubsub_manager = AsyncPubSubManager(self.client)
287
+
288
+ async def test_async_create_database_publication_success(self):
289
+ """Test successful async database publication creation"""
290
+ # Mock successful execution
291
+ mock_result = Mock()
292
+ mock_result.rows = [('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None)]
293
+ self.client.execute = AsyncMock(return_value=mock_result)
294
+
295
+ # Test create database publication
296
+ pub = await self.pubsub_manager.create_database_publication("db_pub1", "central_db", "acc1")
297
+
298
+ # Verify
299
+ self.assertIsInstance(pub, Publication)
300
+ self.assertEqual(pub.name, "db_pub1")
301
+ self.assertEqual(pub.database, "central_db")
302
+ self.assertEqual(pub.tables, "*")
303
+ self.assertEqual(pub.sub_account, "acc1")
304
+ # Should be called twice: once for CREATE, once for SHOW
305
+ self.assertEqual(self.client.execute.call_count, 2)
306
+ self.client.execute.assert_any_call("CREATE PUBLICATION `db_pub1` DATABASE `central_db` ACCOUNT `acc1`")
307
+
308
+ async def test_async_create_table_publication_success(self):
309
+ """Test successful async table publication creation"""
310
+ # Mock successful execution
311
+ mock_result = Mock()
312
+ mock_result.rows = [('table_pub1', 'central_db', 'products', 'acc1', '', datetime.now(), None, None)]
313
+ self.client.execute = AsyncMock(return_value=mock_result)
314
+
315
+ # Test create table publication
316
+ pub = await self.pubsub_manager.create_table_publication("table_pub1", "central_db", "products", "acc1")
317
+
318
+ # Verify
319
+ self.assertIsInstance(pub, Publication)
320
+ self.assertEqual(pub.name, "table_pub1")
321
+ self.assertEqual(pub.database, "central_db")
322
+ self.assertEqual(pub.tables, "products")
323
+ self.assertEqual(pub.sub_account, "acc1")
324
+ # Should be called twice: once for CREATE, once for SHOW
325
+ self.assertEqual(self.client.execute.call_count, 2)
326
+ self.client.execute.assert_any_call(
327
+ "CREATE PUBLICATION `table_pub1` DATABASE `central_db` TABLE `products` ACCOUNT `acc1`"
328
+ )
329
+
330
+ async def test_async_get_publication_success(self):
331
+ """Test successful async publication retrieval"""
332
+ # Mock successful execution
333
+ mock_result = Mock()
334
+ mock_result.rows = [('db_pub1', 'central_db', '*', 'acc1', '', datetime.now(), None, None)]
335
+ self.client.execute = AsyncMock(return_value=mock_result)
336
+
337
+ # Test get publication
338
+ pub = await self.pubsub_manager.get_publication("db_pub1")
339
+
340
+ # Verify
341
+ self.assertIsInstance(pub, Publication)
342
+ self.assertEqual(pub.name, "db_pub1")
343
+ self.client.execute.assert_called_with("SHOW PUBLICATIONS")
344
+
345
+ async def test_async_list_publications_success(self):
346
+ """Test successful async publication listing"""
347
+ # Mock successful execution
348
+ mock_result = Mock()
349
+ mock_result.rows = [
350
+ ('db_pub1', 'sys', 'central_db', '*', None, datetime.now()),
351
+ ('table_pub1', 'sys', 'central_db', 'products', None, datetime.now()),
352
+ ]
353
+ self.client.execute = AsyncMock(return_value=mock_result)
354
+
355
+ # Test list publications
356
+ pubs = await self.pubsub_manager.list_publications()
357
+
358
+ # Verify
359
+ self.assertEqual(len(pubs), 2)
360
+ self.assertIsInstance(pubs[0], Publication)
361
+ self.assertIsInstance(pubs[1], Publication)
362
+ self.client.execute.assert_called_with("SHOW PUBLICATIONS")
363
+
364
+ async def test_async_alter_publication_success(self):
365
+ """Test successful async publication alteration"""
366
+ # Mock successful execution
367
+ mock_result = Mock()
368
+ mock_result.rows = [('db_pub1', 'sys', 'central_db', '*', None, datetime.now())]
369
+ self.client.execute = AsyncMock(return_value=mock_result)
370
+
371
+ # Test alter publication
372
+ pub = await self.pubsub_manager.alter_publication("db_pub1", account="acc2")
373
+
374
+ # Verify
375
+ self.assertIsInstance(pub, Publication)
376
+ # Should be called twice: once for ALTER, once for SHOW
377
+ self.assertEqual(self.client.execute.call_count, 2)
378
+ self.client.execute.assert_any_call("ALTER PUBLICATION `db_pub1` ACCOUNT `acc2`")
379
+
380
+ async def test_async_drop_publication_success(self):
381
+ """Test successful async publication deletion"""
382
+ # Mock successful execution
383
+ mock_result = Mock()
384
+ self.client.execute = AsyncMock(return_value=mock_result)
385
+
386
+ # Test drop publication
387
+ result = await self.pubsub_manager.drop_publication("db_pub1")
388
+
389
+ # Verify
390
+ self.assertTrue(result)
391
+ self.client.execute.assert_called_with("DROP PUBLICATION `db_pub1`")
392
+
393
+ async def test_async_create_subscription_success(self):
394
+ """Test successful async subscription creation"""
395
+ # Mock successful execution
396
+ mock_result = Mock()
397
+ mock_result.rows = [
398
+ (
399
+ 'db_pub1',
400
+ 'sys',
401
+ 'central_db',
402
+ '*',
403
+ None,
404
+ datetime.now(),
405
+ 'sub_db1',
406
+ datetime.now(),
407
+ 0,
408
+ )
409
+ ]
410
+ self.client.execute = AsyncMock(return_value=mock_result)
411
+
412
+ # Test create subscription
413
+ sub = await self.pubsub_manager.create_subscription("sub_db1", "db_pub1", "sys")
414
+
415
+ # Verify
416
+ self.assertIsInstance(sub, Subscription)
417
+ self.assertEqual(sub.sub_name, "sub_db1")
418
+ self.assertEqual(sub.pub_name, "db_pub1")
419
+ # Should be called twice: once for CREATE, once for SHOW
420
+ self.assertEqual(self.client.execute.call_count, 2)
421
+ self.client.execute.assert_any_call("CREATE DATABASE `sub_db1` FROM `sys` PUBLICATION `db_pub1`")
422
+
423
+ async def test_async_get_subscription_success(self):
424
+ """Test successful async subscription retrieval"""
425
+ # Mock successful execution
426
+ mock_result = Mock()
427
+ mock_result.rows = [
428
+ (
429
+ 'db_pub1',
430
+ 'sys',
431
+ 'central_db',
432
+ '*',
433
+ None,
434
+ datetime.now(),
435
+ 'sub_db1',
436
+ datetime.now(),
437
+ 0,
438
+ )
439
+ ]
440
+ self.client.execute = AsyncMock(return_value=mock_result)
441
+
442
+ # Test get subscription
443
+ sub = await self.pubsub_manager.get_subscription("sub_db1")
444
+
445
+ # Verify
446
+ self.assertIsInstance(sub, Subscription)
447
+ self.assertEqual(sub.sub_name, "sub_db1")
448
+ self.client.execute.assert_called_with("SHOW SUBSCRIPTIONS")
449
+
450
+ async def test_async_list_subscriptions_success(self):
451
+ """Test successful async subscription listing"""
452
+ # Mock successful execution
453
+ mock_result = Mock()
454
+ mock_result.rows = [
455
+ (
456
+ 'db_pub1',
457
+ 'sys',
458
+ 'central_db',
459
+ '*',
460
+ None,
461
+ datetime.now(),
462
+ 'sub_db1',
463
+ datetime.now(),
464
+ 0,
465
+ ),
466
+ (
467
+ 'table_pub1',
468
+ 'sys',
469
+ 'central_db',
470
+ 'products',
471
+ None,
472
+ datetime.now(),
473
+ 'sub_table1',
474
+ datetime.now(),
475
+ 0,
476
+ ),
477
+ ]
478
+ self.client.execute = AsyncMock(return_value=mock_result)
479
+
480
+ # Test list subscriptions
481
+ subs = await self.pubsub_manager.list_subscriptions()
482
+
483
+ # Verify
484
+ self.assertEqual(len(subs), 2)
485
+ self.assertIsInstance(subs[0], Subscription)
486
+ self.assertIsInstance(subs[1], Subscription)
487
+ self.client.execute.assert_called_with("SHOW SUBSCRIPTIONS")
488
+
489
+
490
+ class TestTransactionPubSubManager(unittest.TestCase):
491
+ """Test cases for TransactionPubSubManager"""
492
+
493
+ def setUp(self):
494
+ """Set up test fixtures"""
495
+ self.client = Client()
496
+ self.client._engine = Mock()
497
+ self.client._escape_identifier = lambda x: f"`{x}`"
498
+ self.client._escape_string = lambda x: f"'{x}'"
499
+ self.transaction_wrapper = Mock()
500
+ self.transaction_pubsub_manager = TransactionPubSubManager(self.client, self.transaction_wrapper)
501
+
502
+ def test_transaction_create_database_publication(self):
503
+ """Test create database publication within transaction"""
504
+ # Mock successful execution
505
+ mock_result = Mock()
506
+ mock_result.rows = [('db_pub1', 'sys', 'central_db', '*', None, datetime.now())]
507
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
508
+
509
+ # Test create database publication
510
+ pub = self.transaction_pubsub_manager.create_database_publication("db_pub1", "central_db", "acc1")
511
+
512
+ # Verify
513
+ self.assertIsInstance(pub, Publication)
514
+ self.assertEqual(pub.name, "db_pub1")
515
+ # Should be called twice: once for CREATE, once for SHOW
516
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
517
+ self.transaction_wrapper.execute.assert_any_call("CREATE PUBLICATION `db_pub1` DATABASE `central_db` ACCOUNT `acc1`")
518
+
519
+ def test_transaction_create_table_publication(self):
520
+ """Test create table publication within transaction"""
521
+ # Mock successful execution
522
+ mock_result = Mock()
523
+ mock_result.rows = [('table_pub1', 'sys', 'central_db', 'products', None, datetime.now())]
524
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
525
+
526
+ # Test create table publication
527
+ pub = self.transaction_pubsub_manager.create_table_publication("table_pub1", "central_db", "products", "acc1")
528
+
529
+ # Verify
530
+ self.assertIsInstance(pub, Publication)
531
+ self.assertEqual(pub.name, "table_pub1")
532
+ # Should be called twice: once for CREATE, once for SHOW
533
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
534
+ self.transaction_wrapper.execute.assert_any_call(
535
+ "CREATE PUBLICATION `table_pub1` DATABASE `central_db` TABLE `products` ACCOUNT `acc1`"
536
+ )
537
+
538
+ def test_transaction_get_publication(self):
539
+ """Test get publication within transaction"""
540
+ # Mock successful execution
541
+ mock_result = Mock()
542
+ mock_result.rows = [('db_pub1', 'sys', 'central_db', '*', None, datetime.now())]
543
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
544
+
545
+ # Test get publication
546
+ pub = self.transaction_pubsub_manager.get_publication("db_pub1")
547
+
548
+ # Verify
549
+ self.assertIsInstance(pub, Publication)
550
+ self.assertEqual(pub.name, "db_pub1")
551
+ self.transaction_wrapper.execute.assert_called_with("SHOW PUBLICATIONS WHERE pub_name = 'db_pub1'")
552
+
553
+ def test_transaction_list_publications(self):
554
+ """Test list publications within transaction"""
555
+ # Mock successful execution
556
+ mock_result = Mock()
557
+ mock_result.rows = [
558
+ ('db_pub1', 'sys', 'central_db', '*', None, datetime.now()),
559
+ ('table_pub1', 'sys', 'central_db', 'products', None, datetime.now()),
560
+ ]
561
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
562
+
563
+ # Test list publications
564
+ pubs = self.transaction_pubsub_manager.list_publications()
565
+
566
+ # Verify
567
+ self.assertEqual(len(pubs), 2)
568
+ self.assertIsInstance(pubs[0], Publication)
569
+ self.assertIsInstance(pubs[1], Publication)
570
+ self.transaction_wrapper.execute.assert_called_with("SHOW PUBLICATIONS")
571
+
572
+ def test_transaction_alter_publication(self):
573
+ """Test alter publication within transaction"""
574
+ # Mock successful execution
575
+ mock_result = Mock()
576
+ mock_result.rows = [('db_pub1', 'sys', 'central_db', '*', None, datetime.now())]
577
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
578
+
579
+ # Test alter publication
580
+ pub = self.transaction_pubsub_manager.alter_publication("db_pub1", account="acc2")
581
+
582
+ # Verify
583
+ self.assertIsInstance(pub, Publication)
584
+ # Should be called twice: once for ALTER, once for SHOW
585
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
586
+ self.transaction_wrapper.execute.assert_any_call("ALTER PUBLICATION `db_pub1` ACCOUNT `acc2`")
587
+
588
+ def test_transaction_drop_publication(self):
589
+ """Test drop publication within transaction"""
590
+ # Mock successful execution
591
+ mock_result = Mock()
592
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
593
+
594
+ # Test drop publication
595
+ result = self.transaction_pubsub_manager.drop_publication("db_pub1")
596
+
597
+ # Verify
598
+ self.assertTrue(result)
599
+ self.transaction_wrapper.execute.assert_called_with("DROP PUBLICATION `db_pub1`")
600
+
601
+ def test_transaction_create_subscription(self):
602
+ """Test create subscription within transaction"""
603
+ # Mock successful execution
604
+ mock_result = Mock()
605
+ mock_result.rows = [
606
+ (
607
+ 'db_pub1',
608
+ 'sys',
609
+ 'central_db',
610
+ '*',
611
+ None,
612
+ datetime.now(),
613
+ 'sub_db1',
614
+ datetime.now(),
615
+ 0,
616
+ )
617
+ ]
618
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
619
+
620
+ # Test create subscription
621
+ sub = self.transaction_pubsub_manager.create_subscription("sub_db1", "db_pub1", "sys")
622
+
623
+ # Verify
624
+ self.assertIsInstance(sub, Subscription)
625
+ self.assertEqual(sub.sub_name, "sub_db1")
626
+ # Should be called twice: once for CREATE, once for SHOW
627
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
628
+ self.transaction_wrapper.execute.assert_any_call("CREATE DATABASE `sub_db1` FROM `sys` PUBLICATION `db_pub1`")
629
+
630
+ def test_transaction_get_subscription(self):
631
+ """Test get subscription within transaction"""
632
+ # Mock successful execution
633
+ mock_result = Mock()
634
+ mock_result.rows = [
635
+ (
636
+ 'db_pub1',
637
+ 'sys',
638
+ 'central_db',
639
+ '*',
640
+ None,
641
+ datetime.now(),
642
+ 'sub_db1',
643
+ datetime.now(),
644
+ 0,
645
+ )
646
+ ]
647
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
648
+
649
+ # Test get subscription
650
+ sub = self.transaction_pubsub_manager.get_subscription("sub_db1")
651
+
652
+ # Verify
653
+ self.assertIsInstance(sub, Subscription)
654
+ self.assertEqual(sub.sub_name, "sub_db1")
655
+ self.transaction_wrapper.execute.assert_called_with("SHOW SUBSCRIPTIONS")
656
+
657
+ def test_transaction_list_subscriptions(self):
658
+ """Test list subscriptions within transaction"""
659
+ # Mock successful execution
660
+ mock_result = Mock()
661
+ mock_result.rows = [
662
+ (
663
+ 'db_pub1',
664
+ 'sys',
665
+ 'central_db',
666
+ '*',
667
+ None,
668
+ datetime.now(),
669
+ 'sub_db1',
670
+ datetime.now(),
671
+ 0,
672
+ ),
673
+ (
674
+ 'table_pub1',
675
+ 'sys',
676
+ 'central_db',
677
+ 'products',
678
+ None,
679
+ datetime.now(),
680
+ 'sub_table1',
681
+ datetime.now(),
682
+ 0,
683
+ ),
684
+ ]
685
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
686
+
687
+ # Test list subscriptions
688
+ subs = self.transaction_pubsub_manager.list_subscriptions()
689
+
690
+ # Verify
691
+ self.assertEqual(len(subs), 2)
692
+ self.assertIsInstance(subs[0], Subscription)
693
+ self.assertIsInstance(subs[1], Subscription)
694
+ self.transaction_wrapper.execute.assert_called_with("SHOW SUBSCRIPTIONS")
695
+
696
+
697
+ if __name__ == '__main__':
698
+ # Create test suite
699
+ loader = unittest.TestLoader()
700
+ suite = unittest.TestSuite()
701
+
702
+ # Add test cases
703
+ suite.addTests(loader.loadTestsFromTestCase(TestPubSubManager))
704
+ suite.addTests(loader.loadTestsFromTestCase(TestAsyncPubSubManager))
705
+ suite.addTests(loader.loadTestsFromTestCase(TestTransactionPubSubManager))
706
+
707
+ # Run tests
708
+ runner = unittest.TextTestRunner(verbosity=2)
709
+ result = runner.run(suite)
710
+
711
+ # Exit with appropriate code
712
+ sys.exit(0 if result.wasSuccessful() else 1)