Skip to content

Commit 7a2b885

Browse files
committed
test(identity): lock CR-03/CR-07 prompt-graph provenance contracts
Add 5 regression tests verifying: - Producer emits index_integrity_version in provenance (CR-03) - Provenance round-trip validates across serialize/reload (CR-03) - Tampered integrity version is rejected (CR-03) - _load_indexer rejects foreign checkout (CR-07) - Producer.build() rejects foreign indexer before execution (CR-07) Production fixes were already present; these tests prevent drift.
1 parent 2253f64 commit 7a2b885

1 file changed

Lines changed: 91 additions & 0 deletions

File tree

tests/test_prompt_graph_index.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@
1818
from cppmega_mlx.data.prompt_graph_index import (
1919
ClangPromptProjectIndexProducer,
2020
)
21+
from cppmega_mlx.data.prompt_graph_provenance import (
22+
INDEX_INTEGRITY_VERSION,
23+
validate_shared_provenance,
24+
)
2125
from cppmega_mlx.data.symbol_identity import compute_symbol_id
2226

2327

@@ -687,3 +691,90 @@ def test_corrupt_producer_cache_fails_closed(tmp_path: Path) -> None:
687691

688692
with pytest.raises(ValueError, match="cached prompt project index"):
689693
producer.build(FIXTURE, project_id="tests/case3-corrupt")
694+
695+
696+
# ---------------------------------------------------------------------------
697+
# CR-03: index_integrity_version provenance round-trip
698+
# ---------------------------------------------------------------------------
699+
700+
701+
def test_cr03_producer_emits_index_integrity_version(tmp_path: Path) -> None:
702+
"""MLX producer must emit index_integrity_version in provenance output."""
703+
project_id = "tests/case3-cr03-emit"
704+
result = ClangPromptProjectIndexProducer(
705+
cache_dir=tmp_path / "index-cache",
706+
indexer_root=ROOT,
707+
).build(FIXTURE, project_id=project_id)
708+
709+
assert result.receipt["index_integrity_version"] == INDEX_INTEGRITY_VERSION
710+
711+
712+
def test_cr03_provenance_round_trip_validates(tmp_path: Path) -> None:
713+
"""Serialize MLX index, reload, and validate through shared provenance."""
714+
project_id = "tests/case3-cr03-roundtrip"
715+
result = ClangPromptProjectIndexProducer(
716+
cache_dir=tmp_path / "index-cache",
717+
indexer_root=ROOT,
718+
).build(FIXTURE, project_id=project_id)
719+
720+
# Serialize and reload
721+
payload = result.index.to_dict()
722+
serialized = json.dumps(payload, sort_keys=True, ensure_ascii=False)
723+
reloaded = PromptProjectIndex.from_dict(json.loads(serialized))
724+
725+
# Must pass shared provenance validation
726+
validate_shared_provenance(reloaded, expected_indexer_root=ROOT)
727+
728+
729+
def test_cr03_tampered_integrity_version_rejected(tmp_path: Path) -> None:
730+
"""Deliberately changed index_integrity_version must be rejected."""
731+
project_id = "tests/case3-cr03-tamper"
732+
result = ClangPromptProjectIndexProducer(
733+
cache_dir=tmp_path / "index-cache",
734+
indexer_root=ROOT,
735+
).build(FIXTURE, project_id=project_id)
736+
737+
payload = result.index.to_dict()
738+
payload["provenance"]["index_integrity_version"] = "999"
739+
tampered = PromptProjectIndex.from_dict(payload).with_integrity()
740+
741+
with pytest.raises(ValueError, match="integrity version"):
742+
validate_shared_provenance(tampered, expected_indexer_root=ROOT)
743+
744+
745+
# ---------------------------------------------------------------------------
746+
# CR-07: foreign indexer_root rejection
747+
# ---------------------------------------------------------------------------
748+
749+
750+
def test_cr07_load_indexer_rejects_foreign_checkout(tmp_path: Path) -> None:
751+
"""_load_indexer must reject an indexer_root outside the package checkout."""
752+
foreign_root = tmp_path / "foreign_checkout"
753+
foreign_root.mkdir()
754+
(foreign_root / "tools" / "clang_indexer").mkdir(parents=True)
755+
(foreign_root / "tools" / "clang_indexer" / "index_project.py").write_text(
756+
"# foreign\n", encoding="utf-8"
757+
)
758+
759+
with pytest.raises(ValueError, match="same checkout"):
760+
prompt_graph_index_module._load_indexer(foreign_root)
761+
762+
763+
def test_cr07_producer_rejects_foreign_indexer_before_execution(
764+
tmp_path: Path,
765+
) -> None:
766+
"""Producer.build must reject foreign indexer_root before module exec."""
767+
foreign_root = tmp_path / "foreign_checkout"
768+
foreign_root.mkdir()
769+
(foreign_root / "tools" / "clang_indexer").mkdir(parents=True)
770+
(foreign_root / "tools" / "clang_indexer" / "index_project.py").write_text(
771+
"raise RuntimeError('must not execute')\n", encoding="utf-8"
772+
)
773+
774+
producer = ClangPromptProjectIndexProducer(
775+
cache_dir=tmp_path / "index-cache",
776+
indexer_root=foreign_root,
777+
)
778+
779+
with pytest.raises(ValueError, match="same checkout"):
780+
producer.build(FIXTURE, project_id="tests/case3-cr07-foreign")

0 commit comments

Comments
 (0)