generated from coulomb/repo-seed
300 lines
11 KiB
Python
300 lines
11 KiB
Python
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",
|
|
},
|
|
}
|