Skip to content

Commit 0c552d1

Browse files
author
Eugene Shershen
committed
init
1 parent 8f1d3f7 commit 0c552d1

File tree

3 files changed

+301
-0
lines changed

3 files changed

+301
-0
lines changed

tests/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Test package for psqlpy-sqlalchemy

tests/test_dbapi.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Unit tests for psqlpy-sqlalchemy DBAPI interface
4+
"""
5+
6+
import datetime
7+
import unittest
8+
9+
from psqlpy_sqlalchemy.dbapi import PsqlpyDBAPI
10+
11+
12+
class TestPsqlpyDBAPI(unittest.TestCase):
13+
"""Test cases for the psqlpy DBAPI interface"""
14+
15+
def setUp(self):
16+
"""Set up test fixtures before each test method."""
17+
self.dbapi = PsqlpyDBAPI()
18+
19+
def test_dbapi_attributes(self):
20+
"""Test DBAPI 2.0 module attributes"""
21+
self.assertEqual(self.dbapi.apilevel, "2.0")
22+
self.assertEqual(self.dbapi.threadsafety, 2)
23+
self.assertEqual(self.dbapi.paramstyle, "pyformat")
24+
25+
def test_exception_hierarchy(self):
26+
"""Test that all required DBAPI exceptions are available"""
27+
exceptions = [
28+
'Warning', 'Error', 'InterfaceError', 'DatabaseError',
29+
'DataError', 'OperationalError', 'IntegrityError',
30+
'InternalError', 'ProgrammingError', 'NotSupportedError'
31+
]
32+
33+
for exc_name in exceptions:
34+
self.assertTrue(hasattr(self.dbapi, exc_name),
35+
f"Missing DBAPI exception: {exc_name}")
36+
exc_class = getattr(self.dbapi, exc_name)
37+
self.assertTrue(callable(exc_class),
38+
f"Exception {exc_name} is not callable")
39+
40+
def test_type_constructors(self):
41+
"""Test DBAPI type constructors"""
42+
# Test Date constructor
43+
date_obj = self.dbapi.Date(2023, 12, 25)
44+
self.assertIsInstance(date_obj, datetime.date)
45+
self.assertEqual(date_obj.year, 2023)
46+
self.assertEqual(date_obj.month, 12)
47+
self.assertEqual(date_obj.day, 25)
48+
49+
# Test Time constructor
50+
time_obj = self.dbapi.Time(14, 30, 45)
51+
self.assertIsInstance(time_obj, datetime.time)
52+
self.assertEqual(time_obj.hour, 14)
53+
self.assertEqual(time_obj.minute, 30)
54+
self.assertEqual(time_obj.second, 45)
55+
56+
# Test Timestamp constructor
57+
timestamp_obj = self.dbapi.Timestamp(2023, 12, 25, 14, 30, 45)
58+
self.assertIsInstance(timestamp_obj, datetime.datetime)
59+
self.assertEqual(timestamp_obj.year, 2023)
60+
self.assertEqual(timestamp_obj.month, 12)
61+
self.assertEqual(timestamp_obj.day, 25)
62+
self.assertEqual(timestamp_obj.hour, 14)
63+
self.assertEqual(timestamp_obj.minute, 30)
64+
self.assertEqual(timestamp_obj.second, 45)
65+
66+
def test_binary_constructor(self):
67+
"""Test Binary type constructor"""
68+
# Test with string
69+
binary_obj = self.dbapi.Binary("test string")
70+
self.assertIsInstance(binary_obj, bytes)
71+
self.assertEqual(binary_obj, b"test string")
72+
73+
# Test with bytes
74+
binary_obj2 = self.dbapi.Binary(b"test bytes")
75+
self.assertIsInstance(binary_obj2, bytes)
76+
self.assertEqual(binary_obj2, b"test bytes")
77+
78+
# Test with list
79+
binary_obj3 = self.dbapi.Binary([65, 66, 67])
80+
self.assertIsInstance(binary_obj3, bytes)
81+
self.assertEqual(binary_obj3, b"ABC")
82+
83+
def test_tick_constructors(self):
84+
"""Test constructors that work with timestamps"""
85+
import time
86+
87+
# Get current timestamp
88+
current_time = time.time()
89+
90+
# Test DateFromTicks
91+
date_from_ticks = self.dbapi.DateFromTicks(current_time)
92+
self.assertIsInstance(date_from_ticks, datetime.date)
93+
94+
# Test TimeFromTicks
95+
time_from_ticks = self.dbapi.TimeFromTicks(current_time)
96+
self.assertIsInstance(time_from_ticks, datetime.time)
97+
98+
# Test TimestampFromTicks
99+
timestamp_from_ticks = self.dbapi.TimestampFromTicks(current_time)
100+
self.assertIsInstance(timestamp_from_ticks, datetime.datetime)
101+
102+
def test_type_objects(self):
103+
"""Test type objects for type comparison"""
104+
self.assertEqual(self.dbapi.STRING, str)
105+
self.assertEqual(self.dbapi.BINARY, bytes)
106+
self.assertEqual(self.dbapi.NUMBER, (int, float))
107+
self.assertEqual(self.dbapi.ROWID, int)
108+
self.assertIsNotNone(self.dbapi.DATETIME)
109+
110+
def test_connect_method_exists(self):
111+
"""Test that connect method exists"""
112+
self.assertTrue(hasattr(self.dbapi, 'connect'))
113+
self.assertTrue(callable(self.dbapi.connect))
114+
115+
116+
if __name__ == '__main__':
117+
unittest.main()

tests/test_dialect.py

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
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

Comments
 (0)