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,255 @@
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 model-based insert operations
17
+ """
18
+
19
+ import pytest
20
+ import os
21
+ import sys
22
+ from datetime import datetime
23
+
24
+ # Add the matrixone package to the path
25
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
26
+
27
+ from matrixone import Client
28
+ from sqlalchemy import Column, Integer, String, Text, DateTime, DECIMAL
29
+ from matrixone.orm import declarative_base
30
+ from matrixone.exceptions import QueryError
31
+ from .test_config import online_config
32
+
33
+ Base = declarative_base()
34
+
35
+
36
+ class User(Base):
37
+ """Test user model for insert operations"""
38
+
39
+ __tablename__ = "test_insert_users"
40
+
41
+ id = Column(Integer, primary_key=True)
42
+ name = Column(String(100))
43
+ email = Column(String(100))
44
+ age = Column(Integer)
45
+ created_at = Column(DateTime)
46
+
47
+
48
+ class Product(Base):
49
+ """Test product model for insert operations"""
50
+
51
+ __tablename__ = "test_insert_products"
52
+
53
+ id = Column(Integer, primary_key=True)
54
+ name = Column(String(200))
55
+ description = Column(Text)
56
+ price = Column(DECIMAL(10, 2))
57
+ category = Column(String(50))
58
+ in_stock = Column(Integer)
59
+
60
+
61
+ class Order(Base):
62
+ """Test order model for insert operations"""
63
+
64
+ __tablename__ = "test_insert_orders"
65
+
66
+ id = Column(Integer, primary_key=True)
67
+ user_id = Column(Integer)
68
+ product_id = Column(Integer)
69
+ quantity = Column(Integer)
70
+ total_amount = Column(DECIMAL(10, 2))
71
+ order_date = Column(DateTime)
72
+
73
+
74
+ class TestModelInsert:
75
+ """Test model-based insert operations"""
76
+
77
+ @pytest.fixture(scope="class")
78
+ def test_client(self):
79
+ """Create and connect Client for testing"""
80
+ host, port, user, password, database = online_config.get_connection_params()
81
+ client = Client()
82
+ client.connect(host=host, port=port, user=user, password=password, database=database)
83
+ try:
84
+ yield client
85
+ finally:
86
+ try:
87
+ client.disconnect()
88
+ except:
89
+ pass
90
+
91
+ @pytest.fixture(scope="class")
92
+ def test_database(self, test_client):
93
+ """Set up test database and tables"""
94
+ test_db = "test_model_insert_db"
95
+
96
+ try:
97
+ test_client.execute(f"CREATE DATABASE IF NOT EXISTS {test_db}")
98
+ test_client.execute(f"USE {test_db}")
99
+
100
+ # Create test tables using models
101
+ test_client.create_table(User)
102
+ test_client.create_table(Product)
103
+ test_client.create_table(Order)
104
+
105
+ yield test_db
106
+
107
+ finally:
108
+ # Clean up
109
+ try:
110
+ test_client.execute(f"DROP DATABASE IF EXISTS {test_db}")
111
+ except Exception as e:
112
+ print(f"Cleanup failed: {e}")
113
+
114
+ def test_single_insert_with_model(self, test_client, test_database):
115
+ """Test inserting a single record using model class"""
116
+ # Insert a single user using model class
117
+ result = test_client.insert(
118
+ User, {'id': 1, 'name': 'John Doe', 'email': 'john@example.com', 'age': 30, 'created_at': datetime.now()}
119
+ )
120
+
121
+ assert result.affected_rows == 1
122
+
123
+ # Verify the data was inserted correctly
124
+ users = test_client.query(User).all()
125
+ assert len(users) == 1
126
+ assert users[0].name == 'John Doe'
127
+ assert users[0].email == 'john@example.com'
128
+ assert users[0].age == 30
129
+
130
+ def test_batch_insert_with_model(self, test_client, test_database):
131
+ """Test batch inserting multiple records using model class"""
132
+ # Insert multiple products using model class
133
+ products_data = [
134
+ {
135
+ 'id': 1,
136
+ 'name': 'Laptop',
137
+ 'description': 'High-performance laptop',
138
+ 'price': 999.99,
139
+ 'category': 'Electronics',
140
+ 'in_stock': 10,
141
+ },
142
+ {
143
+ 'id': 2,
144
+ 'name': 'Book',
145
+ 'description': 'Programming guide',
146
+ 'price': 29.99,
147
+ 'category': 'Education',
148
+ 'in_stock': 50,
149
+ },
150
+ {
151
+ 'id': 3,
152
+ 'name': 'Phone',
153
+ 'description': 'Smartphone with AI features',
154
+ 'price': 699.99,
155
+ 'category': 'Electronics',
156
+ 'in_stock': 25,
157
+ },
158
+ ]
159
+
160
+ result = test_client.batch_insert(Product, products_data)
161
+ assert result.affected_rows == 3
162
+
163
+ # Verify the data was inserted correctly
164
+ products = test_client.query(Product).all()
165
+ assert len(products) == 3
166
+
167
+ # Check specific products
168
+ laptop = test_client.query(Product).filter_by(name='Laptop').first()
169
+ assert laptop is not None
170
+ assert float(laptop.price) == 999.99
171
+ assert laptop.category == 'Electronics'
172
+ assert laptop.in_stock == 10
173
+
174
+ def test_insert_with_null_values(self, test_client, test_database):
175
+ """Test inserting records with NULL values using model class"""
176
+ # Insert a user with some NULL values
177
+ result = test_client.insert(
178
+ User, {'id': 2, 'name': 'Jane Smith', 'email': None, 'age': 25, 'created_at': None} # NULL value # NULL value
179
+ )
180
+
181
+ assert result.affected_rows == 1
182
+
183
+ # Verify the data was inserted correctly
184
+ user = test_client.query(User).filter_by(id=2).first()
185
+ assert user is not None
186
+ assert user.name == 'Jane Smith'
187
+ assert user.email is None
188
+ assert user.age == 25
189
+ assert user.created_at is None
190
+
191
+ def test_insert_with_different_data_types(self, test_client, test_database):
192
+ """Test inserting records with different data types using model class"""
193
+ # Insert an order with various data types
194
+ result = test_client.insert(
195
+ Order,
196
+ {
197
+ 'id': 1,
198
+ 'user_id': 1,
199
+ 'product_id': 1,
200
+ 'quantity': 2,
201
+ 'total_amount': 1999.98,
202
+ 'order_date': datetime(2024, 1, 15, 10, 30, 0),
203
+ },
204
+ )
205
+
206
+ assert result.affected_rows == 1
207
+
208
+ # Verify the data was inserted correctly
209
+ order = test_client.query(Order).filter_by(id=1).first()
210
+ assert order is not None
211
+ assert order.user_id == 1
212
+ assert order.product_id == 1
213
+ assert order.quantity == 2
214
+ assert float(order.total_amount) == 1999.98
215
+ assert order.order_date.year == 2024
216
+ assert order.order_date.month == 1
217
+ assert order.order_date.day == 15
218
+
219
+ def test_insert_error_handling(self, test_client, test_database):
220
+ """Test error handling when inserting invalid data using model class"""
221
+ # Try to insert a user with duplicate primary key
222
+ with pytest.raises(QueryError):
223
+ test_client.insert(
224
+ User,
225
+ {'id': 1, 'name': 'Duplicate User', 'email': 'duplicate@example.com', 'age': 35}, # Duplicate primary key
226
+ )
227
+
228
+ def test_insert_vs_string_table_name(self, test_client, test_database):
229
+ """Test that both model class and string table name work the same way"""
230
+ # Clear existing data first
231
+ test_client.execute("DELETE FROM test_insert_users")
232
+
233
+ # Insert using model class
234
+ result1 = test_client.insert(User, {'id': 3, 'name': 'Model User', 'email': 'model@example.com', 'age': 28})
235
+
236
+ # Insert using string table name
237
+ result2 = test_client.insert(
238
+ 'test_insert_users', {'id': 4, 'name': 'String User', 'email': 'string@example.com', 'age': 32}
239
+ )
240
+
241
+ assert result1.affected_rows == 1
242
+ assert result2.affected_rows == 1
243
+
244
+ # Verify both records were inserted
245
+ users = test_client.query(User).all()
246
+ assert len(users) == 2 # Only the two we just inserted
247
+
248
+ # Check specific users
249
+ model_user = test_client.query(User).filter_by(name='Model User').first()
250
+ string_user = test_client.query(User).filter_by(name='String User').first()
251
+
252
+ assert model_user is not None
253
+ assert string_user is not None
254
+ assert model_user.email == 'model@example.com'
255
+ assert string_user.email == 'string@example.com'
@@ -0,0 +1,213 @@
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 MySQL driver validation in Client.from_engine() and AsyncClient.from_engine()
17
+ """
18
+
19
+ import pytest
20
+ import os
21
+ import sys
22
+ from sqlalchemy import create_engine
23
+ from sqlalchemy.ext.asyncio import create_async_engine
24
+
25
+ # Add the matrixone package to the path
26
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..'))
27
+
28
+ from matrixone import Client, AsyncClient
29
+ from matrixone.exceptions import ConnectionError
30
+ from .test_config import online_config
31
+
32
+
33
+ class TestMySQLDriverValidation:
34
+ """Online tests for MySQL driver validation"""
35
+
36
+ @pytest.fixture(scope="class")
37
+ def connection_params(self):
38
+ """Get connection parameters"""
39
+ return online_config.get_connection_params()
40
+
41
+ def test_client_from_mysql_engine_success(self, connection_params):
42
+ """Test creating Client from valid MySQL engine"""
43
+ host, port, user, password, database = connection_params
44
+
45
+ # Create valid MySQL engine
46
+ connection_string = f"mysql+pymysql://{user}:{password}@{host}:{port}/{database}"
47
+ engine = create_engine(connection_string)
48
+
49
+ try:
50
+ # Should succeed with MySQL engine
51
+ client = Client.from_engine(engine)
52
+ assert client.connected() is True
53
+
54
+ # Test basic functionality
55
+ result = client.execute("SELECT 1 as test_value")
56
+ rows = result.fetchall()
57
+ assert len(rows) == 1
58
+ assert rows[0][0] == 1
59
+
60
+ finally:
61
+ client.disconnect()
62
+ engine.dispose()
63
+
64
+ def test_client_from_non_mysql_engine_failure(self):
65
+ """Test creating Client from non-MySQL engine fails"""
66
+ # Test with SQLite engine (should fail) - SQLite doesn't require external drivers
67
+ engine = create_engine("sqlite:///:memory:")
68
+
69
+ try:
70
+ # Should raise ConnectionError
71
+ with pytest.raises(ConnectionError) as exc_info:
72
+ Client.from_engine(engine)
73
+
74
+ assert "MatrixOne Client only supports MySQL drivers" in str(exc_info.value)
75
+ assert "sqlite" in str(exc_info.value).lower()
76
+
77
+ finally:
78
+ engine.dispose()
79
+
80
+ def test_client_from_sqlite_engine_failure(self):
81
+ """Test creating Client from SQLite engine fails"""
82
+ # Create SQLite engine (should fail)
83
+ engine = create_engine("sqlite:///:memory:")
84
+
85
+ try:
86
+ # Should raise ConnectionError
87
+ with pytest.raises(ConnectionError) as exc_info:
88
+ Client.from_engine(engine)
89
+
90
+ assert "MatrixOne Client only supports MySQL drivers" in str(exc_info.value)
91
+ assert "sqlite" in str(exc_info.value).lower()
92
+
93
+ finally:
94
+ engine.dispose()
95
+
96
+ def test_client_from_mysqlconnector_engine_success(self, connection_params):
97
+ """Test creating Client from mysql+mysqlconnector engine"""
98
+ # Test the driver detection method directly without creating actual engine
99
+ # since mysql-connector-python might not be installed
100
+ connection_string = "mysql+mysqlconnector://user:pass@localhost:3306/db"
101
+
102
+ # Create a mock engine object to test the detection logic
103
+ class MockEngine:
104
+ def __init__(self, url):
105
+ self.url = url
106
+ self.dialect = MockDialect()
107
+
108
+ class MockDialect:
109
+ def __init__(self):
110
+ self.name = "mysql"
111
+
112
+ mock_engine = MockEngine(connection_string)
113
+
114
+ # Test that our detection method correctly identifies MySQL connector
115
+ assert Client._is_mysql_engine(mock_engine) is True
116
+
117
+ @pytest.mark.asyncio
118
+ async def test_async_client_from_mysql_engine_success(self, connection_params):
119
+ """Test creating AsyncClient from valid MySQL async engine"""
120
+ host, port, user, password, database = connection_params
121
+
122
+ # Create valid MySQL async engine
123
+ connection_string = f"mysql+aiomysql://{user}:{password}@{host}:{port}/{database}"
124
+ engine = create_async_engine(connection_string)
125
+
126
+ try:
127
+ # Should succeed with MySQL async engine
128
+ client = AsyncClient.from_engine(engine)
129
+ assert client.connected() is True
130
+
131
+ # Test basic functionality
132
+ result = await client.execute("SELECT 3 as test_value")
133
+ rows = result.fetchall()
134
+ assert len(rows) == 1
135
+ assert rows[0][0] == 3
136
+
137
+ finally:
138
+ await client.disconnect()
139
+ await engine.dispose()
140
+
141
+ @pytest.mark.asyncio
142
+ async def test_async_client_from_non_mysql_engine_failure(self):
143
+ """Test creating AsyncClient from non-MySQL async engine fails"""
144
+ # Test the driver detection method directly without creating actual engine
145
+ # since aiosqlite might not be installed
146
+ connection_string = "sqlite+aiosqlite:///:memory:"
147
+
148
+ # Create a mock engine object to test the detection logic
149
+ class MockAsyncEngine:
150
+ def __init__(self, url):
151
+ self.url = url
152
+ self.dialect = MockAsyncDialect()
153
+
154
+ class MockAsyncDialect:
155
+ def __init__(self):
156
+ self.name = "sqlite"
157
+
158
+ mock_engine = MockAsyncEngine(connection_string)
159
+
160
+ # Test that our detection method correctly identifies non-MySQL driver
161
+ assert AsyncClient._is_mysql_async_engine(mock_engine) is False
162
+
163
+ def test_client_mysql_driver_detection_methods(self):
164
+ """Test the _is_mysql_engine method with various engines"""
165
+ # Test with MySQL engines (only test pymysql since it's available)
166
+ mysql_engines = [
167
+ create_engine("mysql+pymysql://user:pass@localhost:3306/db"),
168
+ ]
169
+
170
+ for engine in mysql_engines:
171
+ assert Client._is_mysql_engine(engine) is True
172
+ engine.dispose()
173
+
174
+ # Test with non-MySQL engines (only test SQLite since it doesn't require external drivers)
175
+ non_mysql_engines = [
176
+ create_engine("sqlite:///:memory:"),
177
+ ]
178
+
179
+ for engine in non_mysql_engines:
180
+ assert Client._is_mysql_engine(engine) is False
181
+ engine.dispose()
182
+
183
+ @pytest.mark.asyncio
184
+ async def test_async_client_mysql_driver_detection_methods(self):
185
+ """Test the _is_mysql_async_engine method with various async engines"""
186
+ # Test with MySQL async engines (only test aiomysql since it's available)
187
+ mysql_async_engines = [
188
+ create_async_engine("mysql+aiomysql://user:pass@localhost:3306/db"),
189
+ ]
190
+
191
+ for engine in mysql_async_engines:
192
+ assert AsyncClient._is_mysql_async_engine(engine) is True
193
+ await engine.dispose()
194
+
195
+ # Test with non-MySQL async engines using mock objects
196
+ # since aiosqlite might not be installed
197
+ connection_string = "sqlite+aiosqlite:///:memory:"
198
+
199
+ class MockAsyncEngine:
200
+ def __init__(self, url):
201
+ self.url = url
202
+ self.dialect = MockAsyncDialect()
203
+
204
+ class MockAsyncDialect:
205
+ def __init__(self):
206
+ self.name = "sqlite"
207
+
208
+ mock_engine = MockAsyncEngine(connection_string)
209
+ assert AsyncClient._is_mysql_async_engine(mock_engine) is False
210
+
211
+
212
+ if __name__ == "__main__":
213
+ pytest.main([__file__])