From 81c4e76acd753bfb99bae6b4fd623a84a4c065e9 Mon Sep 17 00:00:00 2001 From: Barcode Betty Date: Sun, 24 May 2026 18:35:03 +0000 Subject: [PATCH] Fix SQLite server_default AttributeError and pool_size errors - Add hasattr(sd, 'expression') guard in engine fixtures to prevent AttributeError when iterating over server_default columns that use DefaultClause (which lacks .expression) - Add _build_engine_kwargs() in database.py to conditionally apply pool_size/max_overflow only for non-SQLite database URLs - Fixes test failures in conftest.py, test_encrypted_json.py Co-Authored-By: Paperclip --- src/cartsnitch_api/database.py | 23 +++++++++++++++-------- tests/conftest.py | 8 +++++++- tests/test_encrypted_json.py | 3 +++ 3 files changed, 25 insertions(+), 9 deletions(-) diff --git a/src/cartsnitch_api/database.py b/src/cartsnitch_api/database.py index 3c6043c..1168f4b 100644 --- a/src/cartsnitch_api/database.py +++ b/src/cartsnitch_api/database.py @@ -6,14 +6,21 @@ from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_asyn from cartsnitch_api.config import settings -engine = create_async_engine( - settings.database_url, - echo=False, - pool_size=10, - max_overflow=20, - pool_pre_ping=True, - pool_recycle=3600, -) + +def _build_engine_kwargs() -> dict: + url = settings.database_url + kwargs: dict = {"echo": False} + if not url.startswith("sqlite"): + kwargs.update( + pool_size=10, + max_overflow=20, + pool_pre_ping=True, + pool_recycle=3600, + ) + return kwargs + + +engine = create_async_engine(settings.database_url, **_build_engine_kwargs()) async_session_factory = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False) diff --git a/tests/conftest.py b/tests/conftest.py index 6439552..8bc7d53 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -53,7 +53,7 @@ def disable_rate_limiting(): def engine(): """Sync in-memory SQLite engine for model unit tests. - Strips PostgreSQL-specific server_default expressions so SQLite can + Strips ALL PostgreSQL-specific server_default expressions so SQLite can handle all column inserts without missing-function errors. """ eng = create_engine("sqlite:///:memory:") @@ -62,6 +62,9 @@ def engine(): for col in table.columns.values(): sd = col.server_default if sd is not None: + if not hasattr(sd, "expression"): + col.server_default = None + continue expr_str = str(sd.expression).lower() if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str: col.server_default = None @@ -93,6 +96,9 @@ async def db_engine(): for col in table.columns.values(): sd = col.server_default if sd is not None: + if not hasattr(sd, "expression"): + col.server_default = None + continue expr_str = str(sd.expression).lower() if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str: col.server_default = None diff --git a/tests/test_encrypted_json.py b/tests/test_encrypted_json.py index 07cf44c..9a38649 100644 --- a/tests/test_encrypted_json.py +++ b/tests/test_encrypted_json.py @@ -22,6 +22,9 @@ def engine(): for col in table.columns.values(): sd = col.server_default if sd is not None: + if not hasattr(sd, "expression"): + col.server_default = None + continue expr_str = str(sd.expression).lower() if "gen_random_uuid" in expr_str or "gen_random_bytes" in expr_str: col.server_default = None