from __future__ import annotations import json import threading import urllib.request from http.server import ThreadingHTTPServer from pathlib import Path from railiance_fabric.cli import main as cli_main from railiance_fabric.discovery import ( attribute_stable_key, discovery_stable_key, relationship_stable_key, replacement_scope_id, source_fingerprint, ) from railiance_fabric.registry import RegistryStore from railiance_fabric.server import RegistryHandler def test_registry_stores_diffs_and_accepts_discovery_snapshots(tmp_path: Path) -> None: store = RegistryStore(tmp_path / "registry.sqlite3") store.init_schema() store.upsert_repository({"slug": "fixture-repo", "name": "Fixture Repo"}) base_snapshot = store.add_snapshot( "fixture-repo", { "commit": "base", "graph": _base_graph(), }, ) first = _discovery_snapshot("disc-1", accepted_label="Discovered API", confidence=0.8) second = _discovery_snapshot("disc-2", accepted_label="Discovered API", confidence=0.95, extra=True) stored_first = store.add_discovery_snapshot("fixture-repo", first) stored_second = store.add_discovery_snapshot("fixture-repo", second) assert stored_first["profile"] == "deterministic" assert store.latest_discovery_snapshot("fixture-repo")["id"] == stored_second["id"] assert store.repository_inventory("fixture-repo")["counts"]["discovery_snapshots"] == 2 assert store.status()["counts"]["discovery_snapshots"] == 2 diff = store.discovery_snapshot_diff("fixture-repo") assert diff["from"]["id"] == stored_first["id"] assert diff["to"]["id"] == stored_second["id"] assert diff["discovery"]["nodes"]["confidence_changed"][0]["before"] == 0.8 assert diff["discovery"]["nodes"]["confidence_changed"][0]["after"] == 0.95 assert diff["discovery"]["nodes"]["added"][0]["label"] == "Extra Accepted Capability" accepted = store.accept_discovery_snapshot("fixture-repo", stored_second["id"]) graph_snapshot = accepted["graph_snapshot"] nodes_by_id = {node["id"]: node for node in graph_snapshot["graph"]["nodes"]} assert graph_snapshot["id"] > base_snapshot["id"] assert nodes_by_id["fixture.repo"]["name"] == "Fixture Repo" assert nodes_by_id["fixture.discovered-api"]["attributes"]["discovery_review_state"] == "accepted" assert nodes_by_id["fixture.extra-capability"]["attributes"]["discovery_confidence"] == 0.7 assert store.get_discovery_snapshot(stored_second["id"])["accepted_graph_snapshot_id"] == graph_snapshot["id"] def test_discovery_snapshot_http_and_cli_paths(tmp_path: Path, capsys) -> None: store = RegistryStore(tmp_path / "registry.sqlite3") store.init_schema() store.upsert_repository({"slug": "fixture-repo", "name": "Fixture Repo"}) store.add_snapshot("fixture-repo", {"commit": "base", "graph": _base_graph()}) class Handler(RegistryHandler): pass Handler.store = store server = ThreadingHTTPServer(("127.0.0.1", 0), Handler) thread = threading.Thread(target=server.serve_forever, daemon=True) thread.start() try: base_url = f"http://127.0.0.1:{server.server_port}" snapshot_path = tmp_path / "discovery.json" snapshot_path.write_text(json.dumps(_discovery_snapshot("disc-http")), encoding="utf-8") assert cli_main( [ "registry", "ingest-discovery", str(snapshot_path), "--registry-url", base_url, ] ) == 0 ingest_summary = capsys.readouterr().out assert "ingested discovery snapshot 1 for fixture-repo" in ingest_summary with urllib.request.urlopen( f"{base_url}/repositories/fixture-repo/discovery-snapshots/latest", timeout=5, ) as response: latest = json.loads(response.read()) with urllib.request.urlopen( f"{base_url}/repositories/fixture-repo/inventory", timeout=5, ) as response: inventory = json.loads(response.read()) assert latest["snapshot"]["kind"] == "FabricDiscoverySnapshot" assert inventory["latest_discovery_snapshot"]["id"] == latest["id"] assert cli_main( [ "registry", "accept-discovery", "fixture-repo", str(latest["id"]), "--registry-url", base_url, ] ) == 0 accept_summary = capsys.readouterr().out assert "accepted discovery snapshot" in accept_summary with urllib.request.urlopen(f"{base_url}/exports/graph-explorer", timeout=5) as response: explorer = json.loads(response.read()) discovered = next( element for element in explorer["elements"] if element["data"].get("id") == "fixture.discovered-api" ) assert discovered["data"]["reviewState"] == "accepted" assert discovered["data"]["discovery"]["origin"] == "deterministic" assert discovered["data"]["sourceReferences"][0]["label"].startswith("Discovery") finally: server.shutdown() server.server_close() thread.join(timeout=5) def _base_graph() -> dict[str, object]: return { "apiVersion": "railiance.fabric/v1alpha1", "kind": "FabricGraphExport", "nodes": [ { "id": "fixture.repo", "kind": "Repository", "name": "Fixture Repo", "repo": "fixture-repo", "domain": "testing", "lifecycle": "active", "attributes": {}, } ], "edges": [], } def _discovery_snapshot( commit: str, *, accepted_label: str = "Discovered API", confidence: float = 0.8, extra: bool = False, ) -> dict[str, object]: repo_key = discovery_stable_key("fixture-repo", "Repository", "fixture-repo") service_key = discovery_stable_key("fixture-repo", "ServiceDeclaration", accepted_label) scope_id = replacement_scope_id("fixture-repo", "fixture", "file", source_path="README.md") anchor = { "source_kind": "file", "path": "README.md", "fingerprint": source_fingerprint({"source_kind": "file", "path": "README.md"}), } provenance = { "extractor_id": "fixture", "extractor_version": "0.1.0", "method": "deterministic", "origin": "deterministic", } nodes = [ { "stable_key": repo_key, "graph_id": "fixture.repo", "kind": "Repository", "label": "Do Not Overwrite", "repo": "fixture-repo", "domain": "testing", "canon_category": "source-repository", "canon_anchor": "model/devsecops", "mapping_fit": "direct", "evidence_state": "declared", "aliases": ["fixture-repo"], "origin": "deterministic", "review_state": "accepted", "status": "active", "confidence": 1.0, "replacement_scope": scope_id, "provenance": [provenance], "source_anchors": [anchor], }, { "stable_key": service_key, "graph_id": "fixture.discovered-api", "kind": "ServiceDeclaration", "label": accepted_label, "repo": "fixture-repo", "domain": "testing", "lifecycle": "active", "canon_category": "service", "canon_anchor": "model/landscape", "mapping_fit": "direct", "evidence_state": "declared", "aliases": [accepted_label], "attributes": {"description": "Accepted discovery candidate."}, "origin": "deterministic", "review_state": "accepted", "status": "active", "confidence": confidence, "replacement_scope": scope_id, "provenance": [provenance], "source_anchors": [anchor], }, ] edges = [ { "stable_key": relationship_stable_key(repo_key, "declares", service_key), "edge_type": "declares", "canonical_type": "part_of", "canon_anchor": "model/devsecops", "mapping_fit": "partial", "display_only": True, "evidence_state": "declared", "source_key": repo_key, "target_key": service_key, "origin": "deterministic", "review_state": "accepted", "status": "active", "confidence": 0.8, "replacement_scope": scope_id, "provenance": [provenance], "source_anchors": [anchor], } ] attributes = [ { "stable_key": attribute_stable_key(service_key, "description"), "entity_key": service_key, "name": "description", "value": "Accepted discovery candidate.", "origin": "deterministic", "review_state": "accepted", "confidence": confidence, "replacement_scope": scope_id, "provenance": [provenance], "source_anchors": [anchor], } ] if extra: extra_key = discovery_stable_key("fixture-repo", "CapabilityDeclaration", "Extra Accepted Capability") nodes.append( { "stable_key": extra_key, "graph_id": "fixture.extra-capability", "kind": "CapabilityDeclaration", "label": "Extra Accepted Capability", "repo": "fixture-repo", "domain": "testing", "lifecycle": "active", "canon_category": "software-system", "canon_anchor": "model/landscape", "mapping_fit": "partial", "evidence_state": "declared", "origin": "deterministic", "review_state": "accepted", "status": "active", "confidence": 0.7, "replacement_scope": scope_id, "provenance": [provenance], "source_anchors": [anchor], } ) return { "apiVersion": "railiance.fabric/v1alpha1", "kind": "FabricDiscoverySnapshot", "generated_at": "2026-05-19T00:00:00Z", "source": {"repo_slug": "fixture-repo", "repo_name": "Fixture Repo", "commit": commit}, "scan": { "run_id": f"scan:fixture-repo:{commit}", "profile": "deterministic", "deterministic_only": True, "llm_enabled": False, }, "replacement_scopes": [ { "id": scope_id, "extractor_id": "fixture", "source_kind": "file", "source_path": "README.md", "mode": "replacement", } ], "candidates": {"nodes": nodes, "edges": edges, "attributes": attributes}, "tombstones": [], "reconciliation": { "precedence": ["repo_declaration", "deterministic", "catalog", "registry", "llm", "manual"], "duplicate_policy": "stable-key matches merge automatically", "retirement_policy": "missing candidates retire only inside their replacement scope", }, }