11"""Integration tests for DuckDB migration workflow."""
22
3- import tempfile
43from pathlib import Path
54from typing import Any
65
1211pytestmark = pytest .mark .xdist_group ("duckdb" )
1312
1413
15- def test_duckdb_migration_full_workflow () -> None :
14+ def test_duckdb_migration_full_workflow (tmp_path : Path ) -> None :
1615 """Test full DuckDB migration workflow: init -> create -> upgrade -> downgrade."""
17- with tempfile .TemporaryDirectory () as temp_dir :
18- migration_dir = Path (temp_dir ) / "migrations"
19- db_path = Path (temp_dir ) / "test.duckdb"
16+ migration_dir = tmp_path / "migrations"
17+ db_path = tmp_path / "test.duckdb"
2018
21- config = DuckDBConfig (
22- pool_config = {"database" : str (db_path )},
23- migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
24- )
25- commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
19+ config = DuckDBConfig (
20+ pool_config = {"database" : str (db_path )},
21+ migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
22+ )
23+ commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
2624
27- commands .init (str (migration_dir ), package = True )
25+ commands .init (str (migration_dir ), package = True )
2826
29- assert migration_dir .exists ()
30- assert (migration_dir / "__init__.py" ).exists ()
27+ assert migration_dir .exists ()
28+ assert (migration_dir / "__init__.py" ).exists ()
3129
32- migration_content = '''"""Initial schema migration."""
30+ migration_content = '''"""Initial schema migration."""
3331
3432
3533def up():
@@ -49,44 +47,43 @@ def down():
4947 return ["DROP TABLE IF EXISTS users"]
5048'''
5149
52- migration_file = migration_dir / "0001_create_users.py"
53- migration_file .write_text (migration_content )
50+ migration_file = migration_dir / "0001_create_users.py"
51+ migration_file .write_text (migration_content )
5452
55- commands .upgrade ()
53+ commands .upgrade ()
5654
57- with config .provide_session () as driver :
58- result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'users'" )
59- assert len (result .data ) == 1
55+ with config .provide_session () as driver :
56+ result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'users'" )
57+ assert len (result .data ) == 1
6058
61- driver .
execute (
"INSERT INTO users (id, name, email) VALUES (?, ?, ?)" , (
1 ,
"John Doe" ,
"[email protected] " ))
59+ driver .
execute (
"INSERT INTO users (id, name, email) VALUES (?, ?, ?)" , (
1 ,
"John Doe" ,
"[email protected] " ))
6260
63- users_result = driver .execute ("SELECT * FROM users" )
64- assert len (users_result .data ) == 1
65- assert users_result .data [0 ]["name" ] == "John Doe"
66- assert users_result .
data [
0 ][
"email" ]
== "[email protected] " 61+ users_result = driver .execute ("SELECT * FROM users" )
62+ assert len (users_result .data ) == 1
63+ assert users_result .data [0 ]["name" ] == "John Doe"
64+ assert users_result .
data [
0 ][
"email" ]
== "[email protected] " 6765
68- commands .downgrade ("base" )
66+ commands .downgrade ("base" )
6967
70- with config .provide_session () as driver :
71- result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'users'" )
72- assert len (result .data ) == 0
68+ with config .provide_session () as driver :
69+ result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'users'" )
70+ assert len (result .data ) == 0
7371
7472
75- def test_duckdb_multiple_migrations_workflow () -> None :
73+ def test_duckdb_multiple_migrations_workflow (tmp_path : Path ) -> None :
7674 """Test DuckDB workflow with multiple migrations: create -> apply both -> downgrade one -> downgrade all."""
77- with tempfile .TemporaryDirectory () as temp_dir :
78- migration_dir = Path (temp_dir ) / "migrations"
79- db_path = Path (temp_dir ) / "test.duckdb"
75+ migration_dir = tmp_path / "migrations"
76+ db_path = tmp_path / "test.duckdb"
8077
81- config = DuckDBConfig (
82- pool_config = {"database" : str (db_path )},
83- migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
84- )
85- commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
78+ config = DuckDBConfig (
79+ pool_config = {"database" : str (db_path )},
80+ migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
81+ )
82+ commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
8683
87- commands .init (str (migration_dir ), package = True )
84+ commands .init (str (migration_dir ), package = True )
8885
89- migration1_content = '''"""Create users table."""
86+ migration1_content = '''"""Create users table."""
9087
9188
9289def up():
@@ -105,7 +102,7 @@ def down():
105102 return ["DROP TABLE IF EXISTS users"]
106103'''
107104
108- migration2_content = '''"""Create posts table."""
105+ migration2_content = '''"""Create posts table."""
109106
110107
111108def up():
@@ -126,66 +123,63 @@ def down():
126123 return ["DROP TABLE IF EXISTS posts"]
127124'''
128125
129- (migration_dir / "0001_create_users.py" ).write_text (migration1_content )
130- (migration_dir / "0002_create_posts.py" ).write_text (migration2_content )
126+ (migration_dir / "0001_create_users.py" ).write_text (migration1_content )
127+ (migration_dir / "0002_create_posts.py" ).write_text (migration2_content )
131128
132- commands .upgrade ()
129+ commands .upgrade ()
133130
134- with config .provide_session () as driver :
135- tables_result = driver .execute (
136- "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main' ORDER BY table_name"
137- )
138- table_names = [t ["table_name" ] for t in tables_result .data ]
139- assert "users" in table_names
140- assert "posts" in table_names
131+ with config .provide_session () as driver :
132+ tables_result = driver .execute (
133+ "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main' ORDER BY table_name"
134+ )
135+ table_names = [t ["table_name" ] for t in tables_result .data ]
136+ assert "users" in table_names
137+ assert "posts" in table_names
141138
142- driver .
execute (
"INSERT INTO users (id, name, email) VALUES (?, ?, ?)" , (
1 ,
"Author" ,
"[email protected] " ))
143- driver .execute (
144- "INSERT INTO posts (id, title, content, user_id) VALUES (?, ?, ?, ?)" , (1 , "My Post" , "Post content" , 1 )
145- )
139+ driver .
execute (
"INSERT INTO users (id, name, email) VALUES (?, ?, ?)" , (
1 ,
"Author" ,
"[email protected] " ))
140+ driver .execute (
141+ "INSERT INTO posts (id, title, content, user_id) VALUES (?, ?, ?, ?)" , (1 , "My Post" , "Post content" , 1 )
142+ )
146143
147- posts_result = driver .execute ("SELECT * FROM posts" )
148- assert len (posts_result .data ) == 1
149- assert posts_result .data [0 ]["title" ] == "My Post"
144+ posts_result = driver .execute ("SELECT * FROM posts" )
145+ assert len (posts_result .data ) == 1
146+ assert posts_result .data [0 ]["title" ] == "My Post"
150147
151- commands .downgrade ("0001" )
148+ commands .downgrade ("0001" )
152149
153- with config .provide_session () as driver :
154- tables_result = driver .execute (
155- "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main'"
156- )
157- table_names = [t ["table_name" ] for t in tables_result .data ]
158- assert "users" in table_names
159- assert "posts" not in table_names
150+ with config .provide_session () as driver :
151+ tables_result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_schema = 'main'" )
152+ table_names = [t ["table_name" ] for t in tables_result .data ]
153+ assert "users" in table_names
154+ assert "posts" not in table_names
160155
161- commands .downgrade ("base" )
156+ commands .downgrade ("base" )
162157
163- with config .provide_session () as driver :
164- tables_result = driver .execute (
165- "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main' AND table_name NOT LIKE 'sqlspec_%'"
166- )
158+ with config .provide_session () as driver :
159+ tables_result = driver .execute (
160+ "SELECT table_name FROM information_schema.tables WHERE table_schema = 'main' AND table_name NOT LIKE 'sqlspec_%'"
161+ )
167162
168- table_names = [t ["table_name" ] for t in tables_result .data if not t ["table_name" ].startswith ("sqlspec_" )]
169- assert len (table_names ) == 0
163+ table_names = [t ["table_name" ] for t in tables_result .data if not t ["table_name" ].startswith ("sqlspec_" )]
164+ assert len (table_names ) == 0
170165
171166
172- def test_duckdb_migration_current_command () -> None :
167+ def test_duckdb_migration_current_command (tmp_path : Path ) -> None :
173168 """Test the current migration command shows correct version for DuckDB."""
174- with tempfile .TemporaryDirectory () as temp_dir :
175- migration_dir = Path (temp_dir ) / "migrations"
176- db_path = Path (temp_dir ) / "test.duckdb"
169+ migration_dir = tmp_path / "migrations"
170+ db_path = tmp_path / "test.duckdb"
177171
178- config = DuckDBConfig (
179- pool_config = {"database" : str (db_path )},
180- migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
181- )
182- commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
172+ config = DuckDBConfig (
173+ pool_config = {"database" : str (db_path )},
174+ migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
175+ )
176+ commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
183177
184- commands .init (str (migration_dir ), package = True )
178+ commands .init (str (migration_dir ), package = True )
185179
186- commands .current (verbose = False )
180+ commands .current (verbose = False )
187181
188- migration_content = '''"""Test migration."""
182+ migration_content = '''"""Test migration."""
189183
190184
191185def up():
@@ -198,28 +192,27 @@ def down():
198192 return ["DROP TABLE IF EXISTS test_table"]
199193'''
200194
201- (migration_dir / "0001_test.py" ).write_text (migration_content )
195+ (migration_dir / "0001_test.py" ).write_text (migration_content )
202196
203- commands .upgrade ()
197+ commands .upgrade ()
204198
205- commands .current (verbose = True )
199+ commands .current (verbose = True )
206200
207201
208- def test_duckdb_migration_error_handling () -> None :
202+ def test_duckdb_migration_error_handling (tmp_path : Path ) -> None :
209203 """Test DuckDB migration error handling."""
210- with tempfile .TemporaryDirectory () as temp_dir :
211- migration_dir = Path (temp_dir ) / "migrations"
212- db_path = Path (temp_dir ) / "test.duckdb"
204+ migration_dir = tmp_path / "migrations"
205+ db_path = tmp_path / "test.duckdb"
213206
214- config = DuckDBConfig (
215- pool_config = {"database" : str (db_path )},
216- migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
217- )
218- commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
207+ config = DuckDBConfig (
208+ pool_config = {"database" : str (db_path )},
209+ migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
210+ )
211+ commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
219212
220- commands .init (str (migration_dir ), package = True )
213+ commands .init (str (migration_dir ), package = True )
221214
222- migration_content = '''"""Bad migration."""
215+ migration_content = '''"""Bad migration."""
223216
224217
225218def up():
@@ -232,30 +225,29 @@ def down():
232225 return []
233226'''
234227
235- (migration_dir / "0001_bad.py" ).write_text (migration_content )
228+ (migration_dir / "0001_bad.py" ).write_text (migration_content )
236229
237- commands .upgrade ()
230+ commands .upgrade ()
238231
239- with config .provide_session () as driver :
240- count = driver .select_value ("SELECT COUNT(*) FROM sqlspec_migrations" )
241- assert count == 0 , f"Expected empty migration table after failed migration, but found { count } records"
232+ with config .provide_session () as driver :
233+ count = driver .select_value ("SELECT COUNT(*) FROM sqlspec_migrations" )
234+ assert count == 0 , f"Expected empty migration table after failed migration, but found { count } records"
242235
243236
244- def test_duckdb_migration_with_transactions () -> None :
237+ def test_duckdb_migration_with_transactions (tmp_path : Path ) -> None :
245238 """Test DuckDB migrations work properly with transactions."""
246- with tempfile .TemporaryDirectory () as temp_dir :
247- migration_dir = Path (temp_dir ) / "migrations"
248- db_path = Path (temp_dir ) / "test.duckdb"
239+ migration_dir = tmp_path / "migrations"
240+ db_path = tmp_path / "test.duckdb"
249241
250- config = DuckDBConfig (
251- pool_config = {"database" : str (db_path )},
252- migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
253- )
254- commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
242+ config = DuckDBConfig (
243+ pool_config = {"database" : str (db_path )},
244+ migration_config = {"script_location" : str (migration_dir ), "version_table_name" : "sqlspec_migrations" },
245+ )
246+ commands : SyncMigrationCommands [Any ] | AsyncMigrationCommands [Any ] = create_migration_commands (config )
255247
256- commands .init (str (migration_dir ), package = True )
248+ commands .init (str (migration_dir ), package = True )
257249
258- migration_content = '''"""Migration with multiple operations."""
250+ migration_content = '''"""Migration with multiple operations."""
259251
260252
261253def up():
@@ -275,18 +267,18 @@ def down():
275267 return ["DROP TABLE IF EXISTS customers"]
276268'''
277269
278- (migration_dir / "0001_transaction_test.py" ).write_text (migration_content )
270+ (migration_dir / "0001_transaction_test.py" ).write_text (migration_content )
279271
280- commands .upgrade ()
272+ commands .upgrade ()
281273
282- with config .provide_session () as driver :
283- customers_result = driver .execute ("SELECT * FROM customers ORDER BY name" )
284- assert len (customers_result .data ) == 2
285- assert customers_result .data [0 ]["name" ] == "Customer 1"
286- assert customers_result .data [1 ]["name" ] == "Customer 2"
274+ with config .provide_session () as driver :
275+ customers_result = driver .execute ("SELECT * FROM customers ORDER BY name" )
276+ assert len (customers_result .data ) == 2
277+ assert customers_result .data [0 ]["name" ] == "Customer 1"
278+ assert customers_result .data [1 ]["name" ] == "Customer 2"
287279
288- commands .downgrade ("base" )
280+ commands .downgrade ("base" )
289281
290- with config .provide_session () as driver :
291- result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'customers'" )
292- assert len (result .data ) == 0
282+ with config .provide_session () as driver :
283+ result = driver .execute ("SELECT table_name FROM information_schema.tables WHERE table_name = 'customers'" )
284+ assert len (result .data ) == 0
0 commit comments