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,703 @@
|
|
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 MatrixOne Account Management functionality
|
17
|
+
"""
|
18
|
+
|
19
|
+
import unittest
|
20
|
+
import pytest
|
21
|
+
from unittest.mock import Mock, patch, AsyncMock
|
22
|
+
from datetime import datetime
|
23
|
+
from matrixone.account import AccountManager, Account, User, TransactionAccountManager
|
24
|
+
from matrixone.async_client import AsyncAccountManager, AsyncTransactionAccountManager
|
25
|
+
from matrixone.exceptions import AccountError
|
26
|
+
|
27
|
+
|
28
|
+
class TestAccount(unittest.TestCase):
|
29
|
+
"""Test Account data class"""
|
30
|
+
|
31
|
+
def test_account_creation(self):
|
32
|
+
"""Test Account object creation"""
|
33
|
+
account = Account(
|
34
|
+
name="test_account",
|
35
|
+
admin_name="admin_user",
|
36
|
+
created_time=datetime.now(),
|
37
|
+
status="OPEN",
|
38
|
+
comment="Test account",
|
39
|
+
)
|
40
|
+
|
41
|
+
self.assertEqual(account.name, "test_account")
|
42
|
+
self.assertEqual(account.admin_name, "admin_user")
|
43
|
+
self.assertEqual(account.status, "OPEN")
|
44
|
+
self.assertEqual(account.comment, "Test account")
|
45
|
+
self.assertIsNotNone(account.created_time)
|
46
|
+
|
47
|
+
def test_account_str_representation(self):
|
48
|
+
"""Test Account string representation"""
|
49
|
+
account = Account("test_account", "admin_user", status="OPEN")
|
50
|
+
str_repr = str(account)
|
51
|
+
self.assertIn("test_account", str_repr)
|
52
|
+
self.assertIn("admin_user", str_repr)
|
53
|
+
self.assertIn("OPEN", str_repr)
|
54
|
+
|
55
|
+
|
56
|
+
class TestUser(unittest.TestCase):
|
57
|
+
"""Test User data class"""
|
58
|
+
|
59
|
+
def test_user_creation(self):
|
60
|
+
"""Test User object creation"""
|
61
|
+
user = User(
|
62
|
+
name="test_user",
|
63
|
+
host="%",
|
64
|
+
account="sys",
|
65
|
+
created_time=datetime.now(),
|
66
|
+
status="OPEN",
|
67
|
+
comment="Test user",
|
68
|
+
)
|
69
|
+
|
70
|
+
self.assertEqual(user.name, "test_user")
|
71
|
+
self.assertEqual(user.host, "%")
|
72
|
+
self.assertEqual(user.account, "sys")
|
73
|
+
self.assertEqual(user.status, "OPEN")
|
74
|
+
self.assertEqual(user.comment, "Test user")
|
75
|
+
self.assertIsNotNone(user.created_time)
|
76
|
+
|
77
|
+
def test_user_str_representation(self):
|
78
|
+
"""Test User string representation"""
|
79
|
+
user = User("test_user", "localhost", "test_account", status="OPEN")
|
80
|
+
str_repr = str(user)
|
81
|
+
self.assertIn("test_user", str_repr)
|
82
|
+
self.assertIn("localhost", str_repr)
|
83
|
+
self.assertIn("test_account", str_repr)
|
84
|
+
self.assertIn("OPEN", str_repr)
|
85
|
+
|
86
|
+
|
87
|
+
class TestAccountManager(unittest.TestCase):
|
88
|
+
"""Test AccountManager functionality"""
|
89
|
+
|
90
|
+
def setUp(self):
|
91
|
+
"""Set up test fixtures"""
|
92
|
+
self.client = Mock()
|
93
|
+
self.client._escape_identifier = lambda x: f"`{x}`"
|
94
|
+
self.client._escape_string = lambda x: f"'{x}'"
|
95
|
+
self.account_manager = AccountManager(self.client)
|
96
|
+
|
97
|
+
def test_create_account_success(self):
|
98
|
+
"""Test successful account creation"""
|
99
|
+
# Mock successful execution - first call for CREATE, second call for get_account
|
100
|
+
mock_result = Mock()
|
101
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Test account', None, None)]
|
102
|
+
self.client.execute = Mock(return_value=mock_result)
|
103
|
+
|
104
|
+
# Test account creation
|
105
|
+
account = self.account_manager.create_account(
|
106
|
+
account_name="test_account",
|
107
|
+
admin_name="admin_user",
|
108
|
+
password="password123",
|
109
|
+
comment="Test account",
|
110
|
+
)
|
111
|
+
|
112
|
+
# Verify
|
113
|
+
self.assertIsInstance(account, Account)
|
114
|
+
self.assertEqual(account.name, "test_account")
|
115
|
+
self.assertEqual(account.admin_name, "admin_user")
|
116
|
+
self.assertEqual(account.status, "OPEN")
|
117
|
+
self.assertEqual(account.comment, "Test account")
|
118
|
+
|
119
|
+
# Verify SQL was called correctly - check the first call (CREATE ACCOUNT)
|
120
|
+
self.assertEqual(self.client.execute.call_count, 2) # CREATE + get_account
|
121
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
122
|
+
self.assertIn("CREATE ACCOUNT", first_call_args)
|
123
|
+
self.assertIn("admin_user", first_call_args)
|
124
|
+
self.assertIn("password123", first_call_args)
|
125
|
+
self.assertIn("Test account", first_call_args)
|
126
|
+
|
127
|
+
def test_create_account_failure(self):
|
128
|
+
"""Test account creation failure"""
|
129
|
+
# Mock execution failure
|
130
|
+
self.client.execute = Mock(side_effect=Exception("Database error"))
|
131
|
+
|
132
|
+
# Test account creation failure
|
133
|
+
with self.assertRaises(AccountError) as context:
|
134
|
+
self.account_manager.create_account(account_name="test_account", admin_name="admin_user", password="password123")
|
135
|
+
|
136
|
+
self.assertIn("Failed to create account 'test_account'", str(context.exception))
|
137
|
+
|
138
|
+
def test_drop_account_success(self):
|
139
|
+
"""Test successful account deletion"""
|
140
|
+
# Mock successful execution
|
141
|
+
self.client.execute = Mock(return_value=Mock())
|
142
|
+
|
143
|
+
# Test account deletion
|
144
|
+
self.account_manager.drop_account("test_account")
|
145
|
+
|
146
|
+
# Verify SQL was called correctly
|
147
|
+
self.client.execute.assert_called_with("DROP ACCOUNT `test_account`")
|
148
|
+
|
149
|
+
def test_drop_account_failure(self):
|
150
|
+
"""Test account deletion failure"""
|
151
|
+
# Mock execution failure
|
152
|
+
self.client.execute = Mock(side_effect=Exception("Database error"))
|
153
|
+
|
154
|
+
# Test account deletion failure
|
155
|
+
with self.assertRaises(AccountError) as context:
|
156
|
+
self.account_manager.drop_account("test_account")
|
157
|
+
|
158
|
+
self.assertIn("Failed to drop account 'test_account'", str(context.exception))
|
159
|
+
|
160
|
+
def test_alter_account_success(self):
|
161
|
+
"""Test successful account alteration"""
|
162
|
+
# Mock successful execution and get_account
|
163
|
+
mock_result = Mock()
|
164
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Updated comment', None, None)]
|
165
|
+
self.client.execute = Mock(return_value=mock_result)
|
166
|
+
|
167
|
+
# Test account alteration
|
168
|
+
account = self.account_manager.alter_account(account_name="test_account", comment="Updated comment")
|
169
|
+
|
170
|
+
# Verify
|
171
|
+
self.assertIsInstance(account, Account)
|
172
|
+
self.assertEqual(account.comment, "Updated comment")
|
173
|
+
|
174
|
+
# Verify SQL was called correctly - check the first call (ALTER ACCOUNT)
|
175
|
+
self.assertEqual(self.client.execute.call_count, 2) # ALTER + get_account
|
176
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
177
|
+
self.assertIn("ALTER ACCOUNT", first_call_args)
|
178
|
+
self.assertIn("Updated comment", first_call_args)
|
179
|
+
|
180
|
+
def test_alter_account_suspend(self):
|
181
|
+
"""Test account suspension"""
|
182
|
+
# Mock successful execution and get_account
|
183
|
+
mock_result = Mock()
|
184
|
+
mock_result.rows = [
|
185
|
+
(
|
186
|
+
'test_account',
|
187
|
+
'admin_user',
|
188
|
+
datetime.now(),
|
189
|
+
'SUSPENDED',
|
190
|
+
'Test account',
|
191
|
+
datetime.now(),
|
192
|
+
'Security violation',
|
193
|
+
)
|
194
|
+
]
|
195
|
+
self.client.execute = Mock(return_value=mock_result)
|
196
|
+
|
197
|
+
# Test account suspension
|
198
|
+
account = self.account_manager.alter_account(
|
199
|
+
account_name="test_account", suspend=True, suspend_reason="Security violation"
|
200
|
+
)
|
201
|
+
|
202
|
+
# Verify
|
203
|
+
self.assertEqual(account.status, "SUSPENDED")
|
204
|
+
self.assertEqual(account.suspended_reason, "Security violation")
|
205
|
+
|
206
|
+
# Verify SQL was called correctly - check the first call (ALTER ACCOUNT)
|
207
|
+
self.assertEqual(self.client.execute.call_count, 2) # ALTER + get_account
|
208
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
209
|
+
self.assertIn("SUSPEND COMMENT", first_call_args)
|
210
|
+
self.assertIn("Security violation", first_call_args)
|
211
|
+
|
212
|
+
def test_get_account_success(self):
|
213
|
+
"""Test successful account retrieval"""
|
214
|
+
# Mock successful execution
|
215
|
+
mock_result = Mock()
|
216
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Test account', None, None)]
|
217
|
+
self.client.execute = Mock(return_value=mock_result)
|
218
|
+
|
219
|
+
# Test account retrieval
|
220
|
+
account = self.account_manager.get_account("test_account")
|
221
|
+
|
222
|
+
# Verify
|
223
|
+
self.assertIsInstance(account, Account)
|
224
|
+
self.assertEqual(account.name, "test_account")
|
225
|
+
self.assertEqual(account.admin_name, "admin_user")
|
226
|
+
|
227
|
+
# Verify SQL was called correctly
|
228
|
+
self.client.execute.assert_called_with("SHOW ACCOUNTS")
|
229
|
+
|
230
|
+
def test_get_account_not_found(self):
|
231
|
+
"""Test account not found"""
|
232
|
+
# Mock empty result
|
233
|
+
mock_result = Mock()
|
234
|
+
mock_result.rows = []
|
235
|
+
self.client.execute = Mock(return_value=mock_result)
|
236
|
+
|
237
|
+
# Test account not found
|
238
|
+
with self.assertRaises(AccountError) as context:
|
239
|
+
self.account_manager.get_account("nonexistent_account")
|
240
|
+
|
241
|
+
self.assertIn("Account 'nonexistent_account' not found", str(context.exception))
|
242
|
+
|
243
|
+
def test_list_accounts_success(self):
|
244
|
+
"""Test successful account listing"""
|
245
|
+
# Mock successful execution
|
246
|
+
mock_result = Mock()
|
247
|
+
mock_result.rows = [
|
248
|
+
('account1', 'admin1', datetime.now(), 'OPEN', 'Account 1', None, None),
|
249
|
+
(
|
250
|
+
'account2',
|
251
|
+
'admin2',
|
252
|
+
datetime.now(),
|
253
|
+
'SUSPENDED',
|
254
|
+
'Account 2',
|
255
|
+
datetime.now(),
|
256
|
+
'Security issue',
|
257
|
+
),
|
258
|
+
]
|
259
|
+
self.client.execute = Mock(return_value=mock_result)
|
260
|
+
|
261
|
+
# Test account listing
|
262
|
+
accounts = self.account_manager.list_accounts()
|
263
|
+
|
264
|
+
# Verify
|
265
|
+
self.assertEqual(len(accounts), 2)
|
266
|
+
self.assertIsInstance(accounts[0], Account)
|
267
|
+
self.assertIsInstance(accounts[1], Account)
|
268
|
+
self.assertEqual(accounts[0].name, "account1")
|
269
|
+
self.assertEqual(accounts[1].name, "account2")
|
270
|
+
self.assertEqual(accounts[1].status, "SUSPENDED")
|
271
|
+
|
272
|
+
def test_create_user_success(self):
|
273
|
+
"""Test successful user creation"""
|
274
|
+
# Mock successful execution - first call for CREATE, second call for get_user
|
275
|
+
mock_result = Mock()
|
276
|
+
mock_result.rows = [
|
277
|
+
(
|
278
|
+
'test_user',
|
279
|
+
'localhost',
|
280
|
+
'test_account',
|
281
|
+
datetime.now(),
|
282
|
+
'OPEN',
|
283
|
+
'Test user',
|
284
|
+
None,
|
285
|
+
None,
|
286
|
+
)
|
287
|
+
]
|
288
|
+
self.client.execute = Mock(return_value=mock_result)
|
289
|
+
|
290
|
+
# Test user creation
|
291
|
+
user = self.account_manager.create_user(user_name="test_user", password="password123", comment="Test user")
|
292
|
+
|
293
|
+
# Verify
|
294
|
+
self.assertIsInstance(user, User)
|
295
|
+
self.assertEqual(user.name, "test_user")
|
296
|
+
self.assertEqual(user.host, "%")
|
297
|
+
self.assertEqual(user.account, "sys")
|
298
|
+
self.assertEqual(user.comment, "Test user")
|
299
|
+
|
300
|
+
# Verify SQL was called correctly - check the first call (CREATE USER)
|
301
|
+
self.assertEqual(self.client.execute.call_count, 1) # CREATE only
|
302
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
303
|
+
self.assertIn("CREATE USER", first_call_args)
|
304
|
+
self.assertIn("test_user", first_call_args)
|
305
|
+
self.assertIn("password123", first_call_args)
|
306
|
+
|
307
|
+
def test_drop_user_success(self):
|
308
|
+
"""Test successful user deletion"""
|
309
|
+
# Mock successful execution
|
310
|
+
self.client.execute = Mock(return_value=Mock())
|
311
|
+
|
312
|
+
# Test user deletion
|
313
|
+
self.account_manager.drop_user("test_user")
|
314
|
+
|
315
|
+
# Verify SQL was called correctly
|
316
|
+
call_args = self.client.execute.call_args[0][0]
|
317
|
+
self.assertIn("DROP USER", call_args)
|
318
|
+
self.assertIn("test_user", call_args)
|
319
|
+
|
320
|
+
def test_alter_user_success(self):
|
321
|
+
"""Test successful user alteration"""
|
322
|
+
# Mock successful execution - first call for ALTER, second call for get_user
|
323
|
+
mock_result = Mock()
|
324
|
+
mock_result.rows = [
|
325
|
+
(
|
326
|
+
'test_user',
|
327
|
+
'localhost',
|
328
|
+
'test_account',
|
329
|
+
datetime.now(),
|
330
|
+
'OPEN',
|
331
|
+
'Updated comment',
|
332
|
+
None,
|
333
|
+
None,
|
334
|
+
)
|
335
|
+
]
|
336
|
+
self.client.execute = Mock(return_value=mock_result)
|
337
|
+
|
338
|
+
# Test user alteration (password change instead of comment)
|
339
|
+
user = self.account_manager.alter_user(user_name="test_user", password="new_password")
|
340
|
+
|
341
|
+
# Verify
|
342
|
+
self.assertIsInstance(user, User)
|
343
|
+
self.assertEqual(user.name, "test_user")
|
344
|
+
|
345
|
+
# Verify SQL was called correctly - check the first call (ALTER USER)
|
346
|
+
self.assertEqual(self.client.execute.call_count, 1) # ALTER only
|
347
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
348
|
+
self.assertIn("ALTER USER", first_call_args)
|
349
|
+
self.assertIn("test_user", first_call_args)
|
350
|
+
self.assertIn("new_password", first_call_args)
|
351
|
+
|
352
|
+
def test_alter_user_lock(self):
|
353
|
+
"""Test user locking"""
|
354
|
+
# Mock successful execution - first call for ALTER, second call for get_user
|
355
|
+
mock_result = Mock()
|
356
|
+
mock_result.rows = [
|
357
|
+
(
|
358
|
+
'test_user',
|
359
|
+
'localhost',
|
360
|
+
'test_account',
|
361
|
+
datetime.now(),
|
362
|
+
'LOCKED',
|
363
|
+
'Test user',
|
364
|
+
datetime.now(),
|
365
|
+
'Security violation',
|
366
|
+
)
|
367
|
+
]
|
368
|
+
self.client.execute = Mock(return_value=mock_result)
|
369
|
+
|
370
|
+
# Test user locking
|
371
|
+
user = self.account_manager.alter_user(user_name="test_user", lock=True, lock_reason="Security violation")
|
372
|
+
|
373
|
+
# Verify
|
374
|
+
self.assertEqual(user.status, "LOCKED")
|
375
|
+
self.assertEqual(user.locked_reason, "Security violation")
|
376
|
+
|
377
|
+
# Verify SQL was called correctly - check the first call (ALTER USER)
|
378
|
+
self.assertEqual(self.client.execute.call_count, 1) # ALTER only
|
379
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
380
|
+
self.assertIn("ALTER USER", first_call_args)
|
381
|
+
self.assertIn("LOCK", first_call_args)
|
382
|
+
|
383
|
+
def test_get_user_success(self):
|
384
|
+
"""Test successful user retrieval"""
|
385
|
+
# Mock successful execution
|
386
|
+
mock_result = Mock()
|
387
|
+
mock_result.rows = [
|
388
|
+
(
|
389
|
+
'test_user',
|
390
|
+
'localhost',
|
391
|
+
'test_account',
|
392
|
+
datetime.now(),
|
393
|
+
'OPEN',
|
394
|
+
'Test user',
|
395
|
+
None,
|
396
|
+
None,
|
397
|
+
)
|
398
|
+
]
|
399
|
+
self.client.execute = Mock(return_value=mock_result)
|
400
|
+
|
401
|
+
# Test user retrieval (using list_users instead of get_user)
|
402
|
+
users = self.account_manager.list_users()
|
403
|
+
user = users[0] if users else None
|
404
|
+
|
405
|
+
# Verify
|
406
|
+
self.assertIsInstance(user, User)
|
407
|
+
self.assertEqual(user.name, "test_user")
|
408
|
+
self.assertEqual(user.host, "%")
|
409
|
+
self.assertEqual(user.account, "sys")
|
410
|
+
|
411
|
+
def test_list_users_success(self):
|
412
|
+
"""Test successful user listing"""
|
413
|
+
# Mock successful execution (list_users only returns current user)
|
414
|
+
mock_result = Mock()
|
415
|
+
mock_result.rows = [
|
416
|
+
(
|
417
|
+
'current_user',
|
418
|
+
'%',
|
419
|
+
'current_account',
|
420
|
+
datetime.now(),
|
421
|
+
'OPEN',
|
422
|
+
'Current user',
|
423
|
+
None,
|
424
|
+
None,
|
425
|
+
)
|
426
|
+
]
|
427
|
+
self.client.execute = Mock(return_value=mock_result)
|
428
|
+
|
429
|
+
# Test user listing
|
430
|
+
users = self.account_manager.list_users()
|
431
|
+
|
432
|
+
# Verify
|
433
|
+
self.assertEqual(len(users), 1)
|
434
|
+
self.assertIsInstance(users[0], User)
|
435
|
+
self.assertEqual(users[0].name, "current_user")
|
436
|
+
|
437
|
+
|
438
|
+
class TestTransactionAccountManager(unittest.TestCase):
|
439
|
+
"""Test TransactionAccountManager functionality"""
|
440
|
+
|
441
|
+
def setUp(self):
|
442
|
+
"""Set up test fixtures"""
|
443
|
+
self.client = Mock()
|
444
|
+
self.client._escape_identifier = lambda x: f"`{x}`"
|
445
|
+
self.client._escape_string = lambda x: f"'{x}'"
|
446
|
+
self.transaction = Mock()
|
447
|
+
self.transaction.client = self.client
|
448
|
+
self.transaction.execute = Mock()
|
449
|
+
self.transaction_account_manager = TransactionAccountManager(self.transaction)
|
450
|
+
|
451
|
+
def test_create_account_in_transaction(self):
|
452
|
+
"""Test account creation within transaction"""
|
453
|
+
# Mock successful execution - first call for CREATE, second call for get_account
|
454
|
+
mock_result = Mock()
|
455
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Test account', None, None)]
|
456
|
+
self.transaction.execute = Mock(return_value=mock_result)
|
457
|
+
self.client.execute = Mock(return_value=mock_result)
|
458
|
+
|
459
|
+
# Test account creation in transaction
|
460
|
+
account = self.transaction_account_manager.create_account(
|
461
|
+
account_name="test_account",
|
462
|
+
admin_name="admin_user",
|
463
|
+
password="password123",
|
464
|
+
comment="Test account",
|
465
|
+
)
|
466
|
+
|
467
|
+
# Verify
|
468
|
+
self.assertIsInstance(account, Account)
|
469
|
+
self.assertEqual(account.name, "test_account")
|
470
|
+
|
471
|
+
# Verify transaction.execute was called - only for CREATE ACCOUNT
|
472
|
+
self.assertEqual(self.transaction.execute.call_count, 1) # CREATE only
|
473
|
+
self.assertEqual(self.client.execute.call_count, 1) # get_account
|
474
|
+
first_call_args = self.transaction.execute.call_args_list[0][0][0]
|
475
|
+
self.assertIn("CREATE ACCOUNT", first_call_args)
|
476
|
+
|
477
|
+
|
478
|
+
class TestAsyncAccountManager(unittest.IsolatedAsyncioTestCase):
|
479
|
+
"""Test AsyncAccountManager functionality"""
|
480
|
+
|
481
|
+
def setUp(self):
|
482
|
+
"""Set up test fixtures"""
|
483
|
+
self.client = AsyncMock()
|
484
|
+
self.client._escape_identifier = lambda x: f"`{x}`"
|
485
|
+
self.client._escape_string = lambda x: f"'{x}'"
|
486
|
+
self.async_account_manager = AsyncAccountManager(self.client)
|
487
|
+
|
488
|
+
async def test_async_create_account_success(self):
|
489
|
+
"""Test successful async account creation"""
|
490
|
+
# Mock successful execution
|
491
|
+
mock_result = AsyncMock()
|
492
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Test account', None, None)]
|
493
|
+
self.client.execute = AsyncMock(return_value=mock_result)
|
494
|
+
|
495
|
+
# Test async account creation
|
496
|
+
account = await self.async_account_manager.create_account(
|
497
|
+
account_name="test_account",
|
498
|
+
admin_name="admin_user",
|
499
|
+
password="password123",
|
500
|
+
comment="Test account",
|
501
|
+
)
|
502
|
+
|
503
|
+
# Verify
|
504
|
+
self.assertIsInstance(account, Account)
|
505
|
+
self.assertEqual(account.name, "test_account")
|
506
|
+
self.assertEqual(account.admin_name, "admin_user")
|
507
|
+
self.assertEqual(account.status, "OPEN")
|
508
|
+
self.assertEqual(account.comment, "Test account")
|
509
|
+
|
510
|
+
# Verify SQL was called correctly - check the first call (CREATE ACCOUNT)
|
511
|
+
self.assertEqual(self.client.execute.call_count, 2) # CREATE + get_account
|
512
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
513
|
+
self.assertIn("CREATE ACCOUNT", first_call_args)
|
514
|
+
self.assertIn("admin_user", first_call_args)
|
515
|
+
self.assertIn("password123", first_call_args)
|
516
|
+
self.assertIn("Test account", first_call_args)
|
517
|
+
|
518
|
+
async def test_async_create_account_failure(self):
|
519
|
+
"""Test async account creation failure"""
|
520
|
+
# Mock execution failure
|
521
|
+
self.client.execute = AsyncMock(side_effect=Exception("Database error"))
|
522
|
+
|
523
|
+
# Test async account creation failure
|
524
|
+
with self.assertRaises(AccountError) as context:
|
525
|
+
await self.async_account_manager.create_account(
|
526
|
+
account_name="test_account", admin_name="admin_user", password="password123"
|
527
|
+
)
|
528
|
+
|
529
|
+
self.assertIn("Failed to create account 'test_account'", str(context.exception))
|
530
|
+
|
531
|
+
async def test_async_drop_account_success(self):
|
532
|
+
"""Test successful async account deletion"""
|
533
|
+
# Mock successful execution
|
534
|
+
self.client.execute = AsyncMock(return_value=AsyncMock())
|
535
|
+
|
536
|
+
# Test async account deletion
|
537
|
+
await self.async_account_manager.drop_account("test_account")
|
538
|
+
|
539
|
+
# Verify SQL was called correctly
|
540
|
+
self.client.execute.assert_called_with("DROP ACCOUNT `test_account`")
|
541
|
+
|
542
|
+
async def test_async_list_accounts_success(self):
|
543
|
+
"""Test successful async account listing"""
|
544
|
+
# Mock successful execution
|
545
|
+
mock_result = AsyncMock()
|
546
|
+
mock_result.rows = [
|
547
|
+
('account1', 'admin1', datetime.now(), 'OPEN', 'Account 1', None, None),
|
548
|
+
(
|
549
|
+
'account2',
|
550
|
+
'admin2',
|
551
|
+
datetime.now(),
|
552
|
+
'SUSPENDED',
|
553
|
+
'Account 2',
|
554
|
+
datetime.now(),
|
555
|
+
'Security issue',
|
556
|
+
),
|
557
|
+
]
|
558
|
+
self.client.execute = AsyncMock(return_value=mock_result)
|
559
|
+
|
560
|
+
# Test async account listing
|
561
|
+
accounts = await self.async_account_manager.list_accounts()
|
562
|
+
|
563
|
+
# Verify
|
564
|
+
self.assertEqual(len(accounts), 2)
|
565
|
+
self.assertIsInstance(accounts[0], Account)
|
566
|
+
self.assertIsInstance(accounts[1], Account)
|
567
|
+
self.assertEqual(accounts[0].name, "account1")
|
568
|
+
self.assertEqual(accounts[1].name, "account2")
|
569
|
+
self.assertEqual(accounts[1].status, "SUSPENDED")
|
570
|
+
|
571
|
+
async def test_async_create_user_success(self):
|
572
|
+
"""Test successful async user creation"""
|
573
|
+
# Mock successful execution
|
574
|
+
mock_result = AsyncMock()
|
575
|
+
mock_result.rows = [
|
576
|
+
(
|
577
|
+
'test_user',
|
578
|
+
'localhost',
|
579
|
+
'test_account',
|
580
|
+
datetime.now(),
|
581
|
+
'OPEN',
|
582
|
+
'Test user',
|
583
|
+
None,
|
584
|
+
None,
|
585
|
+
)
|
586
|
+
]
|
587
|
+
self.client.execute = AsyncMock(return_value=mock_result)
|
588
|
+
|
589
|
+
# Test async user creation
|
590
|
+
user = await self.async_account_manager.create_user(
|
591
|
+
user_name="test_user", password="password123", comment="Test user"
|
592
|
+
)
|
593
|
+
|
594
|
+
# Verify
|
595
|
+
self.assertIsInstance(user, User)
|
596
|
+
self.assertEqual(user.name, "test_user")
|
597
|
+
self.assertEqual(user.host, "%")
|
598
|
+
self.assertEqual(user.account, "sys")
|
599
|
+
self.assertEqual(user.comment, "Test user")
|
600
|
+
|
601
|
+
# Verify SQL was called correctly - check the first call (CREATE USER)
|
602
|
+
self.assertEqual(self.client.execute.call_count, 1) # CREATE only
|
603
|
+
first_call_args = self.client.execute.call_args_list[0][0][0]
|
604
|
+
self.assertIn("CREATE USER", first_call_args)
|
605
|
+
self.assertIn("test_user", first_call_args)
|
606
|
+
self.assertIn("password123", first_call_args)
|
607
|
+
|
608
|
+
async def test_async_list_users_success(self):
|
609
|
+
"""Test successful async user listing"""
|
610
|
+
# Mock successful execution
|
611
|
+
mock_result = AsyncMock()
|
612
|
+
mock_result.rows = [
|
613
|
+
('user1', 'localhost', 'account1', datetime.now(), 'OPEN', 'User 1', None, None),
|
614
|
+
(
|
615
|
+
'user2',
|
616
|
+
'localhost',
|
617
|
+
'account1',
|
618
|
+
datetime.now(),
|
619
|
+
'LOCKED',
|
620
|
+
'User 2',
|
621
|
+
datetime.now(),
|
622
|
+
'Security issue',
|
623
|
+
),
|
624
|
+
]
|
625
|
+
self.client.execute = AsyncMock(return_value=mock_result)
|
626
|
+
|
627
|
+
# Test async user listing
|
628
|
+
users = await self.async_account_manager.list_users()
|
629
|
+
|
630
|
+
# Verify
|
631
|
+
self.assertEqual(len(users), 2)
|
632
|
+
self.assertIsInstance(users[0], User)
|
633
|
+
self.assertIsInstance(users[1], User)
|
634
|
+
self.assertEqual(users[0].name, "user1")
|
635
|
+
self.assertEqual(users[1].name, "user2")
|
636
|
+
self.assertEqual(users[1].status, "LOCKED")
|
637
|
+
|
638
|
+
|
639
|
+
class TestAsyncTransactionAccountManager(unittest.IsolatedAsyncioTestCase):
|
640
|
+
"""Test AsyncTransactionAccountManager functionality"""
|
641
|
+
|
642
|
+
def setUp(self):
|
643
|
+
"""Set up test fixtures"""
|
644
|
+
self.client = AsyncMock()
|
645
|
+
self.client._escape_identifier = lambda x: f"`{x}`"
|
646
|
+
self.client._escape_string = lambda x: f"'{x}'"
|
647
|
+
self.transaction = AsyncMock()
|
648
|
+
self.transaction.client = self.client
|
649
|
+
self.transaction.execute = AsyncMock()
|
650
|
+
self.async_transaction_account_manager = AsyncTransactionAccountManager(self.transaction)
|
651
|
+
|
652
|
+
async def test_async_transaction_create_account(self):
|
653
|
+
"""Test account creation within async transaction"""
|
654
|
+
# Mock successful execution
|
655
|
+
mock_result = AsyncMock()
|
656
|
+
mock_result.rows = [('test_account', 'admin_user', datetime.now(), 'OPEN', 'Test account', None, None)]
|
657
|
+
self.transaction.execute = AsyncMock(return_value=mock_result)
|
658
|
+
|
659
|
+
# Test account creation in async transaction
|
660
|
+
account = await self.async_transaction_account_manager.create_account(
|
661
|
+
account_name="test_account",
|
662
|
+
admin_name="admin_user",
|
663
|
+
password="password123",
|
664
|
+
comment="Test account",
|
665
|
+
)
|
666
|
+
|
667
|
+
# Verify
|
668
|
+
self.assertIsInstance(account, Account)
|
669
|
+
self.assertEqual(account.name, "test_account")
|
670
|
+
|
671
|
+
# Verify transaction.execute was called - check the first call (CREATE ACCOUNT)
|
672
|
+
self.assertEqual(self.transaction.execute.call_count, 2) # CREATE + get_account
|
673
|
+
first_call_args = self.transaction.execute.call_args_list[0][0][0]
|
674
|
+
self.assertIn("CREATE ACCOUNT", first_call_args)
|
675
|
+
|
676
|
+
|
677
|
+
if __name__ == '__main__':
|
678
|
+
# Run async tests
|
679
|
+
import asyncio
|
680
|
+
|
681
|
+
async def run_async_tests():
|
682
|
+
"""Run async test methods"""
|
683
|
+
test_instance = TestAsyncAccountManager()
|
684
|
+
test_instance.setUp()
|
685
|
+
|
686
|
+
await test_instance.test_async_create_account_success()
|
687
|
+
await test_instance.test_async_create_account_failure()
|
688
|
+
await test_instance.test_async_drop_account_success()
|
689
|
+
await test_instance.test_async_list_accounts_success()
|
690
|
+
await test_instance.test_async_create_user_success()
|
691
|
+
await test_instance.test_async_list_users_success()
|
692
|
+
|
693
|
+
test_instance = TestAsyncTransactionAccountManager()
|
694
|
+
test_instance.setUp()
|
695
|
+
await test_instance.test_async_transaction_create_account()
|
696
|
+
|
697
|
+
print("✅ All async tests passed!")
|
698
|
+
|
699
|
+
# Run sync tests
|
700
|
+
unittest.main(argv=[''], exit=False, verbosity=2)
|
701
|
+
|
702
|
+
# Run async tests
|
703
|
+
asyncio.run(run_async_tests())
|