"""Tests for `migrations.url_resolution` — the Alembic database-URL resolver. Regression coverage for the bug where `resolve_database_url` read `app.config.Settings().sqlite_path` (which always has a default value) ahead of the Alembic `ini_url`, so `ini_url` — including a `sqlalchemy.url` set programmatically via `Config.set_main_option(...)`, as integration tests do — was unreachable dead code and migrations always ran against `./foraldraledig.db` regardless of what the caller asked for. See `migrations/env.py`'s `_resolve_url` for how this plugs into Alembic. """ from migrations.url_resolution import coerce_sqlite_async, resolve_database_url class TestResolveDatabaseUrl: def test_app_sqlite_path_set_wins_over_ini_url(self, monkeypatch) -> None: monkeypatch.setenv("APP_SQLITE_PATH", "/tmp/scratch/other.db") url = resolve_database_url(x_args={}, ini_url="sqlite:///./foraldraledig.db") assert url == "sqlite:////tmp/scratch/other.db" def test_app_sqlite_path_unset_falls_back_to_ini_url(self, monkeypatch) -> None: monkeypatch.delenv("APP_SQLITE_PATH", raising=False) url = resolve_database_url(x_args={}, ini_url="sqlite:///./foraldraledig.db") assert url == "sqlite:///./foraldraledig.db" def test_app_sqlite_path_empty_string_treated_as_unset(self, monkeypatch) -> None: monkeypatch.setenv("APP_SQLITE_PATH", "") url = resolve_database_url(x_args={}, ini_url="sqlite:///./ini-fallback.db") assert url == "sqlite:///./ini-fallback.db" def test_x_argument_wins_over_env_and_ini(self, monkeypatch) -> None: monkeypatch.setenv("APP_SQLITE_PATH", "/tmp/scratch/other.db") url = resolve_database_url( x_args={"sqlalchemy.url": "sqlite:///./explicit-scratch.db"}, ini_url="sqlite:///./foraldraledig.db", ) assert url == "sqlite:///./explicit-scratch.db" def test_ini_url_used_when_env_var_unset(self, monkeypatch) -> None: monkeypatch.delenv("APP_SQLITE_PATH", raising=False) url = resolve_database_url(x_args={}, ini_url="sqlite:///./ini-fallback.db") assert url == "sqlite:///./ini-fallback.db" def test_regression_programmatic_ini_url_wins_over_app_default(self, monkeypatch) -> None: """The exact real-world scenario that regressed: a caller sets `sqlalchemy.url` programmatically on the Alembic `Config` (as `tests/integration/test_migration_004_backfill.py` does) and no `APP_SQLITE_PATH` is set. This must resolve to that ini value, NOT to `app.config.Settings`'s default of `./foraldraledig.db`. """ monkeypatch.delenv("APP_SQLITE_PATH", raising=False) url = resolve_database_url(x_args={}, ini_url="sqlite:////tmp/whatever.db") assert url == "sqlite:////tmp/whatever.db" assert url != "sqlite:///./foraldraledig.db" class TestCoerceSqliteAsync: def test_sqlite_url_coerced_to_aiosqlite(self) -> None: assert ( coerce_sqlite_async("sqlite:///./foraldraledig.db") == "sqlite+aiosqlite:///./foraldraledig.db" ) def test_resolved_env_url_is_coerced(self, monkeypatch) -> None: monkeypatch.setenv("APP_SQLITE_PATH", "/tmp/scratch/other.db") url = coerce_sqlite_async(resolve_database_url(x_args={}, ini_url=None)) assert url == "sqlite+aiosqlite:////tmp/scratch/other.db" def test_non_sqlite_url_left_untouched(self) -> None: postgres_url = "postgresql+asyncpg://user:pass@host/db" assert coerce_sqlite_async(postgres_url) == postgres_url