Files
ss-tools/backend/tests/integration/test_alembic_user_id_migration.py
root 632b730fff chore: migrate GRACE-Poly anchors to hierarchical dotted naming
Systematic rename of all semantic anchors (#region, [DEF], @RELATION)
across 1400+ files — backend Python, frontend Svelte/TS, specs, docs:
- Flat anchors become Namespace.Module.Entity
- @RELATION references updated to match new anchor paths
- Zero business logic changes
2026-07-22 11:48:15 +03:00

80 lines
3.8 KiB
Python

# #region Test.Alembic.TaskRecordsUserId [C:3] [TYPE Module] [SEMANTICS test,alembic,migration,task,postgres]
# @BRIEF Verifies the real PostgreSQL upgrade that adds task_records.user_id.
# @RELATION BINDS_TO -> [EXT:Alembic:MigrationChain]
# @TEST_FIXTURE: migration_revisions -> INLINE_JSON
# @TEST_EDGE: missing_field -> Pre-user-id schema does not expose task_records.user_id.
# @TEST_EDGE: invalid_type -> Upgrade is addressed by a concrete Alembic revision.
# @TEST_EDGE: external_fail -> PostgreSQL DDL failures propagate instead of being masked.
# @TEST_INVARIANT: task_records_user_id_upgrade -> VERIFIED_BY: [Test.Alembic.TestUserIdMigrationAddsColumn]
# @RATIONALE SQLite and metadata.create_all() cannot expose an unapplied PostgreSQL migration;
# an isolated Testcontainers database exercises Alembic's actual DDL path.
# @REJECTED Stamping the revision or applying raw ALTER TABLE was rejected because each can
# conceal a migration chain failure while falsely marking the schema current.
import os
from pathlib import Path
import pytest
from sqlalchemy import create_engine, inspect
# #region Test.Alembic.MigrationDatabaseUrl [C:2] [TYPE Function]
# @BRIEF Provide a pristine isolated database created via db_factory in the shared PG container.
# @POST Returns a host-accessible connection URL for an empty database.
# @RATIONALE Uses db_factory from the shared Postgres container instead of starting
# a separate PostgreSQL container. This is faster and uses fewer Docker resources.
# The database is created as a fresh, empty database — no tables exist, no Alembic
# has been run. This preserves the pristine-schema semantics required for testing
# Alembic migrations from a clean state.
# @REJECTED Starting a separate PostgresContainer was rejected — it adds ~5s per test
# and unnecessary Docker overhead when the shared container is already running.
@pytest.fixture
def migration_database_url(db_factory) -> str:
"""Provide a pristine PostgreSQL database from the shared container via db_factory.
Returns a host-accessible Postgres URL. The database is empty (no tables).
"""
result = db_factory["create_db"]("_alembic_test")
return result["host_url"]
# #endregion Test.Alembic.MigrationDatabaseUrl
# #region Test.Alembic.TestUserIdMigrationAddsColumn [C:2] [TYPE Function] [SEMANTICS test,alembic,migration,task,postgres]
# @BRIEF Upgrades a pristine PostgreSQL schema through the task_records.user_id revision.
def test_user_id_migration_adds_column(monkeypatch: pytest.MonkeyPatch, migration_database_url: str) -> None:
"""Alembic adds user_id after the predecessor revision without runtime schema creation."""
monkeypatch.setenv("DATABASE_URL", migration_database_url)
_run_alembic_upgrade("b2a3c4d5e6f7")
engine = create_engine(migration_database_url)
try:
before_columns = {column["name"] for column in inspect(engine).get_columns("task_records")}
assert "user_id" not in before_columns
_run_alembic_upgrade("c3d4e5f6a7b8")
after_columns = {column["name"] for column in inspect(engine).get_columns("task_records")}
assert "user_id" in after_columns
finally:
engine.dispose()
# #endregion Test.Alembic.TestUserIdMigrationAddsColumn
# #region _run_alembic_upgrade [C:1] [TYPE Function]
def _run_alembic_upgrade(revision: str) -> None:
"""Run a named revision against the DATABASE_URL injected for this test."""
from alembic.config import Config
from alembic import command
backend_dir = Path(__file__).parents[2]
config = Config(str(backend_dir / "alembic.ini"))
config.set_main_option("script_location", str(backend_dir / "alembic"))
config.set_main_option("sqlalchemy.url", os.environ["DATABASE_URL"])
command.upgrade(config, revision)
# #endregion _run_alembic_upgrade
# #endregion Test.Alembic.TaskRecordsUserId