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,425 @@
|
|
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 SQLAlchemy integration
|
17
|
+
|
18
|
+
These tests are inspired by example_06_sqlalchemy_integration.py
|
19
|
+
"""
|
20
|
+
|
21
|
+
import pytest
|
22
|
+
from sqlalchemy import create_engine, Column, Integer, String, DateTime, Text, ForeignKey, text
|
23
|
+
from sqlalchemy.orm import sessionmaker, relationship, declarative_base
|
24
|
+
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
|
25
|
+
from sqlalchemy.pool import QueuePool
|
26
|
+
from matrixone import Client, AsyncClient
|
27
|
+
from matrixone.logger import create_default_logger
|
28
|
+
|
29
|
+
# SQLAlchemy setup - will be created per test to avoid conflicts
|
30
|
+
|
31
|
+
|
32
|
+
@pytest.mark.online
|
33
|
+
class TestSQLAlchemyIntegration:
|
34
|
+
"""Test SQLAlchemy integration functionality"""
|
35
|
+
|
36
|
+
def test_basic_sqlalchemy_operations(self, test_client):
|
37
|
+
"""Test basic SQLAlchemy operations"""
|
38
|
+
# Create SQLAlchemy engine using MatrixOne client
|
39
|
+
engine = test_client.get_sqlalchemy_engine()
|
40
|
+
|
41
|
+
# Create independent Base and models for this test
|
42
|
+
Base = declarative_base()
|
43
|
+
|
44
|
+
class User(Base):
|
45
|
+
__tablename__ = 'test_users_basic'
|
46
|
+
|
47
|
+
id = Column(Integer, primary_key=True)
|
48
|
+
username = Column(String(50), unique=True, nullable=False)
|
49
|
+
email = Column(String(100), unique=True, nullable=False)
|
50
|
+
created_at = Column(DateTime)
|
51
|
+
|
52
|
+
# Relationship
|
53
|
+
posts = relationship("Post", back_populates="author")
|
54
|
+
|
55
|
+
class Post(Base):
|
56
|
+
__tablename__ = 'test_posts_basic'
|
57
|
+
|
58
|
+
id = Column(Integer, primary_key=True)
|
59
|
+
title = Column(String(200), nullable=False)
|
60
|
+
content = Column(Text)
|
61
|
+
author_id = Column(Integer, ForeignKey('test_users_basic.id'))
|
62
|
+
created_at = Column(DateTime)
|
63
|
+
|
64
|
+
# Relationship
|
65
|
+
author = relationship("User", back_populates="posts")
|
66
|
+
|
67
|
+
# Drop tables if exist and create new ones
|
68
|
+
with engine.begin() as conn:
|
69
|
+
conn.execute(text('DROP TABLE IF EXISTS test_posts_basic'))
|
70
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_basic'))
|
71
|
+
|
72
|
+
# Create tables
|
73
|
+
Base.metadata.create_all(engine)
|
74
|
+
|
75
|
+
# Create session
|
76
|
+
Session = sessionmaker(bind=engine)
|
77
|
+
session = Session()
|
78
|
+
|
79
|
+
try:
|
80
|
+
# Test basic CRUD operations
|
81
|
+
# Create user
|
82
|
+
user = User(username="testuser", email="test@example.com")
|
83
|
+
session.add(user)
|
84
|
+
session.commit()
|
85
|
+
|
86
|
+
# Query user
|
87
|
+
found_user = session.query(User).filter_by(username="testuser").first()
|
88
|
+
assert found_user is not None
|
89
|
+
assert found_user.username == "testuser"
|
90
|
+
assert found_user.email == "test@example.com"
|
91
|
+
|
92
|
+
# Create post
|
93
|
+
post = Post(title="Test Post", content="This is a test post", author_id=found_user.id)
|
94
|
+
session.add(post)
|
95
|
+
session.commit()
|
96
|
+
|
97
|
+
# Query post with relationship
|
98
|
+
found_post = session.query(Post).filter_by(title="Test Post").first()
|
99
|
+
assert found_post is not None
|
100
|
+
assert found_post.title == "Test Post"
|
101
|
+
assert found_post.author.username == "testuser"
|
102
|
+
|
103
|
+
# Update user
|
104
|
+
found_user.email = "updated@example.com"
|
105
|
+
session.commit()
|
106
|
+
|
107
|
+
# Verify update
|
108
|
+
updated_user = session.query(User).filter_by(username="testuser").first()
|
109
|
+
assert updated_user.email == "updated@example.com"
|
110
|
+
|
111
|
+
# Delete post
|
112
|
+
session.delete(found_post)
|
113
|
+
session.commit()
|
114
|
+
|
115
|
+
# Verify deletion
|
116
|
+
deleted_post = session.query(Post).filter_by(title="Test Post").first()
|
117
|
+
assert deleted_post is None
|
118
|
+
|
119
|
+
finally:
|
120
|
+
session.close()
|
121
|
+
# Cleanup
|
122
|
+
Base.metadata.drop_all(engine)
|
123
|
+
|
124
|
+
def test_sqlalchemy_with_transactions(self, test_client):
|
125
|
+
"""Test SQLAlchemy with transactions"""
|
126
|
+
engine = test_client.get_sqlalchemy_engine()
|
127
|
+
|
128
|
+
# Create independent Base and models for this test
|
129
|
+
Base = declarative_base()
|
130
|
+
|
131
|
+
class User(Base):
|
132
|
+
__tablename__ = 'test_users_transactions'
|
133
|
+
|
134
|
+
id = Column(Integer, primary_key=True)
|
135
|
+
username = Column(String(50), unique=True, nullable=False)
|
136
|
+
email = Column(String(100), unique=True, nullable=False)
|
137
|
+
created_at = Column(DateTime)
|
138
|
+
|
139
|
+
# Relationship
|
140
|
+
posts = relationship("Post", back_populates="author")
|
141
|
+
|
142
|
+
class Post(Base):
|
143
|
+
__tablename__ = 'test_posts_transactions'
|
144
|
+
|
145
|
+
id = Column(Integer, primary_key=True)
|
146
|
+
title = Column(String(200), nullable=False)
|
147
|
+
content = Column(Text)
|
148
|
+
author_id = Column(Integer, ForeignKey('test_users_transactions.id'))
|
149
|
+
created_at = Column(DateTime)
|
150
|
+
|
151
|
+
# Relationship
|
152
|
+
author = relationship("User", back_populates="posts")
|
153
|
+
|
154
|
+
# Drop tables if exist and create new ones
|
155
|
+
with engine.begin() as conn:
|
156
|
+
conn.execute(text('DROP TABLE IF EXISTS test_posts_transactions'))
|
157
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_transactions'))
|
158
|
+
|
159
|
+
Base.metadata.create_all(engine)
|
160
|
+
|
161
|
+
Session = sessionmaker(bind=engine)
|
162
|
+
session = Session()
|
163
|
+
|
164
|
+
try:
|
165
|
+
# Test transaction rollback
|
166
|
+
user = User(username="transaction_user", email="transaction@example.com")
|
167
|
+
session.add(user)
|
168
|
+
session.flush() # Flush to get ID
|
169
|
+
|
170
|
+
post = Post(title="Transaction Post", content="This will be rolled back", author_id=user.id)
|
171
|
+
session.add(post)
|
172
|
+
|
173
|
+
# Rollback transaction
|
174
|
+
session.rollback()
|
175
|
+
|
176
|
+
# Verify rollback
|
177
|
+
found_user = session.query(User).filter_by(username="transaction_user").first()
|
178
|
+
assert found_user is None
|
179
|
+
|
180
|
+
# Test successful transaction
|
181
|
+
user = User(username="success_user", email="success@example.com")
|
182
|
+
session.add(user)
|
183
|
+
session.commit()
|
184
|
+
|
185
|
+
# Verify success
|
186
|
+
found_user = session.query(User).filter_by(username="success_user").first()
|
187
|
+
assert found_user is not None
|
188
|
+
|
189
|
+
finally:
|
190
|
+
session.close()
|
191
|
+
Base.metadata.drop_all(engine)
|
192
|
+
|
193
|
+
def test_sqlalchemy_raw_sql(self, test_client):
|
194
|
+
"""Test SQLAlchemy with raw SQL"""
|
195
|
+
engine = test_client.get_sqlalchemy_engine()
|
196
|
+
|
197
|
+
# Create independent Base and models for this test
|
198
|
+
Base = declarative_base()
|
199
|
+
|
200
|
+
class User(Base):
|
201
|
+
__tablename__ = 'test_users_raw_sql'
|
202
|
+
|
203
|
+
id = Column(Integer, primary_key=True)
|
204
|
+
username = Column(String(50), unique=True, nullable=False)
|
205
|
+
email = Column(String(100), unique=True, nullable=False)
|
206
|
+
created_at = Column(DateTime)
|
207
|
+
|
208
|
+
# Drop table if exists and create new one
|
209
|
+
with engine.begin() as conn:
|
210
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_raw_sql'))
|
211
|
+
|
212
|
+
Base.metadata.create_all(engine)
|
213
|
+
|
214
|
+
Session = sessionmaker(bind=engine)
|
215
|
+
session = Session()
|
216
|
+
|
217
|
+
try:
|
218
|
+
# Test raw SQL execution
|
219
|
+
result = session.execute(text("SELECT 1 as test_value, USER() as user_info"))
|
220
|
+
row = result.fetchone()
|
221
|
+
assert row[0] == 1 # test_value
|
222
|
+
assert row[1] is not None # user_info
|
223
|
+
|
224
|
+
# Test raw SQL with parameters
|
225
|
+
result = session.execute(text("SELECT :value as param_value"), {"value": 42})
|
226
|
+
row = result.fetchone()
|
227
|
+
assert row[0] == 42
|
228
|
+
|
229
|
+
# Test raw SQL for table operations
|
230
|
+
session.execute(
|
231
|
+
text("INSERT INTO test_users_raw_sql (username, email) VALUES (:username, :email)"),
|
232
|
+
{"username": "raw_user", "email": "raw@example.com"},
|
233
|
+
)
|
234
|
+
session.commit()
|
235
|
+
|
236
|
+
# Verify insertion
|
237
|
+
found_user = session.query(User).filter_by(username="raw_user").first()
|
238
|
+
assert found_user is not None
|
239
|
+
|
240
|
+
finally:
|
241
|
+
session.close()
|
242
|
+
Base.metadata.drop_all(engine)
|
243
|
+
|
244
|
+
def test_sqlalchemy_connection_pooling(self, test_client):
|
245
|
+
"""Test SQLAlchemy connection pooling"""
|
246
|
+
# Get the default engine (which already has connection pooling configured)
|
247
|
+
engine = test_client.get_sqlalchemy_engine()
|
248
|
+
|
249
|
+
# Create independent Base and models for this test
|
250
|
+
Base = declarative_base()
|
251
|
+
|
252
|
+
class User(Base):
|
253
|
+
__tablename__ = 'test_users_pooling'
|
254
|
+
|
255
|
+
id = Column(Integer, primary_key=True)
|
256
|
+
username = Column(String(50), unique=True, nullable=False)
|
257
|
+
email = Column(String(100), unique=True, nullable=False)
|
258
|
+
created_at = Column(DateTime)
|
259
|
+
|
260
|
+
# Drop table if exists and create new one
|
261
|
+
with engine.begin() as conn:
|
262
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_pooling'))
|
263
|
+
|
264
|
+
Base.metadata.create_all(engine)
|
265
|
+
|
266
|
+
Session = sessionmaker(bind=engine)
|
267
|
+
|
268
|
+
# Test multiple sessions
|
269
|
+
sessions = []
|
270
|
+
try:
|
271
|
+
for i in range(3):
|
272
|
+
session = Session()
|
273
|
+
sessions.append(session)
|
274
|
+
|
275
|
+
user = User(username=f"pool_user_{i}", email=f"pool{i}@example.com")
|
276
|
+
session.add(user)
|
277
|
+
session.commit()
|
278
|
+
|
279
|
+
# Verify user was created
|
280
|
+
found_user = session.query(User).filter_by(username=f"pool_user_{i}").first()
|
281
|
+
assert found_user is not None
|
282
|
+
|
283
|
+
finally:
|
284
|
+
for session in sessions:
|
285
|
+
session.close()
|
286
|
+
Base.metadata.drop_all(engine)
|
287
|
+
|
288
|
+
@pytest.mark.asyncio
|
289
|
+
async def test_async_sqlalchemy_operations(self, test_async_client):
|
290
|
+
"""Test async SQLAlchemy operations using models"""
|
291
|
+
# Create independent Base and models for this test
|
292
|
+
Base = declarative_base()
|
293
|
+
|
294
|
+
class AsyncUser(Base):
|
295
|
+
__tablename__ = 'async_users'
|
296
|
+
|
297
|
+
id = Column(Integer, primary_key=True, autoincrement=True)
|
298
|
+
username = Column(String(50), nullable=False)
|
299
|
+
email = Column(String(100), nullable=False)
|
300
|
+
|
301
|
+
try:
|
302
|
+
# Create table using model
|
303
|
+
await test_async_client.create_table(AsyncUser)
|
304
|
+
|
305
|
+
# Clear any existing data using model
|
306
|
+
await test_async_client.query(AsyncUser).delete()
|
307
|
+
|
308
|
+
# Insert user using model
|
309
|
+
await test_async_client.batch_insert(AsyncUser, [{"username": "async_user", "email": "async@example.com"}])
|
310
|
+
|
311
|
+
# Query user using model
|
312
|
+
users = await test_async_client.query(AsyncUser).filter(AsyncUser.username == "async_user").all()
|
313
|
+
assert len(users) > 0
|
314
|
+
assert users[0].username == "async_user"
|
315
|
+
|
316
|
+
# Update user using model
|
317
|
+
await test_async_client.query(AsyncUser).filter(AsyncUser.username == "async_user").update(
|
318
|
+
email="updated_async@example.com"
|
319
|
+
).execute()
|
320
|
+
|
321
|
+
# Verify update using model
|
322
|
+
updated_user = await test_async_client.query(AsyncUser).filter(AsyncUser.username == "async_user").first()
|
323
|
+
assert updated_user.email == "updated_async@example.com"
|
324
|
+
|
325
|
+
finally:
|
326
|
+
# Cleanup using model
|
327
|
+
await test_async_client.drop_table(AsyncUser)
|
328
|
+
|
329
|
+
def test_sqlalchemy_with_matrixone_features(self, test_client):
|
330
|
+
"""Test SQLAlchemy with MatrixOne-specific features"""
|
331
|
+
engine = test_client.get_sqlalchemy_engine()
|
332
|
+
|
333
|
+
# Create independent Base and models for this test
|
334
|
+
Base = declarative_base()
|
335
|
+
|
336
|
+
class User(Base):
|
337
|
+
__tablename__ = 'test_users_matrixone'
|
338
|
+
|
339
|
+
id = Column(Integer, primary_key=True)
|
340
|
+
username = Column(String(50), unique=True, nullable=False)
|
341
|
+
email = Column(String(100), unique=True, nullable=False)
|
342
|
+
created_at = Column(DateTime)
|
343
|
+
|
344
|
+
# Drop table if exists and create new one
|
345
|
+
with engine.begin() as conn:
|
346
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_matrixone'))
|
347
|
+
|
348
|
+
Base.metadata.create_all(engine)
|
349
|
+
|
350
|
+
Session = sessionmaker(bind=engine)
|
351
|
+
session = Session()
|
352
|
+
|
353
|
+
try:
|
354
|
+
# Test MatrixOne-specific SQL through SQLAlchemy
|
355
|
+
result = session.execute(text("SHOW DATABASES"))
|
356
|
+
databases = result.fetchall()
|
357
|
+
assert len(databases) > 0
|
358
|
+
|
359
|
+
# Test MatrixOne version info
|
360
|
+
result = session.execute(text("SELECT VERSION()"))
|
361
|
+
version = result.fetchone()
|
362
|
+
assert version is not None
|
363
|
+
assert "MatrixOne" in version[0] or "mysql" in version[0].lower()
|
364
|
+
|
365
|
+
# Test MatrixOne user info
|
366
|
+
result = session.execute(text("SELECT USER()"))
|
367
|
+
user_info = result.fetchone()
|
368
|
+
assert user_info is not None
|
369
|
+
|
370
|
+
finally:
|
371
|
+
session.close()
|
372
|
+
Base.metadata.drop_all(engine)
|
373
|
+
|
374
|
+
def test_sqlalchemy_error_handling(self, test_client):
|
375
|
+
"""Test SQLAlchemy error handling"""
|
376
|
+
engine = test_client.get_sqlalchemy_engine()
|
377
|
+
|
378
|
+
# Create independent Base and models for this test
|
379
|
+
Base = declarative_base()
|
380
|
+
|
381
|
+
class User(Base):
|
382
|
+
__tablename__ = 'test_users_errors'
|
383
|
+
|
384
|
+
id = Column(Integer, primary_key=True)
|
385
|
+
username = Column(String(50), unique=True, nullable=False)
|
386
|
+
email = Column(String(100), unique=True, nullable=False)
|
387
|
+
created_at = Column(DateTime)
|
388
|
+
|
389
|
+
# Drop table if exists and create new one
|
390
|
+
with engine.begin() as conn:
|
391
|
+
conn.execute(text('DROP TABLE IF EXISTS test_users_errors'))
|
392
|
+
|
393
|
+
Base.metadata.create_all(engine)
|
394
|
+
|
395
|
+
Session = sessionmaker(bind=engine)
|
396
|
+
session = Session()
|
397
|
+
|
398
|
+
try:
|
399
|
+
# Test duplicate key error
|
400
|
+
user1 = User(username="duplicate_user", email="duplicate1@example.com")
|
401
|
+
session.add(user1)
|
402
|
+
session.commit()
|
403
|
+
|
404
|
+
user2 = User(username="duplicate_user", email="duplicate2@example.com")
|
405
|
+
session.add(user2)
|
406
|
+
|
407
|
+
try:
|
408
|
+
session.commit()
|
409
|
+
assert False, "Should have failed with duplicate key"
|
410
|
+
except Exception as e:
|
411
|
+
# Expected to fail
|
412
|
+
session.rollback()
|
413
|
+
assert "duplicate" in str(e).lower() or "unique" in str(e).lower()
|
414
|
+
|
415
|
+
# Test invalid SQL
|
416
|
+
try:
|
417
|
+
session.execute(text("INVALID SQL STATEMENT"))
|
418
|
+
assert False, "Should have failed with invalid SQL"
|
419
|
+
except Exception as e:
|
420
|
+
# Expected to fail
|
421
|
+
assert "syntax" in str(e).lower() or "error" in str(e).lower()
|
422
|
+
|
423
|
+
finally:
|
424
|
+
session.close()
|
425
|
+
Base.metadata.drop_all(engine)
|