Add the analysis role's execution path: a two-phase orchestration
(per-scenario diagnosis gathered in parallel, then a synthesis pass)
that reads the existing campaign report aggregation plus capped failure
samples, validates the LLM's JSON against the report schema, and strips
fabricated run/scenario references before persisting. Results upsert one
row per campaign (generating/completed/failed) with the model config
snapshot; GET/POST /api/campaigns/{id}/analysis expose the state machine,
guarding non-terminal campaigns and missing analysis models with 400s.
32 lines
1.2 KiB
Python
32 lines
1.2 KiB
Python
"""Verify the campaign_analyses migration creates the table with a unique campaign_id."""
|
|
|
|
import importlib
|
|
|
|
import sqlalchemy as sa
|
|
from alembic.migration import MigrationContext
|
|
from alembic.operations import Operations
|
|
|
|
|
|
def test_campaign_analyses_migration_creates_table(tmp_path, monkeypatch):
|
|
engine = sa.create_engine(f"sqlite:///{tmp_path / 'analyses.db'}")
|
|
|
|
with engine.begin() as connection:
|
|
operations = Operations(MigrationContext.configure(connection))
|
|
migration = importlib.import_module(
|
|
"migrations.versions.c8f5e4b13d26_add_campaign_analyses"
|
|
)
|
|
monkeypatch.setattr(migration, "op", operations)
|
|
migration.upgrade()
|
|
|
|
inspector = sa.inspect(connection)
|
|
assert "campaign_analyses" in inspector.get_table_names()
|
|
|
|
columns = {c["name"] for c in inspector.get_columns("campaign_analyses")}
|
|
assert {
|
|
"id", "campaign_id", "status", "result",
|
|
"model_config_id", "error", "triggered_by", "created_at", "updated_at",
|
|
} <= columns
|
|
|
|
uniques = inspector.get_unique_constraints("campaign_analyses")
|
|
assert any(uc["column_names"] == ["campaign_id"] for uc in uniques)
|