86 lines
3.7 KiB
Python
86 lines
3.7 KiB
Python
"""Verify durable Campaign child-Run identity migration semantics."""
|
|
|
|
import importlib
|
|
|
|
import sqlalchemy as sa
|
|
from alembic.migration import MigrationContext
|
|
from alembic.operations import Operations
|
|
|
|
|
|
def _legacy_eval_runs(connection) -> None:
|
|
connection.execute(sa.text("CREATE TABLE campaigns (id VARCHAR PRIMARY KEY)"))
|
|
connection.execute(
|
|
sa.text(
|
|
"CREATE TABLE eval_runs ("
|
|
"id VARCHAR PRIMARY KEY, campaign_id VARCHAR NULL, "
|
|
"status VARCHAR NOT NULL)"
|
|
)
|
|
)
|
|
connection.execute(sa.text("INSERT INTO eval_runs (id, campaign_id, status) VALUES ('old', NULL, 'completed')"))
|
|
|
|
|
|
def test_campaign_run_identity_migration_is_nullable_unique_and_reversible(tmp_path):
|
|
engine = sa.create_engine(f"sqlite:///{tmp_path / 'campaign-run-identity.db'}")
|
|
migration = importlib.import_module(
|
|
"migrations.versions.c2f4a6b8d0e1_add_campaign_run_identity"
|
|
)
|
|
|
|
with engine.begin() as connection:
|
|
_legacy_eval_runs(connection)
|
|
operations = Operations(MigrationContext.configure(connection))
|
|
migration.op = operations
|
|
migration.upgrade()
|
|
|
|
inspector = sa.inspect(connection)
|
|
columns = {column["name"]: column for column in inspector.get_columns("eval_runs")}
|
|
assert columns["campaign_plan_index"]["nullable"] is True
|
|
assert columns["campaign_occurrence_index"]["nullable"] is True
|
|
assert connection.execute(sa.text("SELECT campaign_plan_index, campaign_occurrence_index FROM eval_runs WHERE id='old'")) .one() == (None, None)
|
|
indexes = inspector.get_indexes("eval_runs")
|
|
assert any(
|
|
index["name"] == "uq_eval_runs_campaign_occurrence"
|
|
and bool(index["unique"])
|
|
and index["column_names"] == ["campaign_id", "campaign_plan_index", "campaign_occurrence_index"]
|
|
for index in indexes
|
|
)
|
|
|
|
connection.execute(
|
|
sa.text(
|
|
"INSERT INTO eval_runs (id, campaign_id, campaign_plan_index, campaign_occurrence_index, status) "
|
|
"VALUES ('child-1', 'campaign-1', 0, 0, 'pending')"
|
|
)
|
|
)
|
|
try:
|
|
connection.execute(
|
|
sa.text(
|
|
"INSERT INTO eval_runs (id, campaign_id, campaign_plan_index, campaign_occurrence_index, status) "
|
|
"VALUES ('child-duplicate', 'campaign-1', 0, 0, 'pending')"
|
|
)
|
|
)
|
|
except sa.exc.IntegrityError:
|
|
pass
|
|
else: # pragma: no cover - assertion guard
|
|
raise AssertionError("Campaign child identity must be unique")
|
|
|
|
migration.downgrade()
|
|
assert "campaign_plan_index" not in {column["name"] for column in sa.inspect(connection).get_columns("eval_runs")}
|
|
assert "campaign_occurrence_index" not in {column["name"] for column in sa.inspect(connection).get_columns("eval_runs")}
|
|
|
|
|
|
def test_fresh_schema_exposes_same_identity_columns_and_unique_index(tmp_path):
|
|
"""SQLModel's fresh-database path matches the Alembic contract."""
|
|
from agenteval.storage.db import EvalRunDB # noqa: F401 - register table
|
|
from sqlmodel import SQLModel, create_engine
|
|
|
|
engine = create_engine(f"sqlite:///{tmp_path / 'fresh.db'}")
|
|
SQLModel.metadata.create_all(engine)
|
|
inspector = sa.inspect(engine)
|
|
columns = {column["name"] for column in inspector.get_columns("eval_runs")}
|
|
assert {"campaign_plan_index", "campaign_occurrence_index"} <= columns
|
|
assert any(
|
|
index["name"] == "uq_eval_runs_campaign_occurrence"
|
|
and bool(index["unique"])
|
|
and index["column_names"] == ["campaign_id", "campaign_plan_index", "campaign_occurrence_index"]
|
|
for index in inspector.get_indexes("eval_runs")
|
|
)
|