22Tests for UUID parameter binding support in psqlpy-sqlalchemy.
33"""
44
5- import asyncio
5+ import os
66import uuid
7+
78import pytest
8- from sqlalchemy import Column , Integer , String , create_engine , text
9+ from sqlalchemy import Column , Integer , String , text
910from sqlalchemy .dialects .postgresql import UUID
11+ from sqlalchemy .exc import StatementError
1012from sqlalchemy .ext .asyncio import AsyncSession , create_async_engine
1113from sqlalchemy .orm import DeclarativeBase , sessionmaker
12- from sqlalchemy .exc import StatementError
13-
1414
15- import os
1615
1716# Skip tests if database is not available (check for CI environment or explicit flag)
1817def should_skip_db_tests ():
1918 """Check if database tests should be skipped."""
2019 # Run tests in CI environment
21- if os .getenv (' GITHUB_ACTIONS' ):
20+ if os .getenv (" GITHUB_ACTIONS" ):
2221 return False
2322 # Run tests if explicitly enabled
24- if os .getenv (' RUN_DB_TESTS' ):
23+ if os .getenv (" RUN_DB_TESTS" ):
2524 return False
2625 # Skip by default in local development
2726 return True
2827
28+
2929pytestmark = pytest .mark .skipif (
30- should_skip_db_tests (),
31- reason = "Database tests require live PostgreSQL connection. Set RUN_DB_TESTS=1 or run in CI."
30+ should_skip_db_tests (),
31+ reason = "Database tests require live PostgreSQL connection. Set RUN_DB_TESTS=1 or run in CI." ,
3232)
3333
3434
@@ -38,7 +38,7 @@ class Base(DeclarativeBase):
3838
3939class TestUUIDTable (Base ):
4040 __tablename__ = "test_uuid_table"
41-
41+
4242 id = Column (Integer , primary_key = True )
4343 uid = Column (UUID (as_uuid = True ), nullable = False )
4444 name = Column (String (100 ))
@@ -49,19 +49,19 @@ async def engine():
4949 """Create test engine."""
5050 # Use environment variables for database connection in CI
5151 db_url = os .getenv (
52- ' DATABASE_URL' ,
53- ' postgresql+psqlpy://postgres:password@localhost:5432/test_db'
52+ " DATABASE_URL" ,
53+ " postgresql+psqlpy://postgres:password@localhost:5432/test_db" ,
5454 )
5555 engine = create_async_engine (db_url , echo = False )
56-
56+
5757 async with engine .begin () as conn :
5858 await conn .run_sync (Base .metadata .create_all )
59-
59+
6060 yield engine
61-
61+
6262 async with engine .begin () as conn :
6363 await conn .run_sync (Base .metadata .drop_all )
64-
64+
6565 await engine .dispose ()
6666
6767
@@ -75,203 +75,230 @@ async def session(engine):
7575
7676class TestUUIDParameterBinding :
7777 """Test UUID parameter binding functionality."""
78-
78+
7979 async def test_uuid_object_parameter (self , engine ):
8080 """Test UUID object as parameter."""
8181 test_uuid = uuid .uuid4 ()
82-
82+
8383 async with engine .begin () as conn :
8484 # Insert with UUID object
8585 await conn .execute (
86- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)" ),
87- {"uid" : test_uuid , "name" : "test_uuid_object" }
86+ text (
87+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)"
88+ ),
89+ {"uid" : test_uuid , "name" : "test_uuid_object" },
8890 )
89-
91+
9092 # Query with UUID object
9193 result = await conn .execute (
9294 text ("SELECT * FROM test_uuid_table WHERE uid = :uid" ),
93- {"uid" : test_uuid }
95+ {"uid" : test_uuid },
9496 )
95-
97+
9698 rows = result .fetchall ()
9799 assert len (rows ) == 1
98100 assert rows [0 ].name == "test_uuid_object"
99-
101+
100102 async def test_uuid_string_parameter (self , engine ):
101103 """Test UUID string as parameter."""
102104 test_uuid = uuid .uuid4 ()
103105 test_uuid_str = str (test_uuid )
104-
106+
105107 async with engine .begin () as conn :
106108 # Insert with UUID string
107109 await conn .execute (
108- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)" ),
109- {"uid" : test_uuid_str , "name" : "test_uuid_string" }
110+ text (
111+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)"
112+ ),
113+ {"uid" : test_uuid_str , "name" : "test_uuid_string" },
110114 )
111-
115+
112116 # Query with UUID string
113117 result = await conn .execute (
114118 text ("SELECT * FROM test_uuid_table WHERE uid = :uid" ),
115- {"uid" : test_uuid_str }
119+ {"uid" : test_uuid_str },
116120 )
117-
121+
118122 rows = result .fetchall ()
119123 assert len (rows ) == 1
120124 assert rows [0 ].name == "test_uuid_string"
121-
125+
122126 async def test_uuid_with_explicit_cast (self , engine ):
123127 """Test UUID parameter with explicit PostgreSQL casting."""
124128 test_uuid = uuid .uuid4 ()
125-
129+
126130 async with engine .begin () as conn :
127131 # Insert test data
128132 await conn .execute (
129- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)" ),
130- {"uid" : test_uuid , "name" : "test_cast" }
133+ text (
134+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)"
135+ ),
136+ {"uid" : test_uuid , "name" : "test_cast" },
131137 )
132-
138+
133139 # This was the original failing case - explicit UUID casting
134140 result = await conn .execute (
135- text ("SELECT * FROM test_uuid_table WHERE uid = :uid::UUID LIMIT :limit" ),
136- {"uid" : str (test_uuid ), "limit" : 2 }
141+ text (
142+ "SELECT * FROM test_uuid_table WHERE uid = :uid::UUID LIMIT :limit"
143+ ),
144+ {"uid" : str (test_uuid ), "limit" : 2 },
137145 )
138-
146+
139147 rows = result .fetchall ()
140148 assert len (rows ) == 1
141149 assert rows [0 ].name == "test_cast"
142-
150+
143151 async def test_uuid_with_sqlalchemy_orm (self , session ):
144152 """Test UUID with SQLAlchemy ORM."""
145153 test_uuid = uuid .uuid4 ()
146-
154+
147155 # Insert with ORM
148156 test_obj = TestUUIDTable (uid = test_uuid , name = "test_orm" )
149157 session .add (test_obj )
150158 await session .commit ()
151-
159+
152160 # Query with ORM
153161 result = await session .execute (
154- text ("SELECT * FROM test_uuid_table WHERE uid = :uid ORDER BY id LIMIT :limit" ),
155- {"uid" : test_uuid , "limit" : 2 }
162+ text (
163+ "SELECT * FROM test_uuid_table WHERE uid = :uid ORDER BY id LIMIT :limit"
164+ ),
165+ {"uid" : test_uuid , "limit" : 2 },
156166 )
157-
167+
158168 rows = result .fetchall ()
159169 assert len (rows ) == 1
160170 assert rows [0 ].name == "test_orm"
161-
171+
162172 async def test_multiple_uuid_parameters (self , engine ):
163173 """Test multiple UUID parameters in one query."""
164174 uuid1 = uuid .uuid4 ()
165175 uuid2 = uuid .uuid4 ()
166-
176+
167177 async with engine .begin () as conn :
168178 # Insert test data
169179 await conn .execute (
170- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid1, :name1), (:uid2, :name2)" ),
171- {"uid1" : uuid1 , "name1" : "first" , "uid2" : uuid2 , "name2" : "second" }
180+ text (
181+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid1, :name1), (:uid2, :name2)"
182+ ),
183+ {
184+ "uid1" : uuid1 ,
185+ "name1" : "first" ,
186+ "uid2" : uuid2 ,
187+ "name2" : "second" ,
188+ },
172189 )
173-
190+
174191 # Query with multiple UUID parameters
175192 result = await conn .execute (
176- text ("SELECT * FROM test_uuid_table WHERE uid IN (:uid1, :uid2) ORDER BY name" ),
177- {"uid1" : uuid1 , "uid2" : uuid2 }
193+ text (
194+ "SELECT * FROM test_uuid_table WHERE uid IN (:uid1, :uid2) ORDER BY name"
195+ ),
196+ {"uid1" : uuid1 , "uid2" : uuid2 },
178197 )
179-
198+
180199 rows = result .fetchall ()
181200 assert len (rows ) == 2
182201 assert rows [0 ].name == "first"
183202 assert rows [1 ].name == "second"
184-
203+
185204 async def test_null_uuid_parameter (self , engine ):
186205 """Test NULL UUID parameter."""
187206 async with engine .begin () as conn :
188207 # Query with NULL UUID - should return no results
189208 result = await conn .execute (
190209 text ("SELECT * FROM test_uuid_table WHERE uid = :uid" ),
191- {"uid" : None }
210+ {"uid" : None },
192211 )
193-
212+
194213 rows = result .fetchall ()
195214 assert len (rows ) == 0
196-
215+
197216 async def test_invalid_uuid_string (self , engine ):
198217 """Test invalid UUID string raises proper error."""
199218 async with engine .begin () as conn :
200219 with pytest .raises ((ValueError , StatementError )):
201220 await conn .execute (
202- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)" ),
203- {"uid" : "invalid-uuid-string" , "name" : "test" }
221+ text (
222+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)"
223+ ),
224+ {"uid" : "invalid-uuid-string" , "name" : "test" },
204225 )
205-
226+
206227 async def test_uuid_edge_cases (self , engine ):
207228 """Test UUID edge cases."""
208229 # Test various UUID formats
209230 test_cases = [
210- uuid .UUID (' 00000000-0000-0000-0000-000000000000' ), # Nil UUID
211- uuid .UUID (' ffffffff-ffff-ffff-ffff-ffffffffffff' ), # Max UUID
231+ uuid .UUID (" 00000000-0000-0000-0000-000000000000" ), # Nil UUID
232+ uuid .UUID (" ffffffff-ffff-ffff-ffff-ffffffffffff" ), # Max UUID
212233 uuid .uuid1 (), # Time-based UUID
213234 uuid .uuid4 (), # Random UUID
214235 ]
215-
236+
216237 async with engine .begin () as conn :
217238 for i , test_uuid in enumerate (test_cases ):
218239 await conn .execute (
219- text ("INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)" ),
220- {"uid" : test_uuid , "name" : f"edge_case_{ i } " }
240+ text (
241+ "INSERT INTO test_uuid_table (uid, name) VALUES (:uid, :name)"
242+ ),
243+ {"uid" : test_uuid , "name" : f"edge_case_{ i } " },
221244 )
222-
245+
223246 # Verify all were inserted correctly
224247 result = await conn .execute (
225- text ("SELECT COUNT(*) as count FROM test_uuid_table WHERE name LIKE 'edge_case_%'" )
248+ text (
249+ "SELECT COUNT(*) as count FROM test_uuid_table WHERE name LIKE 'edge_case_%'"
250+ )
226251 )
227-
252+
228253 count = result .fetchone ().count
229254 assert count == len (test_cases )
230255
231256
232257class TestUUIDTypeCompatibility :
233258 """Test UUID type compatibility with existing functionality."""
234-
259+
235260 async def test_uuid_column_definition (self , engine ):
236261 """Test that UUID columns are properly defined."""
237262 async with engine .begin () as conn :
238263 # Check table structure
239264 result = await conn .execute (
240265 text ("""
241- SELECT column_name, data_type
242- FROM information_schema.columns
266+ SELECT column_name, data_type
267+ FROM information_schema.columns
243268 WHERE table_name = 'test_uuid_table' AND column_name = 'uid'
244269 """ )
245270 )
246-
271+
247272 row = result .fetchone ()
248273 assert row is not None
249- assert row .data_type == ' uuid'
250-
274+ assert row .data_type == " uuid"
275+
251276 async def test_uuid_index_support (self , engine ):
252277 """Test that UUID columns can be indexed."""
253278 async with engine .begin () as conn :
254279 # Create index on UUID column
255280 await conn .execute (
256- text ("CREATE INDEX IF NOT EXISTS idx_test_uuid_uid ON test_uuid_table(uid)" )
281+ text (
282+ "CREATE INDEX IF NOT EXISTS idx_test_uuid_uid ON test_uuid_table(uid)"
283+ )
257284 )
258-
285+
259286 # Verify index was created
260287 result = await conn .execute (
261288 text ("""
262- SELECT indexname
263- FROM pg_indexes
289+ SELECT indexname
290+ FROM pg_indexes
264291 WHERE tablename = 'test_uuid_table' AND indexname = 'idx_test_uuid_uid'
265292 """ )
266293 )
267-
294+
268295 row = result .fetchone ()
269296 assert row is not None
270-
297+
271298 # Clean up
272299 await conn .execute (text ("DROP INDEX IF EXISTS idx_test_uuid_uid" ))
273300
274301
275302if __name__ == "__main__" :
276303 # Run tests directly
277- pytest .main ([__file__ , "-v" ])
304+ pytest .main ([__file__ , "-v" ])
0 commit comments