1+ #!/usr/bin/env python3
2+ """
3+ Unit tests for psqlpy-sqlalchemy dialect
4+ """
5+
6+ import unittest
7+
8+ from sqlalchemy import Column , Integer , MetaData , String , Table , create_engine , text
9+ from sqlalchemy .orm import sessionmaker
10+ from sqlalchemy .schema import CreateTable
11+
12+
13+ class TestPsqlpyDialect (unittest .TestCase ):
14+ """Test cases for the psqlpy SQLAlchemy dialect"""
15+
16+ def setUp (self ):
17+ """Set up test fixtures before each test method."""
18+ self .engine = None
19+
20+ def tearDown (self ):
21+ """Clean up after each test method."""
22+ if self .engine :
23+ self .engine .dispose ()
24+
25+ def test_dialect_registration (self ):
26+ """Test that the dialect is properly registered"""
27+ try :
28+ self .engine = create_engine ("postgresql+psqlpy://user:password@localhost/test" )
29+ self .assertIsNotNone (self .engine .dialect )
30+ self .assertEqual (self .engine .dialect .name , "postgresql" )
31+ self .assertEqual (self .engine .dialect .driver , "psqlpy" )
32+ except Exception as e :
33+ self .fail (f"Failed to register dialect: { e } " )
34+
35+ def test_connection_string_parsing (self ):
36+ """Test connection string parsing"""
37+ try :
38+ self .engine = create_engine ("postgresql+psqlpy://testuser:testpass@localhost:5432/testdb?sslmode=require" )
39+
40+ # Test create_connect_args
41+ args , kwargs = self .engine .dialect .create_connect_args (self .engine .url )
42+
43+ self .assertIsInstance (args , list )
44+ self .assertIsInstance (kwargs , dict )
45+
46+ # Check expected connection parameters
47+ expected_keys = ['host' , 'port' , 'db_name' , 'username' , 'password' ]
48+ for key in expected_keys :
49+ self .assertIn (key , kwargs , f"Missing connection parameter: { key } " )
50+
51+ # Verify specific values
52+ self .assertEqual (kwargs ['host' ], 'localhost' )
53+ self .assertEqual (kwargs ['port' ], 5432 )
54+ self .assertEqual (kwargs ['db_name' ], 'testdb' )
55+ self .assertEqual (kwargs ['username' ], 'testuser' )
56+ self .assertEqual (kwargs ['password' ], 'testpass' )
57+
58+ except Exception as e :
59+ self .fail (f"Failed to parse connection string: { e } " )
60+
61+ def test_basic_sql_compilation (self ):
62+ """Test basic SQL compilation"""
63+ try :
64+ self .engine = create_engine ("postgresql+psqlpy://user:password@localhost/test" )
65+
66+ # Test basic SQL compilation
67+ stmt = text ("SELECT 1 as test_column" )
68+ compiled = stmt .compile (self .engine )
69+ self .assertIsNotNone (compiled )
70+ self .assertIn ("SELECT 1" , str (compiled ))
71+
72+ # Test table creation SQL
73+ metadata = MetaData ()
74+ test_table = Table ('test_table' , metadata ,
75+ Column ('id' , Integer , primary_key = True ),
76+ Column ('name' , String (50 ))
77+ )
78+
79+ create_ddl = CreateTable (test_table )
80+ create_sql = str (create_ddl .compile (self .engine ))
81+ self .assertIsNotNone (create_sql )
82+ self .assertIn ("CREATE TABLE test_table" , create_sql )
83+ self .assertIn ("id" , create_sql )
84+ self .assertIn ("name" , create_sql )
85+
86+ except Exception as e :
87+ self .fail (f"Failed SQL compilation: { e } " )
88+
89+ def test_dbapi_interface (self ):
90+ """Test DBAPI interface"""
91+ try :
92+ self .engine = create_engine ("postgresql+psqlpy://user:password@localhost/test" )
93+ dbapi = self .engine .dialect .import_dbapi ()
94+
95+ self .assertIsNotNone (dbapi )
96+
97+ # Test DBAPI attributes
98+ self .assertEqual (dbapi .apilevel , "2.0" )
99+ self .assertEqual (dbapi .threadsafety , 2 )
100+ self .assertEqual (dbapi .paramstyle , "pyformat" )
101+
102+ # Test exception hierarchy
103+ exceptions = ['Warning' , 'Error' , 'InterfaceError' , 'DatabaseError' ,
104+ 'DataError' , 'OperationalError' , 'IntegrityError' ,
105+ 'InternalError' , 'ProgrammingError' , 'NotSupportedError' ]
106+
107+ for exc_name in exceptions :
108+ self .assertTrue (hasattr (dbapi , exc_name ), f"Missing DBAPI exception: { exc_name } " )
109+
110+ except Exception as e :
111+ self .fail (f"Failed DBAPI interface test: { e } " )
112+
113+ def test_mock_connection (self ):
114+ """Test connection creation (without actual database)"""
115+ try :
116+ self .engine = create_engine ("postgresql+psqlpy://user:password@localhost/test" )
117+
118+ # This will fail because we don't have a real database,
119+ # but we can test that the connection creation process starts correctly
120+ try :
121+ connection = self .engine .connect ()
122+ # If we get here, connection succeeded unexpectedly
123+ connection .close ()
124+ self .fail ("Connection succeeded unexpectedly without a real database" )
125+ except Exception :
126+ # This is expected - we don't have a real database
127+ # The test passes if an exception is raised
128+ pass
129+
130+ except Exception as e :
131+ # If we get here, it means the test setup itself failed
132+ self .fail (f"Unexpected error in connection test setup: { e } " )
133+
134+ def test_dialect_capabilities (self ):
135+ """Test dialect capabilities and features"""
136+ try :
137+ self .engine = create_engine ("postgresql+psqlpy://user:password@localhost/test" )
138+ dialect = self .engine .dialect
139+
140+ # Test key dialect capabilities
141+ self .assertTrue (dialect .supports_statement_cache )
142+ self .assertTrue (dialect .supports_multivalues_insert )
143+ self .assertTrue (dialect .supports_unicode_statements )
144+ self .assertTrue (dialect .supports_unicode_binds )
145+ self .assertTrue (dialect .supports_native_decimal )
146+ self .assertTrue (dialect .supports_native_boolean )
147+ self .assertTrue (dialect .supports_sequences )
148+ self .assertTrue (dialect .implicit_returning )
149+ self .assertTrue (dialect .full_returning )
150+
151+ except Exception as e :
152+ self .fail (f"Failed dialect capabilities test: { e } " )
153+
154+
155+ class TestPsqlpyConnection (unittest .TestCase ):
156+ """Test cases for psqlpy connection wrapper"""
157+
158+ def test_connection_wrapper_creation (self ):
159+ """Test that connection wrapper can be created"""
160+ from psqlpy_sqlalchemy .connection import PsqlpyConnection
161+
162+ # We can't create a real connection without a database,
163+ # but we can test the class exists and has required methods
164+ self .assertTrue (hasattr (PsqlpyConnection , 'cursor' ))
165+ self .assertTrue (hasattr (PsqlpyConnection , 'commit' ))
166+ self .assertTrue (hasattr (PsqlpyConnection , 'rollback' ))
167+ self .assertTrue (hasattr (PsqlpyConnection , 'close' ))
168+
169+ def test_cursor_wrapper_creation (self ):
170+ """Test that cursor wrapper can be created"""
171+ from psqlpy_sqlalchemy .connection import PsqlpyCursor
172+
173+ # Test the class exists and has required methods
174+ self .assertTrue (hasattr (PsqlpyCursor , 'execute' ))
175+ self .assertTrue (hasattr (PsqlpyCursor , 'executemany' ))
176+ self .assertTrue (hasattr (PsqlpyCursor , 'fetchone' ))
177+ self .assertTrue (hasattr (PsqlpyCursor , 'fetchmany' ))
178+ self .assertTrue (hasattr (PsqlpyCursor , 'fetchall' ))
179+ self .assertTrue (hasattr (PsqlpyCursor , 'close' ))
180+
181+
182+ if __name__ == '__main__' :
183+ unittest .main ()
0 commit comments