`alembic/env.py` took its target from `get_settings().database_url`, composed from `.env`'s production `POSTGRES_*` values, so the most natural command in the repo — a bare `alembic current` — talked to production. `-x target=test` and `-x target=production` now name the database explicitly; anything else exits non-zero before any engine is built, and the one line it prints before migrating never shows a credential. `run_alembic` (tests/conftest.py) passes `-x target=test` and sets `TEST_DATABASE_URL` in the subprocess environment instead of overriding `POSTGRES_*` — the `_components()` shim is gone; nothing else used it. `make migrate-test`, `make migrate-production` and `make dev-test-server` are the three documented ways to reach either database now; `dev-test-server` refuses to start if the resolved database is not `core_api_test`. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
50 lines
1.8 KiB
Python
50 lines
1.8 KiB
Python
"""Tests for core-api T-3: alembic refuses to run without a named target.
|
|
|
|
`alembic/env.py` resolves `-x target=test` or `-x target=production`; no
|
|
target, or anything else, must exit non-zero before any engine is built —
|
|
not skip, not fall back to the production settings a bare `alembic current`
|
|
used to reach by accident. Both tests point the *production* settings
|
|
(`POSTGRES_*`) at an unreachable host: if the refusal ever started
|
|
connecting before checking the target, these would hang (and the
|
|
subprocess timeout below would fire) instead of returning quickly.
|
|
"""
|
|
import os
|
|
import subprocess
|
|
|
|
from tests.conftest import ALEMBIC_BIN, REPO_ROOT
|
|
|
|
UNREACHABLE_HOST = "10.255.255.1" # non-routed; a real connection attempt would hang
|
|
|
|
|
|
def _run_with_unreachable_production(*args: str) -> subprocess.CompletedProcess:
|
|
env = os.environ.copy()
|
|
env["POSTGRES_HOST"] = UNREACHABLE_HOST
|
|
env["POSTGRES_USER"] = "probe"
|
|
env["POSTGRES_PASSWORD"] = "probe"
|
|
env["POSTGRES_DATABASE"] = "probe"
|
|
env.pop("TEST_DATABASE_URL", None)
|
|
return subprocess.run(
|
|
[str(ALEMBIC_BIN), *args],
|
|
cwd=str(REPO_ROOT),
|
|
env=env,
|
|
capture_output=True,
|
|
text=True,
|
|
timeout=10, # a real connection attempt to an unreachable host hangs well past this
|
|
)
|
|
|
|
|
|
def test_no_target_refuses_before_connecting():
|
|
result = _run_with_unreachable_production("current")
|
|
assert result.returncode != 0
|
|
combined = result.stdout + result.stderr
|
|
assert "target=test" in combined
|
|
assert "target=production" in combined
|
|
|
|
|
|
def test_nonsense_target_refuses_before_connecting():
|
|
result = _run_with_unreachable_production("-x", "target=nonsense", "current")
|
|
assert result.returncode != 0
|
|
combined = result.stdout + result.stderr
|
|
assert "target=test" in combined
|
|
assert "target=production" in combined
|