feat(pre-push-safety-net): reuse compatible artifacts throughout replay

This commit is contained in:
Marius Ciepluch
2026-09-13 17:08:49 +02:00
parent 0ab5d56618
commit 530b9c9879
21 changed files with 2522 additions and 54 deletions
+18
View File
@@ -88,3 +88,21 @@ def test_generic_driver_accepts_backend_validated_lease(tmp_path, monkeypatch):
assert result.run("census", {})["file_names"] == ["a.js"]
_dispose(result)
assert graph.read_bytes() == b"graph original"
def test_installed_frontend_adaptation_changes_graph_identity(tmp_path, monkeypatch):
import openultrasast.cpg.backend as module
backend, root, _, identity, _ = prepared(tmp_path, monkeypatch)
home = tmp_path / "engine"
home.mkdir()
launcher = home / "joern"
launcher.write_text("launcher")
jar = home / "frontends/jssrc2cpg/lib/io.joern.jssrc2cpg-4.0.625.jar"
jar.parent.mkdir(parents=True)
jar.write_bytes(b"frontend-one")
monkeypatch.setattr(module.shutil, "which", lambda name: str(launcher) if name == "joern" else None)
first = backend.graph_identity(root, language="javascript", declarations_digest="d", exclusions_digest="e")
jar.write_bytes(b"frontend-two")
second = backend.graph_identity(root, language="javascript", declarations_digest="d", exclusions_digest="e")
assert first != second
+89
View File
@@ -0,0 +1,89 @@
"""Live integration of validated graph and query reuse under one deadline."""
import json
import time
from openultrasast.cpg.backend import CpgResult, JoernBackend
from openultrasast.model.contracts import ExecutionBudget
from openultrasast.push.cache import ArtifactCache, SemanticKeys
from openultrasast.push.reuse import ReusingBackend
def test_backend_reuses_graph_and_answers_then_invalidates_source_and_dependencies(tmp_path, monkeypatch):
import openultrasast.cpg.backend as module
monkeypatch.setattr(module, "joern_version", lambda: (4, 0, 625))
builds = []
queries = []
class Engine(JoernBackend):
def build(self, root, *, language, exclude, execution_budget):
builds.append(root)
path = tmp_path / f"graph-{len(builds)}.json"
paths = [str(p) for p in root.glob("*.js")]
path.write_text(json.dumps(paths))
return CpgResult(path, lambda kind, params: self.query(path, kind, params), lambda kind, req: self.query_batch(path, kind, req))
def query(self, path, kind, params):
names = json.loads(path.read_text())
return {"files": len(names), "methods": 2, "calls": 1, "file_names": names, "overlays": "dataflowOss"}
def query_batch(self, path, kind, requests):
queries.append((kind, requests))
return {**{rid: [] for rid in requests}, "__census__": [self.query(path, "census", {})]}
root = tmp_path / "source"
root.mkdir()
(root / "a.js").write_text("function f() {}")
cache = ArtifactCache(tmp_path / "cache", max_bytes=100000)
semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
deadline = ExecutionBudget(time.monotonic() + 10, 0.2)
backend = ReusingBackend(Engine(), cache, semantics, declarations="dependencies", exclusions="exclusions")
def run():
result = backend.build(root, language="javascript", exclude=(), execution_budget=deadline)
assert result is not None
answer = result.run_batch("taint", {"q": {"file": "a.js", "callDepth": 3}})
if result.cleanup:
result.cleanup()
return answer
expected = run()
assert run() == expected
assert len(builds) == 1 and len(queries) == 1
(root / "a.js").write_text("function g() {}")
run()
assert len(builds) == 2 and len(queries) == 2
backend.declarations = "changed-dependency"
run()
assert len(builds) == 3 and len(queries) == 3
backend.semantics = SemanticKeys("changed-facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
run()
assert len(builds) == 3 and len(queries) == 4
def test_fresh_missing_census_cannot_borrow_a_cached_query_census(tmp_path):
from openultrasast.cpg.artifact import GraphArtifact, GraphCensus, GraphIdentity
identity = GraphIdentity(
"source", "declarations", "exclude", "repository", "javascript", "engine", "frontend", "version", ("dataflowOss",), ()
)
artifact = GraphArtifact(identity, "graph-bytes", 10, GraphCensus(1, 1, 1), "complete", (), "/old", ("a.js",))
cache = ArtifactCache(tmp_path / "cache", max_bytes=100000)
semantics = SemanticKeys("facts", "queries", "config", "ranking", "admission", "disabled", "empty", "advisory")
wrapper = ReusingBackend(JoernBackend(), cache, semantics, declarations="d", exclusions="e")
census = [{"files": 1, "methods": 1, "file_names": ["a.js"]}]
calls = []
def batch(kind, requests):
calls.append(requests)
result = {rid: [] for rid in requests}
if len(calls) == 1:
result["__census__"] = census
return result
graph = CpgResult(tmp_path / "graph", lambda *_: None, batch)
result = wrapper._answers(graph, artifact, ExecutionBudget(time.monotonic() + 5, 0.2))
assert "q" in result.run_batch("taint", {"q": {"file": "a.js"}})
answer = result.run_batch("taint", {"q": {"file": "a.js"}, "fresh": {"file": "a.js", "callDepth": 3}})
assert "q" in answer and "fresh" not in answer
+81
View File
@@ -218,3 +218,84 @@ def test_default_replay_does_not_launch_unbudgeted_availability_probe(tmp_path,
monkeypatch.setattr(runner, "JoernBackend", Backend, raising=False)
result = replay(root, base=base, head=head, artifact=tmp_path / "probe.json")
assert builds and result.result.coverage_status == "incomplete"
def test_replay_persists_discovery_without_skipping_readable_snapshots(tmp_path, monkeypatch):
from openultrasast.push import runner
root, base, head, _ = history(tmp_path)
cache = tmp_path / "cache"
cold = tmp_path / "cold.json"
warm = tmp_path / "warm.json"
replay(root, base=base, head=head, artifact=cold, backend=NullBackend(), cache_dir=cache)
def should_reuse(*args):
raise AssertionError("discovery should be cached")
monkeypatch.setattr(runner, "_discover", should_reuse)
replay(root, base=base, head=head, artifact=warm, backend=NullBackend(), cache_dir=cache)
a, b = json.loads(cold.read_text()), json.loads(warm.read_text())
assert a["result"] == b["result"]
assert a["snapshots"] == b["snapshots"]
assert b["timings"]["discovery_hits"] == 2
assert b["snapshots"][0]["bytes_read"] > 0
def test_cache_cannot_write_inside_repository(tmp_path):
root, base, head, git = history(tmp_path)
before = git("status", "--porcelain")
with pytest.raises(ValueError, match="cache.*outside"):
replay(root, base=base, head=head, artifact=tmp_path / "out.json", cache_dir=root / "cache")
assert git("status", "--porcelain") == before
def test_cached_discovery_invalidates_source_and_dependency_edits(tmp_path):
root, base, head, git = history(tmp_path)
cache = tmp_path / "cache"
replay(root, base=base, head=head, artifact=tmp_path / "initial.json", backend=NullBackend(), cache_dir=cache)
(root / "api.js").write_text("function replacement(req) { eval(req.body.other); }")
git("add", "api.js")
git("commit", "-m", "change source")
edited = git("rev-parse", "HEAD")
replay(root, base=base, head=edited, artifact=tmp_path / "edited.json", backend=NullBackend(), cache_dir=cache)
data = json.loads((tmp_path / "edited.json").read_text())
assert data["timings"]["discovery_hits"] == 1
(root / "package.json").write_text('{"dependencies":{"express":"5"}}')
git("add", "package.json")
git("commit", "-m", "change dependencies")
dependency = git("rev-parse", "HEAD")
replay(root, base=base, head=dependency, artifact=tmp_path / "dependency.json", backend=NullBackend(), cache_dir=cache)
data = json.loads((tmp_path / "dependency.json").read_text())
assert data["timings"]["discovery_hits"] == 1
assert data["change_context"]["head_revision"] == dependency
def test_identical_revision_materializes_once_and_reuses_discovery(tmp_path, monkeypatch):
from openultrasast.push.snapshot import SnapshotAdapter
root, base, _, _ = history(tmp_path)
original = SnapshotAdapter.materialize
calls = []
def counted(self, oid, **kwargs):
calls.append(oid)
return original(self, oid, **kwargs)
monkeypatch.setattr(SnapshotAdapter, "materialize", counted)
replay(root, base=base, head=base, artifact=tmp_path / "same.json", backend=NullBackend(), cache_dir=tmp_path / "cache")
data = json.loads((tmp_path / "same.json").read_text())
assert calls == [base]
assert data["timings"]["discovery_hits"] == 1
assert len(data["snapshots"]) == 2
def test_admission_provenance_invalidates_when_installed_runtime_changes(tmp_path, monkeypatch):
from openultrasast.model.scan import ScanBudget
from openultrasast.push import runner
monkeypatch.setattr(runner, "engine_runtime_identity", lambda: "first-installation")
first = runner._provenance(tmp_path, ScanBudget())
monkeypatch.setattr(runner, "engine_runtime_identity", lambda: "second-installation")
second = runner._provenance(tmp_path, ScanBudget())
assert first["semantics"] != second["semantics"]
assert first["facts"] == second["facts"] and first["graph_layout"] == second["graph_layout"]