chore: commit remaining workspace updates
This commit is contained in:
@@ -434,7 +434,12 @@ class TestManagerLifecycleDelegates:
|
||||
mgr.lifecycle.resume_task_with_password = AsyncMock()
|
||||
mgr._add_log = AsyncMock()
|
||||
await mgr.resume_task_with_password("t1", {"db": "pass"})
|
||||
mgr.lifecycle.resume_task_with_password.assert_called_with("t1", {"db": "pass"}, add_log_callback=mgr._add_log)
|
||||
mgr.lifecycle.resume_task_with_password.assert_called_with(
|
||||
"t1", {"db": "pass"},
|
||||
add_log_callback=mgr._add_log,
|
||||
requester_user_id=None,
|
||||
allow_task_override=False,
|
||||
)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
|
||||
@@ -300,9 +300,9 @@ class TestWriteBackupManifest:
|
||||
zf.writestr("a.txt", "data")
|
||||
archive_path = Path(fname)
|
||||
manifest_path = write_backup_manifest(archive_path)
|
||||
# No .tmp file should remain
|
||||
tmp_files = list(archive_path.parent.glob("*.tmp"))
|
||||
assert len(tmp_files) == 0
|
||||
# Scope: only check for this specific manifest's .tmp file
|
||||
manifest_tmp = archive_path.with_suffix(".manifest.json.tmp")
|
||||
assert not manifest_tmp.exists(), f"Orphan .tmp file left: {manifest_tmp}"
|
||||
manifest_path.unlink()
|
||||
finally:
|
||||
os.unlink(fname)
|
||||
|
||||
72
backend/tests/integration/test_alembic_user_id_migration.py
Normal file
72
backend/tests/integration/test_alembic_user_id_migration.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# #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_user_id_migration_adds_column]
|
||||
# @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 migration_database_url [C:1] [TYPE Function]
|
||||
@pytest.fixture
|
||||
def migration_database_url() -> str:
|
||||
"""Provide a pristine PostgreSQL database that receives only Alembic DDL."""
|
||||
from testcontainers.postgres import PostgresContainer
|
||||
|
||||
with PostgresContainer(
|
||||
image="postgres:16-alpine",
|
||||
username="test",
|
||||
password="test",
|
||||
dbname="test_migrations",
|
||||
) as container:
|
||||
yield container.get_connection_url()
|
||||
# #endregion migration_database_url
|
||||
|
||||
|
||||
# #region test_user_id_migration_adds_column [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_user_id_migration_adds_column
|
||||
|
||||
|
||||
# #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
|
||||
@@ -1,6 +1,6 @@
|
||||
# #region TestAlembicMigrations [C:3] [TYPE Module] [SEMANTICS tests, alembic, migration, schema, idempotent]
|
||||
# @BRIEF Verifies Alembic migration chain: fresh upgrade and legacy upgrade.
|
||||
# @RELATION BINDS_TO -> [ed310b33f02c]
|
||||
# #region Test.Alembic.Migrations [C:3] [TYPE Module] [SEMANTICS test,alembic,migration,schema,postgres]
|
||||
# @BRIEF Verifies fresh, legacy, and targeted PostgreSQL Alembic upgrades.
|
||||
# @RELATION BINDS_TO -> [Alembic.AddDeploymentRecords]
|
||||
# @INVARIANT Running `alembic upgrade head` twice is idempotent (no-op on second run) on PostgreSQL.
|
||||
# @INVARIANT Legacy database with existing tables can be upgraded via `alembic upgrade head`.
|
||||
# @PRE Alembic is installed, migration files exist in alembic/versions/.
|
||||
@@ -14,10 +14,9 @@
|
||||
# for legacy databases, ensuring all missing tables/columns are created.
|
||||
|
||||
import os
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
# Ensure backend/src is importable for model metadata
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
|
||||
@@ -38,9 +37,7 @@ _REQUIRES_PG = pytest.mark.skipif(
|
||||
@_REQUIRES_PG
|
||||
def test_fresh_upgrade_creates_tables() -> None:
|
||||
"""Fresh Alembic upgrade creates all expected tables (PostgreSQL only)."""
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from sqlalchemy import text as sa_text
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
from sqlalchemy import create_engine, inspect, text as sa_text
|
||||
_run_alembic_upgrade()
|
||||
engine = create_engine(os.environ["DATABASE_URL"])
|
||||
inspector = inspect(engine)
|
||||
@@ -73,11 +70,9 @@ def test_fresh_upgrade_creates_tables() -> None:
|
||||
@_REQUIRES_PG
|
||||
def test_legacy_database_upgrade() -> None:
|
||||
"""Alembic upgrade head works on a database that already has tables but no alembic_version."""
|
||||
from sqlalchemy import create_engine, inspect
|
||||
from sqlalchemy import text as sa_text
|
||||
from src.models.mapping import Base
|
||||
from src.models import auth as _auth # noqa: F401
|
||||
from src.models import task as _task # noqa: F401
|
||||
from sqlalchemy import create_engine, inspect, text as sa_text
|
||||
|
||||
from src.models import auth as _auth, task as _task # noqa: F401
|
||||
|
||||
engine = create_engine(os.environ["DATABASE_URL"])
|
||||
# Drop alembic_version if exists to simulate legacy state
|
||||
@@ -106,14 +101,16 @@ def test_legacy_database_upgrade() -> None:
|
||||
|
||||
|
||||
# #region _run_alembic_upgrade [C:1] [TYPE Function]
|
||||
def _run_alembic_upgrade() -> None:
|
||||
"""Run alembic upgrade head programmatically."""
|
||||
def _run_alembic_upgrade(revision: str = "head") -> None:
|
||||
"""Run a named Alembic upgrade programmatically against DATABASE_URL."""
|
||||
from alembic.config import Config as AlembicConfig
|
||||
|
||||
from alembic import command as alembic_command
|
||||
backend_dir = Path(__file__).parent.parent
|
||||
alembic_cfg = AlembicConfig(str(backend_dir / "alembic.ini"))
|
||||
alembic_cfg.set_main_option("script_location", str(backend_dir / "alembic"))
|
||||
alembic_command.upgrade(alembic_cfg, "head")
|
||||
alembic_cfg.set_main_option("sqlalchemy.url", os.environ["DATABASE_URL"])
|
||||
alembic_command.upgrade(alembic_cfg, revision)
|
||||
# #endregion _run_alembic_upgrade
|
||||
|
||||
# #endregion TestAlembicMigrations
|
||||
|
||||
Reference in New Issue
Block a user