Add canon reset and reingest guardrails

This commit is contained in:
2026-05-23 14:52:57 +02:00
parent 653411ffb8
commit 9c22d3e0df
12 changed files with 634 additions and 5 deletions

View File

@@ -9,6 +9,8 @@ from pathlib import Path
from railiance_fabric.cli import main as cli_main
from railiance_fabric.graph import build_graph
from railiance_fabric.registry import (
RESET_CONFIRMATION_TOKEN,
RegistryError,
RegistryStore,
backstage_projection,
blast_radius,
@@ -16,6 +18,7 @@ from railiance_fabric.registry import (
library_xregistry_projection,
providers,
unresolved_dependencies,
validate_graph_export,
xregistry_projection,
)
from railiance_fabric.server import RegistryHandler
@@ -231,6 +234,130 @@ def test_registry_http_service_serves_queries(tmp_path: Path) -> None:
thread.join(timeout=5)
def test_graph_export_validation_rejects_unflagged_display_edges() -> None:
graph = {
"apiVersion": "railiance.fabric/v1alpha1",
"kind": "FabricGraphExport",
"nodes": [],
"edges": [
{
"from": "repo.fixture",
"to": "fixture.service",
"type": "declares",
"canonical_type": "part_of",
"canon_anchor": "model/devsecops",
"mapping_fit": "partial",
"display_only": False,
"evidence_state": "declared",
}
],
}
try:
validate_graph_export(graph)
except RegistryError as exc:
assert "display-only edge type" in exc.message
else:
raise AssertionError("expected RegistryError for unflagged display-only edge")
def test_registry_reset_archive_and_guarded_reset(tmp_path: Path) -> 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": "abc123",
"generated_at": "2026-05-23T00:00:00Z",
"graph": build_graph([Path(".")]).to_export(),
},
)
archive = store.reset_archive()
assert archive["kind"] == "RegistryResetArchive"
assert archive["counts"]["repositories"] == 1
assert archive["counts"]["snapshots"] == 1
assert archive["snapshots"][0]["commit"] == "abc123"
try:
store.reset_graph_data(
{
"confirm": "nope",
"reason": "test reset",
"archive_sha256": "abc123",
}
)
except RegistryError as exc:
assert RESET_CONFIRMATION_TOKEN in exc.message
else:
raise AssertionError("expected RegistryError for missing reset confirmation")
event = store.reset_graph_data(
{
"confirm": RESET_CONFIRMATION_TOKEN,
"reason": "test reset",
"archive_path": str(tmp_path / "archive.json"),
"archive_sha256": "abc123",
}
)
assert event["dropped_counts"]["snapshots"] == 1
assert event["repositories_preserved"] == 1
assert store.status()["counts"]["repositories"] == 1
assert store.status()["counts"]["snapshots"] == 0
assert store.status()["counts"]["registry_reset_events"] == 1
def test_registry_cli_exports_archive_before_reset(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": "abc123",
"generated_at": "2026-05-23T00:00:00Z",
"graph": build_graph([Path(".")]).to_export(),
},
)
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:
archive_path = tmp_path / "reset-archive.json"
assert cli_main(
[
"registry",
"reset-graph-data",
"--registry-url",
f"http://127.0.0.1:{server.server_port}",
"--archive",
str(archive_path),
"--confirm",
RESET_CONFIRMATION_TOKEN,
"--reason",
"test reset",
]
) == 0
output = capsys.readouterr().out
archive = json.loads(archive_path.read_text(encoding="utf-8"))
assert "reset event" in output
assert archive["counts"]["snapshots"] == 1
assert store.status()["counts"]["snapshots"] == 0
assert store.status()["counts"]["repositories"] == 1
finally:
server.shutdown()
server.server_close()
thread.join(timeout=5)
def test_registry_sync_manifest_registers_multiple_repos(tmp_path: Path) -> None:
store = RegistryStore(tmp_path / "registry.sqlite3")
store.init_schema()