Files
ss-tools/backend/tests/services/test_structure_snapshot_diff.py
busya a32ca0631b feat(037): capture, verification lifecycle, inheritance + close 036 stabilization
- Authoritative candidate capture with server-issued artifacts and raw-byte
  immutability hashing (source_response_hash server-owned)
- Closed-period lifecycle: request-hash bound approvals, persisted closure
  immutability violations, byte-for-byte catalog stability on reclosure
- Verification runs: persisted VerificationRun model + FK migration,
  publish gate (block_publish), scheduled observability runs (02:00 UTC)
- FR-013 baseline inheritance: prior_release_id migration, plan_inheritance/
  execute_inheritance classification and re-extraction, API endpoints
- Visual executor bound to release-deployment environment; caller mismatch
  rejected; visual SSIM/reconciliation modules
- Query execution decomposed: envelope/model/executor split, no direct SQL
- AgentRun approvals extracted to submodule; evidence adapter; _utils
- Dashboard testing service decomposed into 30+ modules (all <400 LOC)
- Five Feature-037 agent tools with permission guards (tools_037.py)
- API readiness endpoint; Alembic env/migrations; test fixture repos
- Specs 036/037 contracts, openapi.yaml, schema.json, tasks/traceability
  updated; semantic index rebuilt with 0 parse warnings
- Fix ADR-0003 parser ambiguity: remove [DEF🆔ADR] prose example
- Add axiom-mcp-agent-feedback.md: agent findings for MCP rework plan
- Tests: 298 service + 1464 API + 45 agent passing; ruff clean
2026-07-31 11:28:50 +03:00

455 lines
18 KiB
Python

