# Test Model: Infrastructure & Bootstrap Feature: Application initialization, configuration, authentication client, and transaction management. These tests validate the plumbing layer that supports the domain and application layers. ## Unit Test Cases ### App Bootstrap #### TC-UNIT-501: Lifespan — Init and Close - **Type:** Positive - **Layer:** Unit - **File:** `unit/test_main.py::test_lifespan` - **Preconditions:** Mock `init_db` and `close_db` - **Steps:** Enter and exit lifespan context manager - **Expected:** - `init_db` called once on enter - `close_db` called once on exit - `close_db` not called during context - **Last Verified:** 2026-05-07 #### TC-UNIT-502: App Factory — Creates FastAPI App - **Type:** Positive - **Layer:** Unit - **File:** `unit/test_main.py::test_app_factory` - **Expected:** Returns `FastAPI` instance - **Last Verified:** 2026-05-07 #### TC-UNIT-503: App Factory — Has Routes - **Type:** Positive - **Layer:** Unit - **File:** `unit/test_main.py::test_app_factory_has_routes` - **Expected:** `/health` route exists; API routes registered - **Last Verified:** 2026-05-07 #### TC-UNIT-504: Main — Starts Uvicorn - **Type:** Positive - **Layer:** Unit - **File:** `unit/test_main.py::test_main` - **Preconditions:** Mock `uvicorn.run` - **Expected:** - `uvicorn.run` called with `factory=True` - `host="0.0.0.0"`, `port=8000` - **Last Verified:** 2026-05-07 ### Configuration #### TC-UNIT-601: Settings — Default Values - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_default_values` - **Expected:** - `app.name == "Blog API"` - `app.debug is False` - `database_url == "sqlite+aiosqlite:///./blog.db"` (dev default) - `environment == Environment.DEV` - **Last Verified:** 2026-05-07 #### TC-UNIT-602: Settings — Custom Values - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_custom_values` - **Expected:** Custom app, db, and env values applied correctly - **Last Verified:** 2026-05-07 #### TC-UNIT-603: Settings — is_dev / is_prod Properties - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_is_dev_property`, `test_is_prod_property` - **Expected:** Boolean properties match environment enum - **Last Verified:** 2026-05-07 #### TC-UNIT-604: Settings — Prod Requires Security Secret - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_prod_requires_security_secret` - **Expected:** Raises `ValueError` with `SECURITY_SECRET_KEY` when secret is empty in prod - **Last Verified:** 2026-05-07 #### TC-UNIT-605: Settings — Prod Requires KC Secret - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_prod_requires_kc_secret` - **Expected:** Raises `ValueError` with `KC_CLIENT_SECRET` when KC secret is empty in prod - **Last Verified:** 2026-05-07 #### TC-UNIT-606: Settings — Database URL Dev Default - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_database_url_dev_default` - **Expected:** Dev mode defaults to SQLite async URL - **Last Verified:** 2026-05-07 #### TC-UNIT-607: Settings — Database URL Prod Builds Postgres - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_database_url_prod_builds_postgres` - **Expected:** When `db.url` is None in prod, URL built from host/port/user/password/name - **Last Verified:** 2026-05-07 #### TC-UNIT-608: Settings — Database URL Override - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSettings::test_database_url_override` - **Expected:** Explicit `db.url` overrides auto-building in prod - **Last Verified:** 2026-05-07 #### TC-UNIT-609: AppConfig — Defaults - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestAppConfig::test_default_values` - **Expected:** Default name, debug, host, port values - **Last Verified:** 2026-05-07 #### TC-UNIT-610: DBConfig — Defaults - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestDBConfig::test_default_values` - **Expected:** Default PostgreSQL connection params - **Last Verified:** 2026-05-07 #### TC-UNIT-611: DBConfig — URL Validation (Postgres) - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestDBConfig::test_postgres_url_validation` - **Expected:** Postgres URL accepted - **Last Verified:** 2026-05-07 #### TC-UNIT-612: DBConfig — URL Validation (SQLite) - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestDBConfig::test_sqlite_url_validation` - **Expected:** SQLite URL accepted - **Last Verified:** 2026-05-07 #### TC-UNIT-613: DBConfig — URL Validation Rejects Invalid - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestDBConfig::test_invalid_url_validation` - **Expected:** Raises `ValueError` for non-SQLite/non-Postgres URLs - **Last Verified:** 2026-05-07 #### TC-UNIT-614: KCConfig — Defaults - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestKCConfig::test_default_values` - **Expected:** Default Keycloak server, realm, client settings - **Last Verified:** 2026-05-07 #### TC-UNIT-615: KCConfig — is_configured With Secret - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestKCConfig::test_is_configured_with_secret` - **Expected:** `is_configured is True` when `client_secret` is set - **Last Verified:** 2026-05-07 #### TC-UNIT-616: KCConfig — is_configured Without Secret - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestKCConfig::test_is_configured_without_secret` - **Expected:** `is_configured is False` when `client_secret` is empty - **Last Verified:** 2026-05-07 #### TC-UNIT-617: SecurityConfig — Defaults - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSecurityConfig::test_default_values` - **Expected:** Default token expiration (30 min) - **Last Verified:** 2026-05-07 #### TC-UNIT-618: SecurityConfig — is_configured - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestSecurityConfig::test_is_configured_with_secret`, `test_is_configured_without_secret` - **Expected:** `is_configured` reflects secret presence - **Last Verified:** 2026-05-07 #### TC-UNIT-619: Environment Enum — Values - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_config.py::TestEnvironment::test_dev_value`, `test_prod_value` - **Expected:** `DEV.value == "dev"`, `PROD.value == "prod"` - **Last Verified:** 2026-05-07 ### Authentication Client #### TC-UNIT-701: TokenInfo — Valid Token - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestTokenInfo::test_token_info_valid` - **Expected:** `is_valid is True`, all fields populated - **Last Verified:** 2026-05-07 #### TC-UNIT-702: TokenInfo — Inactive Token - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestTokenInfo::test_token_info_invalid_not_active` - **Expected:** `is_valid is False` - **Last Verified:** 2026-05-07 #### TC-UNIT-703: TokenInfo — Missing user_id - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestTokenInfo::test_token_info_invalid_no_user_id` - **Expected:** `is_valid is False` - **Last Verified:** 2026-05-07 #### TC-UNIT-704: TokenInfo — Empty Roles - **Type:** Edge - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestTokenInfo::test_token_info_empty_roles` - **Expected:** `is_valid is True`, roles is empty list - **Last Verified:** 2026-05-07 #### TC-UNIT-705: KeycloakUser — Creation - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakUser::test_keycloak_user_creation` - **Expected:** All fields stored correctly - **Last Verified:** 2026-05-07 #### TC-UNIT-706: KeycloakUser — Defaults - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakUser::test_keycloak_user_defaults` - **Expected:** Optional fields default to empty strings / lists - **Last Verified:** 2026-05-07 #### TC-UNIT-707: KeycloakAuthClient — Initialization - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_client_initialization` - **Expected:** Base URL and credentials set from settings - **Last Verified:** 2026-05-07 #### TC-UNIT-708: KeycloakAuthClient — Introspection URL - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_get_introspection_url` - **Expected:** URL built from settings (server_url, realm) - **Last Verified:** 2026-05-07 #### TC-UNIT-709: KeycloakAuthClient — Userinfo URL - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_get_userinfo_url` - **Expected:** URL built from settings - **Last Verified:** 2026-05-07 #### TC-UNIT-710: KeycloakAuthClient — Introspect Success - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_introspect_token_success` - **Preconditions:** Mock `httpx.AsyncClient` with active token response - **Expected:** Returns `TokenInfo` with active=True, roles parsed from realm_access - **Last Verified:** 2026-05-07 #### TC-UNIT-711: KeycloakAuthClient — Introspect Inactive Token - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_introspect_token_inactive` - **Expected:** Returns `TokenInfo` with active=False, is_valid=False - **Last Verified:** 2026-05-07 #### TC-UNIT-712: KeycloakAuthClient — Introspect HTTP Error - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_introspect_token_http_error` - **Expected:** Returns inactive `TokenInfo` (graceful degradation) - **Last Verified:** 2026-05-07 #### TC-UNIT-713: KeycloakAuthClient — Introspect Uses Cache - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_introspect_token_uses_cache` - **Expected:** Second call with same token uses cache; HTTP client called only once - **Last Verified:** 2026-05-07 #### TC-UNIT-714: KeycloakAuthClient — Get Userinfo Success - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_get_userinfo_success` - **Expected:** Returns `KeycloakUser` with all profile fields - **Last Verified:** 2026-05-07 #### TC-UNIT-715: KeycloakAuthClient — Get Userinfo Error - **Type:** Negative - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_get_userinfo_error` - **Expected:** Returns `None` on HTTP error - **Last Verified:** 2026-05-07 #### TC-UNIT-716: KeycloakAuthClient — Introspect Without Realm Roles - **Type:** Edge - **Layer:** Unit - **File:** `unit/infrastructure/test_auth.py::TestKeycloakAuthClient::test_introspect_token_no_realm_roles` - **Expected:** Returns active token with empty roles list - **Last Verified:** 2026-05-07 ### Transaction Manager #### TC-UNIT-801: SessionTransactionManager — Commit - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_transaction_manager.py::TestSessionTransactionManager::test_commit` - **Expected:** Calls `session.commit` once - **Last Verified:** 2026-05-07 #### TC-UNIT-802: SessionTransactionManager — Rollback - **Type:** Positive - **Layer:** Unit - **File:** `unit/infrastructure/test_transaction_manager.py::TestSessionTransactionManager::test_rollback` - **Expected:** Calls `session.rollback` once - **Last Verified:** 2026-05-07 ## Coverage Summary | Component | Cases | Status | |-----------|-------|--------| | App Bootstrap | 4 | ✅ Lifespan, factory, routes, main entry | | Settings & Config | 19 | ✅ Defaults, overrides, validation, env checks | | Keycloak Auth Client | 16 | ✅ Token introspection, userinfo, caching, errors | | Transaction Manager | 2 | ⚠️ Only commit/rollback; missing nested tx, error handling | ## Gaps (Not Yet Covered) - [ ] TC-UNIT-803: Transaction Manager — rollback on exception - [ ] TC-UNIT-804: Transaction Manager — nested transaction behavior - [ ] TC-UNIT-805: KeycloakAuthClient — cache expiration (TTL) - [ ] TC-UNIT-806: KeycloakAuthClient — cache key isolation per token - [ ] TC-UNIT-807: Settings — prod database URL building with missing components - [ ] TC-UNIT-808: App Factory — CORS middleware configuration - [ ] TC-UNIT-809: App Factory — static files mounting - [ ] TC-UNIT-810: App Factory — error handler registration