"""Unit tests for DB pool configuration (SEC-14-04, phase 106, task 01). Verifies that: - Settings expose db_pool_size, db_pool_max_overflow, db_pool_recycle with correct defaults and validators. - create_engine() receives the pool kwargs from settings. """ from __future__ import annotations import contextlib import pytest from app.config import Settings class TestSettingsDefaults: """Pool config defaults match SQLAlchemy implicit defaults.""" def test_pool_size_default(self): assert Settings().db_pool_size == 5 def test_pool_max_overflow_default(self): assert Settings().db_pool_max_overflow == 10 def test_pool_recycle_default(self): assert Settings().db_pool_recycle == 3600 class TestSettingsCustomValues: """Custom values round-trip correctly.""" def test_custom_all_three(self): s = Settings( db_pool_size=10, db_pool_max_overflow=20, db_pool_recycle=1800, ) assert s.db_pool_size == 10 assert s.db_pool_max_overflow == 20 assert s.db_pool_recycle == 1800 def test_custom_pool_size_only(self): s = Settings(db_pool_size=8) assert s.db_pool_size == 8 assert s.db_pool_max_overflow == 10 assert s.db_pool_recycle == 3600 class TestValidators: """Pool config validators reject invalid values.""" def test_pool_size_zero_raises(self): with pytest.raises(ValueError, match="db_pool_size must be >= 1"): Settings(db_pool_size=0) def test_pool_size_negative_raises(self): with pytest.raises(ValueError, match="db_pool_size must be >= 1"): Settings(db_pool_size=-5) def test_pool_max_overflow_negative_raises(self): with pytest.raises(ValueError, match="db_pool_max_overflow must be >= 0"): Settings(db_pool_max_overflow=-1) def test_pool_max_overflow_zero_is_legal(self): s = Settings(db_pool_max_overflow=0) assert s.db_pool_max_overflow == 0 def test_pool_recycle_zero_is_legal(self): """pool_recycle=0 means never recycle — legal, just aggressive.""" s = Settings(db_pool_recycle=0) assert s.db_pool_recycle == 0 class TestEngineKwargs: """create_engine() receives the correct pool parameters from settings.""" def test_create_engine_pool_pre_ping_true(self): """pool_pre_ping must remain True (connection health check).""" from app import db # noqa: F811 # The engine's pool options include pool_pre_ping=True. # We verify by checking the pool's _pre_ping attribute. assert db.engine.pool._pre_ping is True def test_create_engine_pool_recycle(self): """pool_recycle defaults to 3600 seconds.""" from app import db # noqa: F811 assert db.engine.pool._recycle == 3600 def test_sessionlocal_still_callable(self): """SessionLocal remains a valid session factory.""" from app import db # noqa: F811 assert callable(db.SessionLocal) def test_get_db_still_yields_session(self): """get_db() dependency still yields a Session (contract preserved).""" from app import db # noqa: F811 gen = db.get_db() session = next(gen) assert isinstance(session, db.Session) session.close() # Generator cleanup with contextlib.suppress(StopIteration): next(gen)