#region Test.BaselineEngine.StructureSnapshot.Diff [C:4] [TYPE Module] [SEMANTICS test,structure-snapshot,diff,metadata-verification]
# @defgroup Test.BaselineEngine.StructureSnapshot.Diff Diff-level tests for release-bound snapshot comparison.
# @LAYER Test
# @RELATION BINDS_TO -> [BaselineEngine.StructureSnapshot.Diff]
# @RELATION VERIFIES -> [BaselineEngine.StructureSnapshot.Diff]
# @TEST_EDGE wrong_dashboard_snapshot -> raises ValueError when snapshot metadata mismatches release request
# @TEST_EDGE omitted_semantic_dimensions -> diff covers dataset identity, chart dataset,
# filter dataset, type, column attributes, metric definition, access capability
# @TEST_EDGE missing_snapshot_file -> FileNotFoundError propagated
# @TEST_EDGE metadata_verification -> cross-verification of all 5 identity dimensions
# @INVARIANT Snapshot metadata is cross-verified before diff computation.
# @INVARIANT Mismatch raises ValueError, never produces a synthetic diff.
from __future__ import annotations
import json
from pathlib import Path
import pytest
from unittest.mock import MagicMock, patch
from src.models.dashboard_release import DashboardRelease
from src.models.deployment import DeploymentRecord
from src.models.git import GitRepository
from src.schemas.dashboard_testing import DashboardQueryModel
from src.schemas.dashboard_testing.structure_snapshot import (
ProvenanceEnvelope,
SnapshotDiffRequest,
SnapshotMetadataVerification,
)
from src.services.dashboard_testing.structure_snapshot_diff import diff_release_snapshots
# #region Test.StructureSnapshot.Diff [C:3] [TYPE Class]
class TestDiffReleaseSnapshots:
"""Tests for diff_release_snapshots()."""
def _make_envelope(self, **overrides) -> ProvenanceEnvelope:
"""Create a standard ProvenanceEnvelope with overridable defaults."""
defaults = {
"release_id": "rel-1",
"release_version": "v1.0.0",
"release_commit_hash": "a" * 40,
"repository_id": "repo-1",
"repository_key": "test-project",
"dashboard_id": 42,
"environment_id": "env-1",
}
defaults.update(overrides)
return ProvenanceEnvelope(**defaults)
def _write_provenance_snapshot(
self, tmp_path: Path, release_version: str, envelope: ProvenanceEnvelope,
dashboard_id: int = 42, environment_id: str = "env-1",
) -> Path:
"""Write a provenance-wrapped snapshot file and return its path."""
snap_dir = tmp_path / "snapshots"
snap_dir.mkdir(parents=True, exist_ok=True)
snap_path = snap_dir / f"{release_version}.json"
model = DashboardQueryModel(
environment_id=environment_id, dashboard_id=dashboard_id,
title="Test", query_model_fingerprint="sha256:fp",
)
payload = {
"provenance": envelope.model_dump(mode="json"),
"query_model": model.model_dump(mode="json"),
}
snap_path.write_text(json.dumps(payload))
return snap_path
# #region Test.StructureSnapshot.Diff.TestReleaseNotFound
def test_release_not_found_from(self):
"""Missing base release raises ValueError."""
db = MagicMock()
db.query.return_value.filter.return_value.first.return_value = None
with pytest.raises(ValueError, match="not found"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="nonexistent", release_id_to="rel-2"),
db=db,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestReleaseNotFoundTo
def test_release_not_found_to(self):
"""Missing target release raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, None
]
with pytest.raises(ValueError, match="not found"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="nonexistent"),
db=db,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestRepoNotFound
def test_repository_not_found_for_diff(self):
"""Missing GitRepository for release raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "missing-repo"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "missing-repo"
release_to.deployment_id = "dep-2"
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, None # Third call for GitRepository returns None
]
with pytest.raises(ValueError, match="not found"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestMetadataMismatchEnv
def test_metadata_mismatch_environment(self, tmp_path):
"""Snapshot environment_id mismatch raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "repo-1"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "repo-1"
release_to.deployment_id = "dep-2"
repo_record = MagicMock(spec=GitRepository)
repo_record.id = "repo-1"
repo_record.dashboard_id = 42
repo_record.local_path = "git_repos/test-project"
dep_from = MagicMock(spec=DeploymentRecord)
dep_from.id = "dep-1"
dep_from.environment_id = "env-1"
dep_from.commit_hash = "a" * 40
dep_to = MagicMock(spec=DeploymentRecord)
dep_to.id = "dep-2"
dep_to.environment_id = "env-2" # Different from from!
dep_to.commit_hash = "b" * 40
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, repo_record, dep_from, dep_to,
]
git_service = MagicMock()
git_service.legacy_base_path = str(tmp_path / "git_repos")
# Write provenance snapshots where envelope env matches model env but not dep env
env_from = self._make_envelope(environment_id="env-1")
env_to = self._make_envelope(
release_id="rel-2", release_version="v2.0.0",
release_commit_hash="b" * 40, environment_id="env-1", # envelope says env-1
)
snap_from = self._write_provenance_snapshot(tmp_path, "v1.0.0", env_from)
snap_to = self._write_provenance_snapshot(tmp_path, "v2.0.0", env_to)
with patch(
"src.services.dashboard_testing.structure_snapshot_diff.build_snapshot_path",
side_effect=[snap_from, snap_to],
), pytest.raises(ValueError, match="Snapshot metadata verification failed"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db, git_service=git_service,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestMetadataMismatchDashboard
def test_metadata_mismatch_dashboard_id(self, tmp_path):
"""Snapshot dashboard_id mismatch raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "repo-1"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "repo-1"
release_to.deployment_id = "dep-2"
repo_record = MagicMock(spec=GitRepository)
repo_record.id = "repo-1"
repo_record.dashboard_id = 42
repo_record.local_path = "git_repos/test-project"
dep_from = MagicMock(spec=DeploymentRecord)
dep_from.id = "dep-1"
dep_from.environment_id = "env-1"
dep_from.commit_hash = "a" * 40
dep_to = MagicMock(spec=DeploymentRecord)
dep_to.id = "dep-2"
dep_to.environment_id = "env-1"
dep_to.commit_hash = "b" * 40
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, repo_record, dep_from, dep_to,
]
git_service = MagicMock()
git_service.legacy_base_path = str(tmp_path / "git_repos")
# Envelope and model both say different dashboard_id
env_from = self._make_envelope(dashboard_id=42)
env_to = self._make_envelope(
release_id="rel-2", release_version="v2.0.0",
release_commit_hash="b" * 40, dashboard_id=99, # dash mismatch
)
snap_from = self._write_provenance_snapshot(tmp_path, "v1.0.0", env_from, dashboard_id=42)
snap_to = self._write_provenance_snapshot(tmp_path, "v2.0.0", env_to, dashboard_id=99)
with patch(
"src.services.dashboard_testing.structure_snapshot_diff.build_snapshot_path",
side_effect=[snap_from, snap_to],
), pytest.raises(ValueError, match="Snapshot metadata verification failed"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db, git_service=git_service,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestCommitMismatch
def test_commit_hash_mismatch(self, tmp_path):
"""Deployment commit_hash mismatch with release raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "repo-1"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "repo-1"
release_to.deployment_id = "dep-2"
repo_record = MagicMock(spec=GitRepository)
repo_record.id = "repo-1"
repo_record.dashboard_id = 42
repo_record.local_path = "git_repos/test-project"
dep_from = MagicMock(spec=DeploymentRecord)
dep_from.id = "dep-1"
dep_from.environment_id = "env-1"
dep_from.commit_hash = "a" * 40
dep_to = MagicMock(spec=DeploymentRecord)
dep_to.id = "dep-2"
dep_to.environment_id = "env-1"
dep_to.commit_hash = "c" * 40 # doesn't match release commit b*40
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, repo_record, dep_from, dep_to,
]
git_service = MagicMock()
git_service.legacy_base_path = str(tmp_path / "git_repos")
env_from = self._make_envelope(release_commit_hash="a" * 40)
env_to = self._make_envelope(
release_id="rel-2", release_version="v2.0.0",
release_commit_hash="b" * 40,
)
snap_from = self._write_provenance_snapshot(tmp_path, "v1.0.0", env_from)
snap_to = self._write_provenance_snapshot(tmp_path, "v2.0.0", env_to)
with patch(
"src.services.dashboard_testing.structure_snapshot_diff.build_snapshot_path",
side_effect=[snap_from, snap_to],
), pytest.raises(ValueError, match="Snapshot metadata verification failed"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db, git_service=git_service,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestLegacySnapshotRejected
def test_legacy_snapshot_without_provenance_rejected(self, tmp_path):
"""Legacy snapshot without provenance raises ValueError."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "repo-1"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "repo-1"
release_to.deployment_id = "dep-2"
repo_record = MagicMock(spec=GitRepository)
repo_record.id = "repo-1"
repo_record.dashboard_id = 42
repo_record.local_path = "git_repos/test-project"
dep_from = MagicMock(spec=DeploymentRecord)
dep_from.id = "dep-1"
dep_from.environment_id = "env-1"
dep_from.commit_hash = "a" * 40
dep_to = MagicMock(spec=DeploymentRecord)
dep_to.id = "dep-2"
dep_to.environment_id = "env-1"
dep_to.commit_hash = "b" * 40
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, repo_record, dep_from, dep_to,
]
git_service = MagicMock()
git_service.legacy_base_path = str(tmp_path / "git_repos")
# Write a LEGACY snapshot (bare model, no provenance)
snap_dir = tmp_path / "snapshots"
snap_dir.mkdir(parents=True, exist_ok=True)
snap_path = snap_dir / "v1.0.0.json"
model = DashboardQueryModel(
environment_id="env-1", dashboard_id=42, title="Legacy",
query_model_fingerprint="sha256:legacy",
)
snap_path.write_text(json.dumps(model.model_dump(mode="json")))
with patch(
"src.services.dashboard_testing.structure_snapshot_diff.build_snapshot_path",
return_value=snap_path,
), pytest.raises(ValueError, match="provenance"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db, git_service=git_service,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestRepositoryMismatch
def test_repository_mismatch(self, tmp_path):
"""Cross-repo releases raise ValueError (same-repo contract)."""
release_from = MagicMock(spec=DashboardRelease)
release_from.id = "rel-1"
release_from.version = "v1.0.0"
release_from.commit_hash = "a" * 40
release_from.repository_id = "repo-1"
release_from.deployment_id = "dep-1"
release_to = MagicMock(spec=DashboardRelease)
release_to.id = "rel-2"
release_to.version = "v2.0.0"
release_to.commit_hash = "b" * 40
release_to.repository_id = "repo-2" # different repo!
release_to.deployment_id = "dep-2"
repo_record = MagicMock(spec=GitRepository)
repo_record.id = "repo-1"
repo_record.dashboard_id = 42
repo_record.local_path = "git_repos/test-project"
dep_from = MagicMock(spec=DeploymentRecord)
dep_from.id = "dep-1"
dep_from.environment_id = "env-1"
dep_from.commit_hash = "a" * 40
dep_to = MagicMock(spec=DeploymentRecord)
dep_to.id = "dep-2"
dep_to.environment_id = "env-1"
dep_to.commit_hash = "b" * 40
db = MagicMock()
db.query.return_value.filter.return_value.first.side_effect = [
release_from, release_to, repo_record, dep_from, dep_to,
]
git_service = MagicMock()
git_service.legacy_base_path = str(tmp_path / "git_repos")
# from has repo-1, to has repo-2 in envelope (cross-repo)
env_from = self._make_envelope(repository_id="repo-1")
env_to = self._make_envelope(
release_id="rel-2", release_version="v2.0.0",
release_commit_hash="b" * 40, repository_id="repo-2",
)
snap_from = self._write_provenance_snapshot(tmp_path, "v1.0.0", env_from)
snap_to = self._write_provenance_snapshot(tmp_path, "v2.0.0", env_to)
with patch(
"src.services.dashboard_testing.structure_snapshot_diff.build_snapshot_path",
side_effect=[snap_from, snap_to],
), pytest.raises(ValueError, match="Snapshot metadata verification failed"):
diff_release_snapshots(
SnapshotDiffRequest(release_id_from="rel-1", release_id_to="rel-2"),
db=db, git_service=git_service,
)
# #endregion
# #region Test.StructureSnapshot.Diff.TestMetadataVerificationSummary
def test_metadata_verification_summary(self):
"""SnapshotMetadataVerification summary correctly reports individual match status."""
# Test all_match is True when all dimensions match
v = SnapshotMetadataVerification(
environment_match=True,
dashboard_match=True,
repository_match=True,
release_match=True,
commit_match=True,
all_match=True,
)
assert v.all_match is True
# Test all_match is False when any dimension mismatches
v2 = SnapshotMetadataVerification(
environment_match=True,
dashboard_match=False,
repository_match=True,
release_match=True,
commit_match=True,
all_match=False,
details="Dashboard mismatch",
)
assert v2.all_match is False
assert v2.dashboard_match is False
# #endregion
# #endregion
#endregion Test.BaselineEngine.StructureSnapshot.Diff