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,585 @@
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 PitrManager
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, PitrError, Pitr
29
+ from matrixone.exceptions import PitrError as PitrErrorClass
30
+ from matrixone.pitr import TransactionPitrManager
31
+
32
+
33
+ class TestPitrManager(unittest.TestCase):
34
+ """Test cases for PitrManager"""
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.pitr import PitrManager
44
+
45
+ self.pitr_manager = PitrManager(self.client)
46
+
47
+ def test_create_cluster_pitr_success(self):
48
+ """Test successful cluster PITR creation"""
49
+ # Mock successful execution
50
+ mock_result = Mock()
51
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
52
+ self.client.execute = Mock(return_value=mock_result)
53
+
54
+ # Test create cluster PITR
55
+ pitr = self.pitr_manager.create_cluster_pitr("cluster_pitr1", 1, "d")
56
+
57
+ # Verify
58
+ self.assertIsInstance(pitr, Pitr)
59
+ self.assertEqual(pitr.name, "cluster_pitr1")
60
+ self.assertEqual(pitr.level, "cluster")
61
+ self.assertEqual(pitr.range_value, 1)
62
+ self.assertEqual(pitr.range_unit, "d")
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 PITR `cluster_pitr1` FOR CLUSTER RANGE 1 'd'")
66
+
67
+ def test_create_cluster_pitr_failure(self):
68
+ """Test cluster PITR creation failure"""
69
+ # Mock execution failure
70
+ self.client.execute = Mock(side_effect=Exception("Permission denied"))
71
+
72
+ # Test create cluster PITR failure
73
+ with self.assertRaises(PitrErrorClass):
74
+ self.pitr_manager.create_cluster_pitr("cluster_pitr1", 1, "d")
75
+
76
+ def test_create_account_pitr_success(self):
77
+ """Test successful account PITR creation"""
78
+ # Mock successful execution
79
+ mock_result = Mock()
80
+ mock_result.rows = [('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h')]
81
+ self.client.execute = Mock(return_value=mock_result)
82
+
83
+ # Test create account PITR for current account
84
+ pitr = self.pitr_manager.create_account_pitr("account_pitr1", range_value=2, range_unit="h")
85
+
86
+ # Verify
87
+ self.assertIsInstance(pitr, Pitr)
88
+ self.assertEqual(pitr.name, "account_pitr1")
89
+ self.assertEqual(pitr.level, "account")
90
+ self.assertEqual(pitr.range_value, 2)
91
+ self.assertEqual(pitr.range_unit, "h")
92
+ # Should be called twice: once for CREATE, once for SHOW
93
+ self.assertEqual(self.client.execute.call_count, 2)
94
+ self.client.execute.assert_any_call("CREATE PITR `account_pitr1` FOR ACCOUNT RANGE 2 'h'")
95
+
96
+ def test_create_account_pitr_for_specific_account(self):
97
+ """Test create account PITR for specific account"""
98
+ # Mock successful execution
99
+ mock_result = Mock()
100
+ mock_result.rows = [('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 1, 'd')]
101
+ self.client.execute = Mock(return_value=mock_result)
102
+
103
+ # Test create account PITR for specific account
104
+ pitr = self.pitr_manager.create_account_pitr("account_pitr1", "acc1", 1, "d")
105
+
106
+ # Verify
107
+ self.assertIsInstance(pitr, Pitr)
108
+ # Should be called twice: once for CREATE, once for SHOW
109
+ self.assertEqual(self.client.execute.call_count, 2)
110
+ self.client.execute.assert_any_call("CREATE PITR `account_pitr1` FOR ACCOUNT `acc1` RANGE 1 'd'")
111
+
112
+ def test_create_database_pitr_success(self):
113
+ """Test successful database PITR creation"""
114
+ # Mock successful execution
115
+ mock_result = Mock()
116
+ mock_result.rows = [('db_pitr1', datetime.now(), datetime.now(), 'database', 'acc1', 'db1', '*', 1, 'y')]
117
+ self.client.execute = Mock(return_value=mock_result)
118
+
119
+ # Test create database PITR
120
+ pitr = self.pitr_manager.create_database_pitr("db_pitr1", "db1", 1, "y")
121
+
122
+ # Verify
123
+ self.assertIsInstance(pitr, Pitr)
124
+ self.assertEqual(pitr.name, "db_pitr1")
125
+ self.assertEqual(pitr.level, "database")
126
+ self.assertEqual(pitr.database_name, "db1")
127
+ self.assertEqual(pitr.range_value, 1)
128
+ self.assertEqual(pitr.range_unit, "y")
129
+ # Should be called twice: once for CREATE, once for SHOW
130
+ self.assertEqual(self.client.execute.call_count, 2)
131
+ self.client.execute.assert_any_call("CREATE PITR `db_pitr1` FOR DATABASE `db1` RANGE 1 'y'")
132
+
133
+ def test_create_table_pitr_success(self):
134
+ """Test successful table PITR creation"""
135
+ # Mock successful execution
136
+ mock_result = Mock()
137
+ mock_result.rows = [('tab_pitr1', datetime.now(), datetime.now(), 'table', 'acc1', 'db1', 't1', 1, 'y')]
138
+ self.client.execute = Mock(return_value=mock_result)
139
+
140
+ # Test create table PITR
141
+ pitr = self.pitr_manager.create_table_pitr("tab_pitr1", "db1", "t1", 1, "y")
142
+
143
+ # Verify
144
+ self.assertIsInstance(pitr, Pitr)
145
+ self.assertEqual(pitr.name, "tab_pitr1")
146
+ self.assertEqual(pitr.level, "table")
147
+ self.assertEqual(pitr.database_name, "db1")
148
+ self.assertEqual(pitr.table_name, "t1")
149
+ self.assertEqual(pitr.range_value, 1)
150
+ self.assertEqual(pitr.range_unit, "y")
151
+ # Should be called twice: once for CREATE, once for SHOW
152
+ self.assertEqual(self.client.execute.call_count, 2)
153
+ self.client.execute.assert_any_call("CREATE PITR `tab_pitr1` FOR TABLE `db1` `t1` RANGE 1 'y'")
154
+
155
+ def test_get_pitr_success(self):
156
+ """Test successful PITR retrieval"""
157
+ # Mock successful execution
158
+ mock_result = Mock()
159
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
160
+ self.client.execute = Mock(return_value=mock_result)
161
+
162
+ # Test get PITR
163
+ pitr = self.pitr_manager.get("cluster_pitr1")
164
+
165
+ # Verify
166
+ self.assertIsInstance(pitr, Pitr)
167
+ self.assertEqual(pitr.name, "cluster_pitr1")
168
+ self.client.execute.assert_called_with("SHOW PITR WHERE pitr_name = 'cluster_pitr1'")
169
+
170
+ def test_get_pitr_not_found(self):
171
+ """Test PITR not found"""
172
+ # Mock empty result
173
+ mock_result = Mock()
174
+ mock_result.rows = []
175
+ self.client.execute = Mock(return_value=mock_result)
176
+
177
+ # Test get PITR not found
178
+ with self.assertRaises(PitrErrorClass):
179
+ self.pitr_manager.get("nonexistent_pitr")
180
+
181
+ def test_list_pitrs_success(self):
182
+ """Test successful PITR listing"""
183
+ # Mock successful execution
184
+ mock_result = Mock()
185
+ mock_result.rows = [
186
+ ('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd'),
187
+ ('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h'),
188
+ ]
189
+ self.client.execute = Mock(return_value=mock_result)
190
+
191
+ # Test list PITRs
192
+ pitrs = self.pitr_manager.list()
193
+
194
+ # Verify
195
+ self.assertEqual(len(pitrs), 2)
196
+ self.assertIsInstance(pitrs[0], Pitr)
197
+ self.assertIsInstance(pitrs[1], Pitr)
198
+ self.client.execute.assert_called_with("SHOW PITR")
199
+
200
+ def test_list_pitrs_with_filters(self):
201
+ """Test PITR listing with filters"""
202
+ # Mock successful execution
203
+ mock_result = Mock()
204
+ mock_result.rows = [('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h')]
205
+ self.client.execute = Mock(return_value=mock_result)
206
+
207
+ # Test list PITRs with filters
208
+ pitrs = self.pitr_manager.list(level="account", account_name="acc1")
209
+
210
+ # Verify
211
+ self.assertEqual(len(pitrs), 1)
212
+ self.client.execute.assert_called_with("SHOW PITR WHERE pitr_level = 'account' AND account_name = 'acc1'")
213
+
214
+ def test_alter_pitr_success(self):
215
+ """Test successful PITR alteration"""
216
+ # Mock successful execution
217
+ mock_result = Mock()
218
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 2, 'h')]
219
+ self.client.execute = Mock(return_value=mock_result)
220
+
221
+ # Test alter PITR
222
+ pitr = self.pitr_manager.alter("cluster_pitr1", 2, "h")
223
+
224
+ # Verify
225
+ self.assertIsInstance(pitr, Pitr)
226
+ self.assertEqual(pitr.range_value, 2)
227
+ self.assertEqual(pitr.range_unit, "h")
228
+ # Should be called twice: once for ALTER, once for SHOW
229
+ self.assertEqual(self.client.execute.call_count, 2)
230
+ self.client.execute.assert_any_call("ALTER PITR `cluster_pitr1` RANGE 2 'h'")
231
+
232
+ def test_delete_pitr_success(self):
233
+ """Test successful PITR deletion"""
234
+ # Mock successful execution
235
+ mock_result = Mock()
236
+ self.client.execute = Mock(return_value=mock_result)
237
+
238
+ # Test delete PITR
239
+ result = self.pitr_manager.delete("cluster_pitr1")
240
+
241
+ # Verify
242
+ self.assertTrue(result)
243
+ self.client.execute.assert_called_with("DROP PITR `cluster_pitr1`")
244
+
245
+ def test_validate_range_invalid_value(self):
246
+ """Test range validation with invalid value"""
247
+ # Test invalid range value
248
+ with self.assertRaises(PitrErrorClass):
249
+ self.pitr_manager.create_cluster_pitr("test", 0, "d")
250
+
251
+ with self.assertRaises(PitrErrorClass):
252
+ self.pitr_manager.create_cluster_pitr("test", 101, "d")
253
+
254
+ def test_validate_range_invalid_unit(self):
255
+ """Test range validation with invalid unit"""
256
+ # Test invalid range unit
257
+ with self.assertRaises(PitrErrorClass):
258
+ self.pitr_manager.create_cluster_pitr("test", 1, "invalid")
259
+
260
+
261
+ class TestAsyncPitrManager(unittest.IsolatedAsyncioTestCase):
262
+ """Test cases for AsyncPitrManager"""
263
+
264
+ async def asyncSetUp(self):
265
+ """Set up async test fixtures"""
266
+ self.client = AsyncClient()
267
+ self.client._engine = Mock()
268
+ self.client._escape_identifier = lambda x: f"`{x}`"
269
+ self.client._escape_string = lambda x: f"'{x}'"
270
+ # Initialize managers manually for testing
271
+ from matrixone.async_client import AsyncPitrManager
272
+
273
+ self.pitr_manager = AsyncPitrManager(self.client)
274
+
275
+ async def test_async_create_cluster_pitr_success(self):
276
+ """Test successful async cluster PITR creation"""
277
+ # Mock successful execution
278
+ mock_result = Mock()
279
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
280
+ self.client.execute = AsyncMock(return_value=mock_result)
281
+
282
+ # Test create cluster PITR
283
+ pitr = await self.pitr_manager.create_cluster_pitr("cluster_pitr1", 1, "d")
284
+
285
+ # Verify
286
+ self.assertIsInstance(pitr, Pitr)
287
+ self.assertEqual(pitr.name, "cluster_pitr1")
288
+ self.assertEqual(pitr.level, "cluster")
289
+ self.assertEqual(pitr.range_value, 1)
290
+ self.assertEqual(pitr.range_unit, "d")
291
+ # Should be called twice: once for CREATE, once for SHOW
292
+ self.assertEqual(self.client.execute.call_count, 2)
293
+ self.client.execute.assert_any_call("CREATE PITR `cluster_pitr1` FOR CLUSTER RANGE 1 'd'")
294
+
295
+ async def test_async_create_account_pitr_success(self):
296
+ """Test successful async account PITR creation"""
297
+ # Mock successful execution
298
+ mock_result = Mock()
299
+ mock_result.rows = [('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h')]
300
+ self.client.execute = AsyncMock(return_value=mock_result)
301
+
302
+ # Test create account PITR
303
+ pitr = await self.pitr_manager.create_account_pitr("account_pitr1", range_value=2, range_unit="h")
304
+
305
+ # Verify
306
+ self.assertIsInstance(pitr, Pitr)
307
+ self.assertEqual(pitr.name, "account_pitr1")
308
+ self.assertEqual(pitr.level, "account")
309
+ self.assertEqual(pitr.range_value, 2)
310
+ self.assertEqual(pitr.range_unit, "h")
311
+ # Should be called twice: once for CREATE, once for SHOW
312
+ self.assertEqual(self.client.execute.call_count, 2)
313
+ self.client.execute.assert_any_call("CREATE PITR `account_pitr1` FOR ACCOUNT RANGE 2 'h'")
314
+
315
+ async def test_async_create_database_pitr_success(self):
316
+ """Test successful async database PITR creation"""
317
+ # Mock successful execution
318
+ mock_result = Mock()
319
+ mock_result.rows = [('db_pitr1', datetime.now(), datetime.now(), 'database', 'acc1', 'db1', '*', 1, 'y')]
320
+ self.client.execute = AsyncMock(return_value=mock_result)
321
+
322
+ # Test create database PITR
323
+ pitr = await self.pitr_manager.create_database_pitr("db_pitr1", "db1", 1, "y")
324
+
325
+ # Verify
326
+ self.assertIsInstance(pitr, Pitr)
327
+ self.assertEqual(pitr.name, "db_pitr1")
328
+ self.assertEqual(pitr.level, "database")
329
+ self.assertEqual(pitr.database_name, "db1")
330
+ self.assertEqual(pitr.range_value, 1)
331
+ self.assertEqual(pitr.range_unit, "y")
332
+ # Should be called twice: once for CREATE, once for SHOW
333
+ self.assertEqual(self.client.execute.call_count, 2)
334
+ self.client.execute.assert_any_call("CREATE PITR `db_pitr1` FOR DATABASE `db1` RANGE 1 'y'")
335
+
336
+ async def test_async_create_table_pitr_success(self):
337
+ """Test successful async table PITR creation"""
338
+ # Mock successful execution
339
+ mock_result = Mock()
340
+ mock_result.rows = [('tab_pitr1', datetime.now(), datetime.now(), 'table', 'acc1', 'db1', 't1', 1, 'y')]
341
+ self.client.execute = AsyncMock(return_value=mock_result)
342
+
343
+ # Test create table PITR
344
+ pitr = await self.pitr_manager.create_table_pitr("tab_pitr1", "db1", "t1", 1, "y")
345
+
346
+ # Verify
347
+ self.assertIsInstance(pitr, Pitr)
348
+ self.assertEqual(pitr.name, "tab_pitr1")
349
+ self.assertEqual(pitr.level, "table")
350
+ self.assertEqual(pitr.database_name, "db1")
351
+ self.assertEqual(pitr.table_name, "t1")
352
+ self.assertEqual(pitr.range_value, 1)
353
+ self.assertEqual(pitr.range_unit, "y")
354
+ # Should be called twice: once for CREATE, once for SHOW
355
+ self.assertEqual(self.client.execute.call_count, 2)
356
+ self.client.execute.assert_any_call("CREATE PITR `tab_pitr1` FOR TABLE `db1` `t1` RANGE 1 'y'")
357
+
358
+ async def test_async_get_pitr_success(self):
359
+ """Test successful async PITR retrieval"""
360
+ # Mock successful execution
361
+ mock_result = Mock()
362
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
363
+ self.client.execute = AsyncMock(return_value=mock_result)
364
+
365
+ # Test get PITR
366
+ pitr = await self.pitr_manager.get("cluster_pitr1")
367
+
368
+ # Verify
369
+ self.assertIsInstance(pitr, Pitr)
370
+ self.assertEqual(pitr.name, "cluster_pitr1")
371
+ self.client.execute.assert_called_with("SHOW PITR WHERE pitr_name = 'cluster_pitr1'")
372
+
373
+ async def test_async_list_pitrs_success(self):
374
+ """Test successful async PITR listing"""
375
+ # Mock successful execution
376
+ mock_result = Mock()
377
+ mock_result.rows = [
378
+ ('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd'),
379
+ ('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h'),
380
+ ]
381
+ self.client.execute = AsyncMock(return_value=mock_result)
382
+
383
+ # Test list PITRs
384
+ pitrs = await self.pitr_manager.list()
385
+
386
+ # Verify
387
+ self.assertEqual(len(pitrs), 2)
388
+ self.assertIsInstance(pitrs[0], Pitr)
389
+ self.assertIsInstance(pitrs[1], Pitr)
390
+ self.client.execute.assert_called_with("SHOW PITR")
391
+
392
+ async def test_async_alter_pitr_success(self):
393
+ """Test successful async PITR alteration"""
394
+ # Mock successful execution
395
+ mock_result = Mock()
396
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 2, 'h')]
397
+ self.client.execute = AsyncMock(return_value=mock_result)
398
+
399
+ # Test alter PITR
400
+ pitr = await self.pitr_manager.alter("cluster_pitr1", 2, "h")
401
+
402
+ # Verify
403
+ self.assertIsInstance(pitr, Pitr)
404
+ self.assertEqual(pitr.range_value, 2)
405
+ self.assertEqual(pitr.range_unit, "h")
406
+ # Should be called twice: once for ALTER, once for SHOW
407
+ self.assertEqual(self.client.execute.call_count, 2)
408
+ self.client.execute.assert_any_call("ALTER PITR `cluster_pitr1` RANGE 2 'h'")
409
+
410
+ async def test_async_delete_pitr_success(self):
411
+ """Test successful async PITR deletion"""
412
+ # Mock successful execution
413
+ mock_result = Mock()
414
+ self.client.execute = AsyncMock(return_value=mock_result)
415
+
416
+ # Test delete PITR
417
+ result = await self.pitr_manager.delete("cluster_pitr1")
418
+
419
+ # Verify
420
+ self.assertTrue(result)
421
+ self.client.execute.assert_called_with("DROP PITR `cluster_pitr1`")
422
+
423
+
424
+ class TestTransactionPitrManager(unittest.TestCase):
425
+ """Test cases for TransactionPitrManager"""
426
+
427
+ def setUp(self):
428
+ """Set up test fixtures"""
429
+ self.client = Client()
430
+ self.client._engine = Mock()
431
+ self.client._escape_identifier = lambda x: f"`{x}`"
432
+ self.client._escape_string = lambda x: f"'{x}'"
433
+ self.transaction_wrapper = Mock()
434
+ self.transaction_pitr_manager = TransactionPitrManager(self.client, self.transaction_wrapper)
435
+
436
+ def test_transaction_create_cluster_pitr(self):
437
+ """Test create cluster PITR within transaction"""
438
+ # Mock successful execution
439
+ mock_result = Mock()
440
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
441
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
442
+
443
+ # Test create cluster PITR
444
+ pitr = self.transaction_pitr_manager.create_cluster_pitr("cluster_pitr1", 1, "d")
445
+
446
+ # Verify
447
+ self.assertIsInstance(pitr, Pitr)
448
+ self.assertEqual(pitr.name, "cluster_pitr1")
449
+ # Should be called twice: once for CREATE, once for SHOW
450
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
451
+ self.transaction_wrapper.execute.assert_any_call("CREATE PITR `cluster_pitr1` FOR CLUSTER RANGE 1 'd'")
452
+
453
+ def test_transaction_create_account_pitr(self):
454
+ """Test create account PITR within transaction"""
455
+ # Mock successful execution
456
+ mock_result = Mock()
457
+ mock_result.rows = [('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h')]
458
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
459
+
460
+ # Test create account PITR
461
+ pitr = self.transaction_pitr_manager.create_account_pitr("account_pitr1", range_value=2, range_unit="h")
462
+
463
+ # Verify
464
+ self.assertIsInstance(pitr, Pitr)
465
+ self.assertEqual(pitr.name, "account_pitr1")
466
+ # Should be called twice: once for CREATE, once for SHOW
467
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
468
+ self.transaction_wrapper.execute.assert_any_call("CREATE PITR `account_pitr1` FOR ACCOUNT RANGE 2 'h'")
469
+
470
+ def test_transaction_create_database_pitr(self):
471
+ """Test create database PITR within transaction"""
472
+ # Mock successful execution
473
+ mock_result = Mock()
474
+ mock_result.rows = [('db_pitr1', datetime.now(), datetime.now(), 'database', 'acc1', 'db1', '*', 1, 'y')]
475
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
476
+
477
+ # Test create database PITR
478
+ pitr = self.transaction_pitr_manager.create_database_pitr("db_pitr1", "db1", 1, "y")
479
+
480
+ # Verify
481
+ self.assertIsInstance(pitr, Pitr)
482
+ self.assertEqual(pitr.name, "db_pitr1")
483
+ # Should be called twice: once for CREATE, once for SHOW
484
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
485
+ self.transaction_wrapper.execute.assert_any_call("CREATE PITR `db_pitr1` FOR DATABASE `db1` RANGE 1 'y'")
486
+
487
+ def test_transaction_create_table_pitr(self):
488
+ """Test create table PITR within transaction"""
489
+ # Mock successful execution
490
+ mock_result = Mock()
491
+ mock_result.rows = [('tab_pitr1', datetime.now(), datetime.now(), 'table', 'acc1', 'db1', 't1', 1, 'y')]
492
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
493
+
494
+ # Test create table PITR
495
+ pitr = self.transaction_pitr_manager.create_table_pitr("tab_pitr1", "db1", "t1", 1, "y")
496
+
497
+ # Verify
498
+ self.assertIsInstance(pitr, Pitr)
499
+ self.assertEqual(pitr.name, "tab_pitr1")
500
+ # Should be called twice: once for CREATE, once for SHOW
501
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
502
+ self.transaction_wrapper.execute.assert_any_call("CREATE PITR `tab_pitr1` FOR TABLE `db1` TABLE `t1` RANGE 1 'y'")
503
+
504
+ def test_transaction_get_pitr(self):
505
+ """Test get PITR within transaction"""
506
+ # Mock successful execution
507
+ mock_result = Mock()
508
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd')]
509
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
510
+
511
+ # Test get PITR
512
+ pitr = self.transaction_pitr_manager.get("cluster_pitr1")
513
+
514
+ # Verify
515
+ self.assertIsInstance(pitr, Pitr)
516
+ self.assertEqual(pitr.name, "cluster_pitr1")
517
+ self.transaction_wrapper.execute.assert_called_with("SHOW PITR WHERE pitr_name = 'cluster_pitr1'")
518
+
519
+ def test_transaction_list_pitrs(self):
520
+ """Test list PITRs within transaction"""
521
+ # Mock successful execution
522
+ mock_result = Mock()
523
+ mock_result.rows = [
524
+ ('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 1, 'd'),
525
+ ('account_pitr1', datetime.now(), datetime.now(), 'account', 'acc1', '*', '*', 2, 'h'),
526
+ ]
527
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
528
+
529
+ # Test list PITRs
530
+ pitrs = self.transaction_pitr_manager.list()
531
+
532
+ # Verify
533
+ self.assertEqual(len(pitrs), 2)
534
+ self.assertIsInstance(pitrs[0], Pitr)
535
+ self.assertIsInstance(pitrs[1], Pitr)
536
+ self.transaction_wrapper.execute.assert_called_with("SHOW PITR")
537
+
538
+ def test_transaction_alter_pitr(self):
539
+ """Test alter PITR within transaction"""
540
+ # Mock successful execution
541
+ mock_result = Mock()
542
+ mock_result.rows = [('cluster_pitr1', datetime.now(), datetime.now(), 'cluster', '*', '*', '*', 2, 'h')]
543
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
544
+
545
+ # Test alter PITR
546
+ pitr = self.transaction_pitr_manager.alter("cluster_pitr1", 2, "h")
547
+
548
+ # Verify
549
+ self.assertIsInstance(pitr, Pitr)
550
+ self.assertEqual(pitr.range_value, 2)
551
+ self.assertEqual(pitr.range_unit, "h")
552
+ # Should be called twice: once for ALTER, once for SHOW
553
+ self.assertEqual(self.transaction_wrapper.execute.call_count, 2)
554
+ self.transaction_wrapper.execute.assert_any_call("ALTER PITR `cluster_pitr1` RANGE 2 'h'")
555
+
556
+ def test_transaction_delete_pitr(self):
557
+ """Test delete PITR within transaction"""
558
+ # Mock successful execution
559
+ mock_result = Mock()
560
+ self.transaction_wrapper.execute = Mock(return_value=mock_result)
561
+
562
+ # Test delete PITR
563
+ result = self.transaction_pitr_manager.delete("cluster_pitr1")
564
+
565
+ # Verify
566
+ self.assertTrue(result)
567
+ self.transaction_wrapper.execute.assert_called_with("DROP PITR `cluster_pitr1`")
568
+
569
+
570
+ if __name__ == '__main__':
571
+ # Create test suite
572
+ loader = unittest.TestLoader()
573
+ suite = unittest.TestSuite()
574
+
575
+ # Add test cases
576
+ suite.addTests(loader.loadTestsFromTestCase(TestPitrManager))
577
+ suite.addTests(loader.loadTestsFromTestCase(TestAsyncPitrManager))
578
+ suite.addTests(loader.loadTestsFromTestCase(TestTransactionPitrManager))
579
+
580
+ # Run tests
581
+ runner = unittest.TextTestRunner(verbosity=2)
582
+ result = runner.run(suite)
583
+
584
+ # Exit with appropriate code
585
+ sys.exit(0 if result.wasSuccessful() else 1)