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,285 @@
|
|
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
|
+
Online tests for Async Client functionality - tests actual database operations
|
17
|
+
"""
|
18
|
+
|
19
|
+
import pytest
|
20
|
+
import pytest_asyncio
|
21
|
+
import asyncio
|
22
|
+
import os
|
23
|
+
import sys
|
24
|
+
from datetime import datetime
|
25
|
+
|
26
|
+
# Add the matrixone package to the path
|
27
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
28
|
+
|
29
|
+
from matrixone import AsyncClient
|
30
|
+
from matrixone.exceptions import ConnectionError, QueryError
|
31
|
+
from matrixone.sqlalchemy_ext import boolean_match
|
32
|
+
from .test_config import online_config
|
33
|
+
|
34
|
+
|
35
|
+
class TestAsyncClientOnline:
|
36
|
+
"""Online tests for Async Client functionality"""
|
37
|
+
|
38
|
+
@pytest_asyncio.fixture(scope="function")
|
39
|
+
async def test_async_client(self):
|
40
|
+
"""Create and connect AsyncClient for testing"""
|
41
|
+
host, port, user, password, database = online_config.get_connection_params()
|
42
|
+
client = AsyncClient()
|
43
|
+
await client.connect(host, port, user, password, database)
|
44
|
+
try:
|
45
|
+
yield client
|
46
|
+
finally:
|
47
|
+
try:
|
48
|
+
await client.disconnect()
|
49
|
+
except Exception as e:
|
50
|
+
print(f"Warning: Failed to disconnect async client: {e}")
|
51
|
+
|
52
|
+
@pytest_asyncio.fixture(scope="function")
|
53
|
+
async def test_database(self, test_async_client):
|
54
|
+
"""Set up test database and table"""
|
55
|
+
test_db = "test_async_client_db"
|
56
|
+
test_table = "test_async_client_table"
|
57
|
+
|
58
|
+
try:
|
59
|
+
await test_async_client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
60
|
+
await test_async_client.execute(
|
61
|
+
f"""
|
62
|
+
CREATE TABLE IF NOT EXISTS {test_db}.{test_table} (
|
63
|
+
id INT PRIMARY KEY,
|
64
|
+
name VARCHAR(100),
|
65
|
+
value INT,
|
66
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
67
|
+
)
|
68
|
+
"""
|
69
|
+
)
|
70
|
+
|
71
|
+
# Insert test data
|
72
|
+
await test_async_client.execute(f"INSERT INTO {test_db}.{test_table} VALUES (1, 'async_test1', 100, NOW())")
|
73
|
+
await test_async_client.execute(f"INSERT INTO {test_db}.{test_table} VALUES (2, 'async_test2', 200, NOW())")
|
74
|
+
await test_async_client.execute(f"INSERT INTO {test_db}.{test_table} VALUES (3, 'async_test3', 300, NOW())")
|
75
|
+
|
76
|
+
yield test_db, test_table
|
77
|
+
|
78
|
+
finally:
|
79
|
+
# Clean up
|
80
|
+
try:
|
81
|
+
await test_async_client.execute(f"DROP DATABASE IF EXISTS {test_db}")
|
82
|
+
except Exception as e:
|
83
|
+
print(f"Cleanup failed: {e}")
|
84
|
+
|
85
|
+
@pytest.mark.asyncio
|
86
|
+
async def test_basic_async_connection_and_query(self, test_async_client):
|
87
|
+
"""Test basic async connection and query functionality"""
|
88
|
+
# Test simple query
|
89
|
+
result = await test_async_client.execute("SELECT 1 as test_value")
|
90
|
+
rows = result.fetchall()
|
91
|
+
assert len(rows) == 1
|
92
|
+
assert rows[0][0] == 1
|
93
|
+
|
94
|
+
# Test query with parameters
|
95
|
+
result = await test_async_client.execute("SELECT ? as param_value", (42,))
|
96
|
+
rows = result.fetchall()
|
97
|
+
assert len(rows) == 1
|
98
|
+
assert rows[0][0] == 42
|
99
|
+
|
100
|
+
@pytest.mark.asyncio
|
101
|
+
async def test_async_table_operations(self, test_async_client, test_database):
|
102
|
+
"""Test async table operations"""
|
103
|
+
test_db, test_table = test_database
|
104
|
+
|
105
|
+
# Test SELECT
|
106
|
+
result = await test_async_client.execute(f"SELECT * FROM {test_db}.{test_table} ORDER BY id")
|
107
|
+
rows = result.fetchall()
|
108
|
+
assert len(rows) == 3
|
109
|
+
assert rows[0][0] == 1 # id
|
110
|
+
assert rows[0][1] == 'async_test1' # name
|
111
|
+
|
112
|
+
# Test INSERT
|
113
|
+
await test_async_client.execute(f"INSERT INTO {test_db}.{test_table} VALUES (4, 'async_test4', 400, NOW())")
|
114
|
+
|
115
|
+
# Verify INSERT
|
116
|
+
result = await test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
117
|
+
count = result.fetchone()[0]
|
118
|
+
assert count == 4
|
119
|
+
|
120
|
+
# Test UPDATE
|
121
|
+
await test_async_client.execute(f"UPDATE {test_db}.{test_table} SET value = 500 WHERE id = 4")
|
122
|
+
|
123
|
+
# Verify UPDATE
|
124
|
+
result = await test_async_client.execute(f"SELECT value FROM {test_db}.{test_table} WHERE id = 4")
|
125
|
+
value = result.fetchone()[0]
|
126
|
+
assert value == 500
|
127
|
+
|
128
|
+
# Test DELETE
|
129
|
+
await test_async_client.execute(f"DELETE FROM {test_db}.{test_table} WHERE id = 4")
|
130
|
+
|
131
|
+
# Verify DELETE
|
132
|
+
result = await test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
133
|
+
count = result.fetchone()[0]
|
134
|
+
assert count == 3
|
135
|
+
|
136
|
+
@pytest.mark.asyncio
|
137
|
+
async def test_async_transaction_operations(self, test_async_client, test_database):
|
138
|
+
"""Test async transaction operations"""
|
139
|
+
test_db, test_table = test_database
|
140
|
+
|
141
|
+
async with test_async_client.transaction() as tx:
|
142
|
+
# Insert within transaction
|
143
|
+
await tx.execute(f"INSERT INTO {test_db}.{test_table} VALUES (5, 'async_test5', 500, NOW())")
|
144
|
+
|
145
|
+
# Verify within transaction
|
146
|
+
result = await tx.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
147
|
+
count = result.fetchone()[0]
|
148
|
+
assert count == 4
|
149
|
+
|
150
|
+
# Verify transaction commit
|
151
|
+
result = await test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
152
|
+
count = result.fetchone()[0]
|
153
|
+
assert count == 4
|
154
|
+
|
155
|
+
# Test transaction rollback
|
156
|
+
try:
|
157
|
+
async with test_async_client.transaction() as tx:
|
158
|
+
await tx.execute(f"INSERT INTO {test_db}.{test_table} VALUES (6, 'async_test6', 600, NOW())")
|
159
|
+
# Force rollback by raising exception
|
160
|
+
raise Exception("Test rollback")
|
161
|
+
except Exception:
|
162
|
+
pass
|
163
|
+
|
164
|
+
# Verify rollback
|
165
|
+
result = await test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
166
|
+
count = result.fetchone()[0]
|
167
|
+
assert count == 4 # Should still be 4, not 5
|
168
|
+
|
169
|
+
# Clean up
|
170
|
+
await test_async_client.execute(f"DELETE FROM {test_db}.{test_table} WHERE id = 5")
|
171
|
+
|
172
|
+
@pytest.mark.asyncio
|
173
|
+
async def test_async_error_handling(self, test_async_client):
|
174
|
+
"""Test async error handling"""
|
175
|
+
# Test connection error handling
|
176
|
+
with pytest.raises(QueryError):
|
177
|
+
await test_async_client.execute("SELECT * FROM non_existent_table")
|
178
|
+
|
179
|
+
# Test invalid SQL
|
180
|
+
with pytest.raises(QueryError):
|
181
|
+
await test_async_client.execute("INVALID SQL STATEMENT")
|
182
|
+
|
183
|
+
@pytest.mark.asyncio
|
184
|
+
async def test_async_result_set_operations(self, test_async_client, test_database):
|
185
|
+
"""Test async result set operations"""
|
186
|
+
test_db, test_table = test_database
|
187
|
+
|
188
|
+
result = await test_async_client.execute(f"SELECT * FROM {test_db}.{test_table} ORDER BY id")
|
189
|
+
|
190
|
+
# Test fetchone
|
191
|
+
first_row = result.fetchone()
|
192
|
+
assert first_row is not None
|
193
|
+
assert first_row[0] == 1
|
194
|
+
|
195
|
+
# Test fetchmany
|
196
|
+
next_rows = result.fetchmany(2)
|
197
|
+
assert len(next_rows) == 2
|
198
|
+
assert next_rows[0][0] == 2
|
199
|
+
assert next_rows[1][0] == 3
|
200
|
+
|
201
|
+
# Test fetchall (should return remaining rows)
|
202
|
+
remaining_rows = result.fetchall()
|
203
|
+
assert len(remaining_rows) == 0 # Should be empty
|
204
|
+
|
205
|
+
# Test column names
|
206
|
+
result = await test_async_client.execute(f"SELECT id, name, value FROM {test_db}.{test_table} LIMIT 1")
|
207
|
+
columns = result.keys()
|
208
|
+
expected_columns = ['id', 'name', 'value']
|
209
|
+
assert list(columns) == expected_columns
|
210
|
+
|
211
|
+
@pytest.mark.asyncio
|
212
|
+
async def test_async_parameter_binding(self, test_async_client, test_database):
|
213
|
+
"""Test async parameter binding"""
|
214
|
+
test_db, test_table = test_database
|
215
|
+
|
216
|
+
# Test string parameters
|
217
|
+
result = await test_async_client.execute(f"SELECT * FROM {test_db}.{test_table} WHERE name = ?", ('async_test1',))
|
218
|
+
rows = result.fetchall()
|
219
|
+
assert len(rows) == 1
|
220
|
+
assert rows[0][1] == 'async_test1'
|
221
|
+
|
222
|
+
# Test numeric parameters
|
223
|
+
result = await test_async_client.execute(f"SELECT * FROM {test_db}.{test_table} WHERE value = ?", (200,))
|
224
|
+
rows = result.fetchall()
|
225
|
+
assert len(rows) == 1
|
226
|
+
assert rows[0][2] == 200
|
227
|
+
|
228
|
+
# Test multiple parameters
|
229
|
+
result = await test_async_client.execute(
|
230
|
+
f"SELECT * FROM {test_db}.{test_table} WHERE name = ? AND value > ?",
|
231
|
+
('async_test2', 150),
|
232
|
+
)
|
233
|
+
rows = result.fetchall()
|
234
|
+
assert len(rows) == 1
|
235
|
+
assert rows[0][1] == 'async_test2'
|
236
|
+
assert rows[0][2] == 200
|
237
|
+
|
238
|
+
@pytest.mark.asyncio
|
239
|
+
async def test_async_concurrent_operations(self, test_async_client, test_database):
|
240
|
+
"""Test async concurrent operations"""
|
241
|
+
test_db, test_table = test_database
|
242
|
+
|
243
|
+
# Test concurrent queries
|
244
|
+
tasks = [
|
245
|
+
test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}"),
|
246
|
+
test_async_client.execute(f"SELECT MAX(value) FROM {test_db}.{test_table}"),
|
247
|
+
test_async_client.execute(f"SELECT MIN(value) FROM {test_db}.{test_table}"),
|
248
|
+
]
|
249
|
+
|
250
|
+
results = await asyncio.gather(*tasks)
|
251
|
+
|
252
|
+
# All queries should complete successfully
|
253
|
+
assert len(results) == 3
|
254
|
+
assert results[0].fetchone()[0] == 3 # count
|
255
|
+
assert results[1].fetchone()[0] == 300 # max value
|
256
|
+
assert results[2].fetchone()[0] == 100 # min value
|
257
|
+
|
258
|
+
@pytest.mark.asyncio
|
259
|
+
async def test_async_connection_pooling(self, test_async_client, test_database):
|
260
|
+
"""Test async connection pooling and reuse"""
|
261
|
+
test_db, test_table = test_database
|
262
|
+
|
263
|
+
# Execute multiple queries to test connection reuse
|
264
|
+
for i in range(5):
|
265
|
+
result = await test_async_client.execute("SELECT 1 as test")
|
266
|
+
rows = result.fetchall()
|
267
|
+
assert len(rows) == 1
|
268
|
+
assert rows[0][0] == 1
|
269
|
+
|
270
|
+
# Test concurrent operations
|
271
|
+
tasks = []
|
272
|
+
for i in range(3):
|
273
|
+
task = test_async_client.execute(f"SELECT COUNT(*) FROM {test_db}.{test_table}")
|
274
|
+
tasks.append(task)
|
275
|
+
|
276
|
+
results = await asyncio.gather(*tasks)
|
277
|
+
|
278
|
+
# All should return the same count
|
279
|
+
for result in results:
|
280
|
+
count = result.fetchone()[0]
|
281
|
+
assert count == 3
|
282
|
+
|
283
|
+
|
284
|
+
if __name__ == '__main__':
|
285
|
+
pytest.main([__file__])
|
@@ -0,0 +1,293 @@
|
|
1
|
+
"""
|
2
|
+
Online tests for async model-based insert operations
|
3
|
+
"""
|
4
|
+
|
5
|
+
import pytest
|
6
|
+
import pytest_asyncio
|
7
|
+
import os
|
8
|
+
import sys
|
9
|
+
import asyncio
|
10
|
+
from datetime import datetime
|
11
|
+
|
12
|
+
# Add the matrixone package to the path
|
13
|
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
|
14
|
+
|
15
|
+
from matrixone import AsyncClient
|
16
|
+
from matrixone.orm import declarative_base
|
17
|
+
from matrixone.exceptions import QueryError
|
18
|
+
from sqlalchemy import Column, Integer, String, Text, DECIMAL, DateTime
|
19
|
+
from .test_config import online_config
|
20
|
+
|
21
|
+
Base = declarative_base()
|
22
|
+
|
23
|
+
|
24
|
+
class AsyncUser(Base):
|
25
|
+
__tablename__ = 'test_async_insert_users'
|
26
|
+
id = Column(Integer, primary_key=True)
|
27
|
+
name = Column(String(50))
|
28
|
+
email = Column(String(100))
|
29
|
+
age = Column(Integer)
|
30
|
+
created_at = Column(DateTime)
|
31
|
+
|
32
|
+
|
33
|
+
class AsyncProduct(Base):
|
34
|
+
__tablename__ = 'test_async_insert_products'
|
35
|
+
id = Column(Integer, primary_key=True)
|
36
|
+
name = Column(String(100))
|
37
|
+
description = Column(Text)
|
38
|
+
price = Column(DECIMAL(10, 2))
|
39
|
+
category = Column(String(50))
|
40
|
+
in_stock = Column(Integer)
|
41
|
+
|
42
|
+
|
43
|
+
class AsyncOrder(Base):
|
44
|
+
__tablename__ = 'test_async_insert_orders'
|
45
|
+
id = Column(Integer, primary_key=True)
|
46
|
+
user_id = Column(Integer)
|
47
|
+
product_id = Column(Integer)
|
48
|
+
quantity = Column(Integer)
|
49
|
+
total_amount = Column(DECIMAL(10, 2))
|
50
|
+
order_date = Column(DateTime)
|
51
|
+
|
52
|
+
|
53
|
+
class TestAsyncModelInsert:
|
54
|
+
"""Test async model-based insert operations"""
|
55
|
+
|
56
|
+
async def _setup_test_environment(self):
|
57
|
+
"""Set up test environment for each test"""
|
58
|
+
host, port, user, password, database = online_config.get_connection_params()
|
59
|
+
client = AsyncClient()
|
60
|
+
await client.connect(host=host, port=port, user=user, password=password, database=database)
|
61
|
+
|
62
|
+
test_db = "test_async_model_insert_db"
|
63
|
+
await client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
|
64
|
+
await client.execute(f"USE {test_db}")
|
65
|
+
|
66
|
+
# Create test tables using models
|
67
|
+
await client.create_table(AsyncUser)
|
68
|
+
await client.create_table(AsyncProduct)
|
69
|
+
await client.create_table(AsyncOrder)
|
70
|
+
|
71
|
+
return client, test_db
|
72
|
+
|
73
|
+
async def _cleanup_test_environment(self, client, test_db):
|
74
|
+
"""Clean up test environment"""
|
75
|
+
try:
|
76
|
+
await client.execute(f"DROP DATABASE IF EXISTS {test_db}")
|
77
|
+
await client.disconnect()
|
78
|
+
except Exception as e:
|
79
|
+
print(f"Cleanup failed: {e}")
|
80
|
+
|
81
|
+
@pytest.mark.asyncio
|
82
|
+
async def test_async_single_insert_with_model(self):
|
83
|
+
"""Test inserting a single record using model class with async client"""
|
84
|
+
client, test_db = await self._setup_test_environment()
|
85
|
+
try:
|
86
|
+
# Insert a single user using model class
|
87
|
+
result = await client.insert(
|
88
|
+
AsyncUser,
|
89
|
+
{'id': 1, 'name': 'John Doe', 'email': 'john@example.com', 'age': 30, 'created_at': datetime.now()},
|
90
|
+
)
|
91
|
+
|
92
|
+
assert result.affected_rows == 1
|
93
|
+
|
94
|
+
# Verify the data was inserted correctly
|
95
|
+
users = await client.query(AsyncUser).all()
|
96
|
+
assert len(users) == 1
|
97
|
+
|
98
|
+
user = users[0]
|
99
|
+
assert user.name == 'John Doe'
|
100
|
+
assert user.email == 'john@example.com'
|
101
|
+
assert user.age == 30
|
102
|
+
|
103
|
+
finally:
|
104
|
+
await self._cleanup_test_environment(client, test_db)
|
105
|
+
|
106
|
+
@pytest.mark.asyncio
|
107
|
+
async def test_async_batch_insert_with_model(self):
|
108
|
+
"""Test batch inserting multiple records using model class with async client"""
|
109
|
+
client, test_db = await self._setup_test_environment()
|
110
|
+
try:
|
111
|
+
# Insert multiple products using model class
|
112
|
+
products_data = [
|
113
|
+
{
|
114
|
+
'id': 1,
|
115
|
+
'name': 'Laptop',
|
116
|
+
'description': 'High-performance laptop',
|
117
|
+
'price': 999.99,
|
118
|
+
'category': 'Electronics',
|
119
|
+
'in_stock': 10,
|
120
|
+
},
|
121
|
+
{
|
122
|
+
'id': 2,
|
123
|
+
'name': 'Book',
|
124
|
+
'description': 'Programming guide',
|
125
|
+
'price': 29.99,
|
126
|
+
'category': 'Education',
|
127
|
+
'in_stock': 50,
|
128
|
+
},
|
129
|
+
{
|
130
|
+
'id': 3,
|
131
|
+
'name': 'Phone',
|
132
|
+
'description': 'Smartphone with AI features',
|
133
|
+
'price': 699.99,
|
134
|
+
'category': 'Electronics',
|
135
|
+
'in_stock': 25,
|
136
|
+
},
|
137
|
+
]
|
138
|
+
|
139
|
+
result = await client.batch_insert(AsyncProduct, products_data)
|
140
|
+
assert result.affected_rows == 3
|
141
|
+
|
142
|
+
# Verify the data was inserted correctly
|
143
|
+
products = await client.query(AsyncProduct).all()
|
144
|
+
assert len(products) == 3
|
145
|
+
|
146
|
+
# Check specific products
|
147
|
+
laptop = await client.query(AsyncProduct).filter_by(name='Laptop').first()
|
148
|
+
assert laptop is not None
|
149
|
+
assert float(laptop.price) == 999.99
|
150
|
+
assert laptop.category == 'Electronics'
|
151
|
+
assert laptop.in_stock == 10
|
152
|
+
|
153
|
+
finally:
|
154
|
+
await self._cleanup_test_environment(client, test_db)
|
155
|
+
|
156
|
+
@pytest.mark.asyncio
|
157
|
+
async def test_async_insert_with_null_values(self):
|
158
|
+
"""Test inserting records with NULL values using model class with async client"""
|
159
|
+
client, test_db = await self._setup_test_environment()
|
160
|
+
try:
|
161
|
+
# Insert a user with some NULL values
|
162
|
+
result = await client.insert(
|
163
|
+
AsyncUser,
|
164
|
+
{'id': 2, 'name': 'Jane Smith', 'email': None, 'age': 25, 'created_at': None}, # NULL value # NULL value
|
165
|
+
)
|
166
|
+
|
167
|
+
assert result.affected_rows == 1
|
168
|
+
|
169
|
+
# Verify the data was inserted correctly
|
170
|
+
user = await client.query(AsyncUser).filter_by(id=2).first()
|
171
|
+
assert user is not None
|
172
|
+
assert user.name == 'Jane Smith'
|
173
|
+
assert user.email is None
|
174
|
+
assert user.age == 25
|
175
|
+
assert user.created_at is None
|
176
|
+
|
177
|
+
finally:
|
178
|
+
await self._cleanup_test_environment(client, test_db)
|
179
|
+
|
180
|
+
@pytest.mark.asyncio
|
181
|
+
async def test_async_insert_with_different_data_types(self):
|
182
|
+
"""Test inserting records with different data types using model class with async client"""
|
183
|
+
client, test_db = await self._setup_test_environment()
|
184
|
+
try:
|
185
|
+
# Insert an order with various data types
|
186
|
+
result = await client.insert(
|
187
|
+
AsyncOrder,
|
188
|
+
{
|
189
|
+
'id': 1,
|
190
|
+
'user_id': 1,
|
191
|
+
'product_id': 1,
|
192
|
+
'quantity': 2,
|
193
|
+
'total_amount': 1999.98,
|
194
|
+
'order_date': datetime(2024, 1, 15, 10, 30, 0),
|
195
|
+
},
|
196
|
+
)
|
197
|
+
|
198
|
+
assert result.affected_rows == 1
|
199
|
+
|
200
|
+
# Verify the data was inserted correctly
|
201
|
+
order = await client.query(AsyncOrder).filter_by(id=1).first()
|
202
|
+
assert order is not None
|
203
|
+
assert order.user_id == 1
|
204
|
+
assert order.product_id == 1
|
205
|
+
assert order.quantity == 2
|
206
|
+
assert float(order.total_amount) == 1999.98
|
207
|
+
assert order.order_date == datetime(2024, 1, 15, 10, 30, 0)
|
208
|
+
|
209
|
+
finally:
|
210
|
+
await self._cleanup_test_environment(client, test_db)
|
211
|
+
|
212
|
+
@pytest.mark.asyncio
|
213
|
+
async def test_async_insert_error_handling(self):
|
214
|
+
"""Test error handling when inserting invalid data using model class with async client"""
|
215
|
+
client, test_db = await self._setup_test_environment()
|
216
|
+
try:
|
217
|
+
# First insert a user
|
218
|
+
await client.insert(AsyncUser, {'id': 1, 'name': 'John Doe', 'email': 'john@example.com', 'age': 30})
|
219
|
+
|
220
|
+
# Try to insert a user with duplicate primary key
|
221
|
+
with pytest.raises(QueryError):
|
222
|
+
await client.insert(
|
223
|
+
AsyncUser,
|
224
|
+
{
|
225
|
+
'id': 1, # Duplicate primary key
|
226
|
+
'name': 'Duplicate User',
|
227
|
+
'email': 'duplicate@example.com',
|
228
|
+
'age': 35,
|
229
|
+
},
|
230
|
+
)
|
231
|
+
|
232
|
+
finally:
|
233
|
+
await self._cleanup_test_environment(client, test_db)
|
234
|
+
|
235
|
+
@pytest.mark.asyncio
|
236
|
+
async def test_async_insert_vs_string_table_name(self):
|
237
|
+
"""Test that both model class and string table name work the same way with async client"""
|
238
|
+
client, test_db = await self._setup_test_environment()
|
239
|
+
try:
|
240
|
+
# Insert using model class
|
241
|
+
result1 = await client.insert(
|
242
|
+
AsyncUser, {'id': 3, 'name': 'Model User', 'email': 'model@example.com', 'age': 28}
|
243
|
+
)
|
244
|
+
|
245
|
+
# Insert using string table name
|
246
|
+
result2 = await client.insert(
|
247
|
+
'test_async_insert_users', {'id': 4, 'name': 'String User', 'email': 'string@example.com', 'age': 32}
|
248
|
+
)
|
249
|
+
|
250
|
+
assert result1.affected_rows == 1
|
251
|
+
assert result2.affected_rows == 1
|
252
|
+
|
253
|
+
# Verify both records were inserted
|
254
|
+
users = await client.query(AsyncUser).all()
|
255
|
+
assert len(users) == 2
|
256
|
+
|
257
|
+
# Check specific users
|
258
|
+
model_user = await client.query(AsyncUser).filter_by(name='Model User').first()
|
259
|
+
string_user = await client.query(AsyncUser).filter_by(name='String User').first()
|
260
|
+
|
261
|
+
assert model_user is not None
|
262
|
+
assert string_user is not None
|
263
|
+
assert model_user.email == 'model@example.com'
|
264
|
+
assert string_user.email == 'string@example.com'
|
265
|
+
|
266
|
+
finally:
|
267
|
+
await self._cleanup_test_environment(client, test_db)
|
268
|
+
|
269
|
+
@pytest.mark.asyncio
|
270
|
+
async def test_async_transaction_insert_with_model(self):
|
271
|
+
"""Test inserting records within a transaction using model class with async client"""
|
272
|
+
client, test_db = await self._setup_test_environment()
|
273
|
+
try:
|
274
|
+
async with client.transaction() as tx:
|
275
|
+
# Insert a user within transaction
|
276
|
+
result = await tx.insert(
|
277
|
+
AsyncUser, {'id': 100, 'name': 'Transaction User', 'email': 'tx@example.com', 'age': 45}
|
278
|
+
)
|
279
|
+
|
280
|
+
assert result.affected_rows == 1
|
281
|
+
|
282
|
+
# Verify within transaction
|
283
|
+
user = await tx.query(AsyncUser).filter_by(id=100).first()
|
284
|
+
assert user is not None
|
285
|
+
assert user.name == 'Transaction User'
|
286
|
+
|
287
|
+
# Verify after commit
|
288
|
+
user_after_commit = await client.query(AsyncUser).filter_by(id=100).first()
|
289
|
+
assert user_after_commit is not None
|
290
|
+
assert user_after_commit.name == 'Transaction User'
|
291
|
+
|
292
|
+
finally:
|
293
|
+
await self._cleanup_test_environment(client, test_db)
|