Files

124 lines
5.9 KiB
Python
Raw Permalink Normal View History

"""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
def test_the_single_attempt_path_is_reused_and_normalized_too(tmp_path):
"""The portioner asks `run_batch_once`; wrapping only `run_batch` let every portioned query skip reuse."""
from openultrasast.cpg.artifact import GraphArtifact, GraphCensus, GraphIdentity
from openultrasast.cpg.backend import TIMING
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"]}]
once_calls = []
def once(kind, requests):
once_calls.append(dict(requests))
return {
**{rid: [{"file": "/old/a.js"}] for rid in requests},
"__census__": census,
TIMING: [{"id": rid, "ms": 5.0} for rid in requests],
}
graph = CpgResult(tmp_path / "graph", lambda *_: None, lambda k, r: None, run_batch_once=once)
result = wrapper._answers(graph, artifact, ExecutionBudget(time.monotonic() + 5, 0.2))
assert result.run_batch_once is not None
first = result.run_batch_once("taint", {"q": {"file": "a.js"}})
assert first["q"] == [{"file": "a.js"}], "the source root was not stripped on the single-attempt path"
assert first[TIMING] == [{"id": "q", "ms": 5.0}]
second = result.run_batch_once("taint", {"q": {"file": "a.js"}})
assert second["q"] == [{"file": "a.js"}] and len(once_calls) == 1, "the single-attempt path bypassed the cache"
assert TIMING not in second, "a cached answer cost the engine nothing and must report no time"