from __future__ import annotations from pathlib import Path ROOT = Path(__file__).resolve().parents[1] MIGRATION = ( ROOT / "migrations" / "versions" / "20260723_110_ai_data_rules.py" ) EXPECTED_TABLES = { "data_rules", "data_rule_versions", "rule_generation_runs", "data_standards", "data_standard_versions", "standard_rule_bindings", "dataflow_versions", "dataflow_component_bindings", "rule_execution_plans", "rule_artifacts", "dataflow_deployments", "rule_runs", "rule_violation_samples", } def test_ai_data_rule_migration_defines_all_three_domain_layers(): source = MIGRATION.read_text(encoding="utf-8") assert 'revision = "20260723_110"' in source assert 'down_revision = "20260722_110"' in source for table in EXPECTED_TABLES: assert f"CREATE TABLE public.{table}" in source def test_ai_data_rule_migration_enforces_immutable_versions_and_fixed_bindings(): source = MIGRATION.read_text(encoding="utf-8") for expected in ( "UNIQUE (rule_uid, version_no)", "UNIQUE (standard_uid, version_no)", "UNIQUE (dataflow_uid, version_no)", "standard_version_id UUID NOT NULL", "rule_version_id UUID NOT NULL", "dataflow_version_id UUID NOT NULL", "component_kind VARCHAR(30) NOT NULL", "package_hash CHAR(64)", "plan_hash CHAR(64)", "artifact_digest CHAR(64)", "context_hash CHAR(64)", "candidate_hash CHAR(64)", "workflow_version_id UUID", ): assert expected in source def test_ai_data_rule_migration_is_forward_preserving(): source = MIGRATION.read_text(encoding="utf-8") downgrade = source.split("def downgrade()", 1)[1] assert "DROP TABLE" not in downgrade.upper() assert "pass" in downgrade