mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
test(javascript): share semantic graph fixtures (#1631)
This commit is contained in:
@@ -1,3 +1,8 @@
|
||||
import {
|
||||
semanticFixtureGraph,
|
||||
semanticFixtureInput,
|
||||
semanticGraphTestEvidence,
|
||||
} from "../../../tests/support/javascriptSemanticGraphFixture.js";
|
||||
import { expect, it } from "vitest";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -7,12 +12,10 @@ import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSc
|
||||
import {
|
||||
JAVASCRIPT_SEMANTIC_RELATION_FAMILY,
|
||||
JAVASCRIPT_SEMANTIC_NODE_KINDS,
|
||||
JAVASCRIPT_SEMANTIC_RELATION_FAMILIES,
|
||||
JAVASCRIPT_SEMANTIC_RELATIONS,
|
||||
type JavaScriptSemanticGraphInput,
|
||||
} from "./javascriptSemanticGraphSchemas.js";
|
||||
import {
|
||||
createJavaScriptSemanticFingerprint,
|
||||
createJavaScriptSemanticGraph,
|
||||
createJavaScriptSemanticGraphNode,
|
||||
createJavaScriptSemanticGraphRelation,
|
||||
@@ -32,77 +35,6 @@ import {
|
||||
import { queryJavaScriptSemanticGraph } from "./javascriptSemanticQuery.js";
|
||||
|
||||
const SHA = "a".repeat(64);
|
||||
const JAG_ID = `jag_${"b".repeat(64)}`;
|
||||
const completeCoverage = {
|
||||
status: "complete",
|
||||
truncated: false,
|
||||
omitted_count: 0,
|
||||
limits: [],
|
||||
} satisfies ApplicationGraphEvidence["coverage"];
|
||||
|
||||
const evidence = (
|
||||
state: "observed" | "inferred" = "observed",
|
||||
): ApplicationGraphEvidence => ({
|
||||
authority:
|
||||
state === "observed"
|
||||
? "ast-static-analysis"
|
||||
: "static-relationship-inference",
|
||||
state,
|
||||
confidence: state === "observed" ? "exact" : "high",
|
||||
artifact: { available: true, artifact_id: `art_${SHA}`, sha256: SHA },
|
||||
location: {
|
||||
available: true,
|
||||
value: {
|
||||
kind: "source-range",
|
||||
source: "bundle.js",
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 1, column: 10 },
|
||||
},
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "recover-semantic-relation",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: completeCoverage,
|
||||
limitations:
|
||||
state === "observed"
|
||||
? []
|
||||
: ["Static reachability does not prove runtime execution."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
const unknownEvidence = (): ApplicationGraphEvidence => ({
|
||||
authority: "unknown",
|
||||
state: "unknown",
|
||||
confidence: "unknown",
|
||||
artifact: {
|
||||
available: false,
|
||||
reason: "unknown",
|
||||
detail: "Unknown artifact.",
|
||||
},
|
||||
location: {
|
||||
available: false,
|
||||
reason: "unresolved",
|
||||
detail: "Dynamic call target.",
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "retain-dynamic-call",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: {
|
||||
status: "unknown",
|
||||
truncated: false,
|
||||
omitted_count: null,
|
||||
limits: [],
|
||||
},
|
||||
limitations: ["Dynamic call target remains unknown."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
const node = (
|
||||
kind: (typeof JAVASCRIPT_SEMANTIC_NODE_KINDS)[number],
|
||||
role: string,
|
||||
@@ -129,7 +61,7 @@ const node = (
|
||||
application_node_ids: [],
|
||||
label: role,
|
||||
properties,
|
||||
evidence: evidence(),
|
||||
evidence: semanticGraphTestEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
@@ -157,7 +89,7 @@ it("captures caller-owned node identity, properties, and provenance", () => {
|
||||
application_node_ids: [],
|
||||
label: "literal",
|
||||
properties: { nested: ["original"] },
|
||||
evidence: evidence(),
|
||||
evidence: semanticGraphTestEvidence(),
|
||||
};
|
||||
const captured = createJavaScriptSemanticGraphNode(input, contexts);
|
||||
const expected = structuredClone(captured);
|
||||
@@ -181,137 +113,6 @@ it("captures caller-owned node identity, properties, and provenance", () => {
|
||||
).toEqual(expectedEvidence);
|
||||
});
|
||||
|
||||
const fixtureInput = (withUnknown = false): JavaScriptSemanticGraphInput => {
|
||||
const evidenceContexts = new JavaScriptSemanticEvidenceContextRegistry();
|
||||
const module = node("module", "module", {}, evidenceContexts);
|
||||
const literal = node(
|
||||
"literal",
|
||||
"literal",
|
||||
{ value: "TOKEN" },
|
||||
evidenceContexts,
|
||||
);
|
||||
const binding = node("binding", "binding", {}, evidenceContexts);
|
||||
const callable = node("function", "function", {}, evidenceContexts);
|
||||
const request = node(
|
||||
"request",
|
||||
"request",
|
||||
{
|
||||
endpoint: "https://example.invalid/v1",
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
const relations = [
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: literal.node_id,
|
||||
target_node_id: binding.node_id,
|
||||
relation: "defines",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: binding.node_id,
|
||||
target_node_id: callable.node_id,
|
||||
relation: "captures",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: callable.node_id,
|
||||
target_node_id: request.node_id,
|
||||
relation: "constructs-request",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
];
|
||||
const dynamic = withUnknown
|
||||
? createJavaScriptSemanticGraphUnknown(
|
||||
{
|
||||
node_id: callable.node_id,
|
||||
family: "call-flow",
|
||||
relation_kinds: ["calls"],
|
||||
reason: "dynamic-call",
|
||||
detail: "Computed callee is unresolved.",
|
||||
candidate_node_ids: [],
|
||||
evidence: unknownEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
)
|
||||
: null;
|
||||
const unknowns = dynamic === null ? [] : [dynamic];
|
||||
const coverageFamilies = JAVASCRIPT_SEMANTIC_RELATION_FAMILIES.map(
|
||||
(family) => ({
|
||||
family,
|
||||
status:
|
||||
withUnknown && family === "call-flow"
|
||||
? ("unknown" as const)
|
||||
: ("complete" as const),
|
||||
retained_relations: relations.filter(({ relation }) =>
|
||||
relation === "defines"
|
||||
? family === "data-flow"
|
||||
: relation === "captures"
|
||||
? family === "closure"
|
||||
: family === "request",
|
||||
).length,
|
||||
omitted_relations: withUnknown && family === "call-flow" ? null : 0,
|
||||
unknown_ids:
|
||||
dynamic !== null && family === "call-flow" ? [dynamic.unknown_id] : [],
|
||||
}),
|
||||
);
|
||||
const fingerprint = createJavaScriptSemanticFingerprint(
|
||||
{
|
||||
function_node_id: callable.node_id,
|
||||
algorithm: "rea.javascript-semantic-function",
|
||||
status: "complete",
|
||||
components: {
|
||||
parameter_arity: 0,
|
||||
normalized_ast_sha256: "1".repeat(64),
|
||||
control_flow_sha256: "2".repeat(64),
|
||||
relation_shape_sha256: "3".repeat(64),
|
||||
literal_set_sha256: "4".repeat(64),
|
||||
effects: ["network"],
|
||||
},
|
||||
limitations: [],
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
return {
|
||||
schema: "JavaScriptSemanticRelationGraph",
|
||||
root_artifact_sha256: SHA,
|
||||
application_graph_id: JAG_ID,
|
||||
root_node_ids: [module.node_id],
|
||||
evidence_contexts: evidenceContexts.contexts,
|
||||
nodes: [request, callable, binding, literal, module],
|
||||
relations,
|
||||
fingerprints: [fingerprint],
|
||||
unknowns,
|
||||
coverage: {
|
||||
status: withUnknown ? "partial" : "complete",
|
||||
truncated: false,
|
||||
omitted_nodes: withUnknown ? null : 0,
|
||||
omitted_relations: withUnknown ? null : 0,
|
||||
limits: [],
|
||||
families: coverageFamilies,
|
||||
},
|
||||
limitations: withUnknown ? ["Dynamic call target remains unknown."] : [],
|
||||
};
|
||||
};
|
||||
|
||||
const fixtureGraph = (withUnknown = false): JavaScriptSemanticGraph =>
|
||||
createJavaScriptSemanticGraph(fixtureInput(withUnknown));
|
||||
|
||||
const registryForGraph = (
|
||||
graph: JavaScriptSemanticGraph,
|
||||
): JavaScriptSemanticEvidenceContextRegistry => {
|
||||
@@ -327,7 +128,7 @@ const registryForGraph = (
|
||||
};
|
||||
|
||||
const graphWithCandidateRelations = (): JavaScriptSemanticGraph => {
|
||||
const base = fixtureGraph();
|
||||
const base = semanticFixtureGraph();
|
||||
const evidenceContexts = registryForGraph(base);
|
||||
const relations = base.relations.map(
|
||||
({ relation_id: _relationId, ...relation }) =>
|
||||
@@ -356,7 +157,7 @@ const graphWithCandidateRelations = (): JavaScriptSemanticGraph => {
|
||||
relation: "owns",
|
||||
resolution: "candidate",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
@@ -401,7 +202,7 @@ it("counts candidate edges by query direction and relation filter", () => {
|
||||
});
|
||||
|
||||
it("matches scalar literal seeds and skips non-scalar literal metadata", () => {
|
||||
const input = fixtureInput();
|
||||
const input = semanticFixtureInput();
|
||||
const metadataContexts = new JavaScriptSemanticEvidenceContextRegistry();
|
||||
const literals = [
|
||||
node("literal", "string-seven", { value: "7" }, metadataContexts),
|
||||
@@ -472,7 +273,7 @@ it("defines every required relation family and canonicalizes records", () => {
|
||||
expect(JAVASCRIPT_SEMANTIC_RELATIONS).toContain("detaches-task");
|
||||
expect(JAVASCRIPT_SEMANTIC_RELATIONS).toContain("forwards-signal");
|
||||
expect(JAVASCRIPT_SEMANTIC_RELATIONS).toContain("validates");
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
expect(
|
||||
parseJavaScriptSemanticGraph(
|
||||
JSON.parse(serializeJavaScriptSemanticGraph(graph)),
|
||||
@@ -484,7 +285,7 @@ it("defines every required relation family and canonicalizes records", () => {
|
||||
});
|
||||
|
||||
it("rejects stale identities, dangling endpoints, and incomplete coverage claims", () => {
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
expect(() =>
|
||||
parseJavaScriptSemanticGraph({
|
||||
...graph,
|
||||
@@ -493,10 +294,10 @@ it("rejects stale identities, dangling endpoints, and incomplete coverage claims
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
createJavaScriptSemanticGraph({
|
||||
...fixtureInput(),
|
||||
...semanticFixtureInput(),
|
||||
relations: [
|
||||
{
|
||||
...fixtureInput().relations[0],
|
||||
...semanticFixtureInput().relations[0],
|
||||
target_node_id: `jsrg_node_${"f".repeat(64)}`,
|
||||
},
|
||||
],
|
||||
@@ -504,11 +305,11 @@ it("rejects stale identities, dangling endpoints, and incomplete coverage claims
|
||||
).toThrow();
|
||||
expect(() =>
|
||||
createJavaScriptSemanticGraph({
|
||||
...fixtureInput(),
|
||||
coverage: { ...fixtureInput().coverage, omitted_nodes: 1 },
|
||||
...semanticFixtureInput(),
|
||||
coverage: { ...semanticFixtureInput().coverage, omitted_nodes: 1 },
|
||||
}),
|
||||
).toThrow(/Complete graph coverage/u);
|
||||
const unknownGraphInput = fixtureInput(true);
|
||||
const unknownGraphInput = semanticFixtureInput(true);
|
||||
const originalUnknown = unknownGraphInput.unknowns[0];
|
||||
if (originalUnknown === undefined)
|
||||
throw new Error("Expected unknown frontier");
|
||||
@@ -536,7 +337,7 @@ it("rejects stale identities, dangling endpoints, and incomplete coverage claims
|
||||
});
|
||||
|
||||
it("validates evidence context ownership and references", () => {
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
const alteredContext = {
|
||||
...graph,
|
||||
evidence_contexts: graph.evidence_contexts.map((context, index) =>
|
||||
@@ -568,7 +369,7 @@ it("validates evidence context ownership and references", () => {
|
||||
"Evidence context identifier is stale",
|
||||
);
|
||||
|
||||
const withUnknown = fixtureGraph(true);
|
||||
const withUnknown = semanticFixtureGraph(true);
|
||||
const extraContext = withUnknown.evidence_contexts.find(
|
||||
({ context_id }) =>
|
||||
!graph.evidence_contexts.some(
|
||||
@@ -649,7 +450,7 @@ it("validates evidence context ownership and references", () => {
|
||||
});
|
||||
|
||||
it("shares provenance contexts while preserving fact-specific locations and unknown IDs", () => {
|
||||
const graph = fixtureGraph(true);
|
||||
const graph = semanticFixtureGraph(true);
|
||||
const first = graph.unknowns[0];
|
||||
if (first === undefined) throw new Error("Expected an unknown frontier");
|
||||
const evidenceContexts = registryForGraph(graph);
|
||||
@@ -682,7 +483,7 @@ const complete = <Value>(steps: Iterator<void, Value>): Value => {
|
||||
};
|
||||
|
||||
it("captures nested input before yielding and preserves the synchronous graph commitment", () => {
|
||||
const input = fixtureInput(true);
|
||||
const input = semanticFixtureInput(true);
|
||||
const node = input.nodes[0];
|
||||
if (node === undefined) throw new Error("Expected fixture node");
|
||||
const nested = { text: 'Unicode: \u{1f600} \ud800 " \\ ', rows: [1, 2, 3] };
|
||||
@@ -780,7 +581,7 @@ const invalidGraphCases: {
|
||||
it.each(invalidGraphCases)(
|
||||
"retains full validation diagnostics for $label in owned steps",
|
||||
({ change, message }) => {
|
||||
const input = fixtureInput(true);
|
||||
const input = semanticFixtureInput(true);
|
||||
change(input);
|
||||
const issues = (action: () => unknown) => {
|
||||
try {
|
||||
@@ -802,7 +603,7 @@ it.each(invalidGraphCases)(
|
||||
it("interns each distinct evidence context once under its content digest", () => {
|
||||
const registry = new JavaScriptSemanticEvidenceContextRegistry();
|
||||
const elsewhere: ApplicationGraphEvidence = {
|
||||
...evidence(),
|
||||
...semanticGraphTestEvidence(),
|
||||
location: {
|
||||
available: true,
|
||||
value: {
|
||||
@@ -813,17 +614,17 @@ it("interns each distinct evidence context once under its content digest", () =>
|
||||
},
|
||||
},
|
||||
};
|
||||
const first = registry.intern(evidence());
|
||||
const first = registry.intern(semanticGraphTestEvidence());
|
||||
const repeated = registry.intern(elsewhere);
|
||||
const reordered = registry.intern({
|
||||
...evidence("inferred"),
|
||||
...semanticGraphTestEvidence("inferred"),
|
||||
limitations: [
|
||||
"Static reachability does not prove runtime execution.",
|
||||
"Static reachability does not prove runtime execution.",
|
||||
],
|
||||
});
|
||||
const inferred = registry.intern(evidence("inferred"));
|
||||
const { location: _location, ...context } = evidence();
|
||||
const inferred = registry.intern(semanticGraphTestEvidence("inferred"));
|
||||
const { location: _location, ...context } = semanticGraphTestEvidence();
|
||||
|
||||
expect(repeated.context_id).toBe(first.context_id);
|
||||
expect(repeated.location).toEqual(elsewhere.location);
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import {
|
||||
semanticFixtureGraph,
|
||||
semanticGraphTestEvidence,
|
||||
unknownSemanticGraphTestEvidence,
|
||||
} from "../../../tests/support/javascriptSemanticGraphFixture.js";
|
||||
import { expect, it } from "vitest";
|
||||
|
||||
import type { ApplicationGraphEvidence } from "./javascriptApplicationEvidenceSchemas.js";
|
||||
import {
|
||||
JAVASCRIPT_SEMANTIC_NODE_KINDS,
|
||||
JAVASCRIPT_SEMANTIC_RELATION_FAMILIES,
|
||||
@@ -8,7 +12,6 @@ import {
|
||||
type JavaScriptSemanticGraphInput,
|
||||
} from "./javascriptSemanticGraphSchemas.js";
|
||||
import {
|
||||
createJavaScriptSemanticFingerprint,
|
||||
createJavaScriptSemanticGraph,
|
||||
createJavaScriptSemanticGraphNode,
|
||||
createJavaScriptSemanticGraphRelation,
|
||||
@@ -26,76 +29,6 @@ import type { JsonValue } from "../jsonValue.js";
|
||||
|
||||
const SHA = "a".repeat(64);
|
||||
const JAG_ID = `jag_${"b".repeat(64)}`;
|
||||
const completeCoverage = {
|
||||
status: "complete",
|
||||
truncated: false,
|
||||
omitted_count: 0,
|
||||
limits: [],
|
||||
} satisfies ApplicationGraphEvidence["coverage"];
|
||||
|
||||
const evidence = (
|
||||
state: "observed" | "inferred" = "observed",
|
||||
): ApplicationGraphEvidence => ({
|
||||
authority:
|
||||
state === "observed"
|
||||
? "ast-static-analysis"
|
||||
: "static-relationship-inference",
|
||||
state,
|
||||
confidence: state === "observed" ? "exact" : "high",
|
||||
artifact: { available: true, artifact_id: `art_${SHA}`, sha256: SHA },
|
||||
location: {
|
||||
available: true,
|
||||
value: {
|
||||
kind: "source-range",
|
||||
source: "bundle.js",
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 1, column: 10 },
|
||||
},
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "recover-semantic-relation",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: completeCoverage,
|
||||
limitations:
|
||||
state === "observed"
|
||||
? []
|
||||
: ["Static reachability does not prove runtime execution."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
const unknownEvidence = (): ApplicationGraphEvidence => ({
|
||||
authority: "unknown",
|
||||
state: "unknown",
|
||||
confidence: "unknown",
|
||||
artifact: {
|
||||
available: false,
|
||||
reason: "unknown",
|
||||
detail: "Unknown artifact.",
|
||||
},
|
||||
location: {
|
||||
available: false,
|
||||
reason: "unresolved",
|
||||
detail: "Dynamic call target.",
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "retain-dynamic-call",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: {
|
||||
status: "unknown",
|
||||
truncated: false,
|
||||
omitted_count: null,
|
||||
limits: [],
|
||||
},
|
||||
limitations: ["Dynamic call target remains unknown."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
const node = (
|
||||
kind: (typeof JAVASCRIPT_SEMANTIC_NODE_KINDS)[number],
|
||||
role: string,
|
||||
@@ -119,133 +52,13 @@ const node = (
|
||||
application_node_ids: [],
|
||||
label: role,
|
||||
properties,
|
||||
evidence: evidence(),
|
||||
evidence: semanticGraphTestEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
|
||||
const fixtureGraph = (withUnknown = false): JavaScriptSemanticGraph => {
|
||||
const evidenceContexts = new JavaScriptSemanticEvidenceContextRegistry();
|
||||
const module = node("module", "module", evidenceContexts);
|
||||
const literal = node("literal", "literal", evidenceContexts, {
|
||||
value: "TOKEN",
|
||||
});
|
||||
const binding = node("binding", "binding", evidenceContexts);
|
||||
const callable = node("function", "function", evidenceContexts);
|
||||
const request = node("request", "request", evidenceContexts, {
|
||||
endpoint: "https://example.invalid/v1",
|
||||
});
|
||||
const relations = [
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: literal.node_id,
|
||||
target_node_id: binding.node_id,
|
||||
relation: "defines",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: binding.node_id,
|
||||
target_node_id: callable.node_id,
|
||||
relation: "captures",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: callable.node_id,
|
||||
target_node_id: request.node_id,
|
||||
relation: "constructs-request",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
];
|
||||
const dynamic = withUnknown
|
||||
? createJavaScriptSemanticGraphUnknown(
|
||||
{
|
||||
node_id: callable.node_id,
|
||||
family: "call-flow",
|
||||
relation_kinds: ["calls"],
|
||||
reason: "dynamic-call",
|
||||
detail: "Computed callee is unresolved.",
|
||||
candidate_node_ids: [],
|
||||
evidence: unknownEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
)
|
||||
: null;
|
||||
const unknowns = dynamic === null ? [] : [dynamic];
|
||||
const coverageFamilies = JAVASCRIPT_SEMANTIC_RELATION_FAMILIES.map(
|
||||
(family) => ({
|
||||
family,
|
||||
status:
|
||||
withUnknown && family === "call-flow"
|
||||
? ("unknown" as const)
|
||||
: ("complete" as const),
|
||||
retained_relations: relations.filter(({ relation }) =>
|
||||
relation === "defines"
|
||||
? family === "data-flow"
|
||||
: relation === "captures"
|
||||
? family === "closure"
|
||||
: family === "request",
|
||||
).length,
|
||||
omitted_relations: withUnknown && family === "call-flow" ? null : 0,
|
||||
unknown_ids:
|
||||
dynamic !== null && family === "call-flow" ? [dynamic.unknown_id] : [],
|
||||
}),
|
||||
);
|
||||
const fingerprint = createJavaScriptSemanticFingerprint(
|
||||
{
|
||||
function_node_id: callable.node_id,
|
||||
algorithm: "rea.javascript-semantic-function",
|
||||
status: "complete",
|
||||
components: {
|
||||
parameter_arity: 0,
|
||||
normalized_ast_sha256: "1".repeat(64),
|
||||
control_flow_sha256: "2".repeat(64),
|
||||
relation_shape_sha256: "3".repeat(64),
|
||||
literal_set_sha256: "4".repeat(64),
|
||||
effects: ["network"],
|
||||
},
|
||||
limitations: [],
|
||||
evidence: evidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
return createJavaScriptSemanticGraph({
|
||||
schema: "JavaScriptSemanticRelationGraph",
|
||||
root_artifact_sha256: SHA,
|
||||
application_graph_id: JAG_ID,
|
||||
root_node_ids: [module.node_id],
|
||||
evidence_contexts: evidenceContexts.contexts,
|
||||
nodes: [request, callable, binding, literal, module],
|
||||
relations,
|
||||
fingerprints: [fingerprint],
|
||||
unknowns,
|
||||
coverage: {
|
||||
status: withUnknown ? "partial" : "complete",
|
||||
truncated: false,
|
||||
omitted_nodes: withUnknown ? null : 0,
|
||||
omitted_relations: withUnknown ? null : 0,
|
||||
limits: [],
|
||||
families: coverageFamilies,
|
||||
},
|
||||
limitations: withUnknown ? ["Dynamic call target remains unknown."] : [],
|
||||
});
|
||||
};
|
||||
|
||||
const graphWithCandidateEdge = (): JavaScriptSemanticGraph => {
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
const input = constructionInput(graph);
|
||||
const evidenceContexts = registryForGraph(graph);
|
||||
const literal = graph.nodes.find(({ kind }) => kind === "literal");
|
||||
@@ -259,7 +72,7 @@ const graphWithCandidateEdge = (): JavaScriptSemanticGraph => {
|
||||
relation: "supplies-request-field",
|
||||
resolution: "candidate",
|
||||
properties: { field: "authorization" },
|
||||
evidence: evidence("inferred"),
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
@@ -303,7 +116,7 @@ const registryForGraph = (
|
||||
};
|
||||
|
||||
it("returns every relevant unknown inline", () => {
|
||||
const graph = fixtureGraph(true);
|
||||
const graph = semanticFixtureGraph(true);
|
||||
const callable = graph.nodes.find(({ kind }) => kind === "function");
|
||||
if (callable === undefined) throw new Error("Expected callable node");
|
||||
const result = queryJavaScriptSemanticGraph(graph, {
|
||||
@@ -317,7 +130,7 @@ it("returns every relevant unknown inline", () => {
|
||||
});
|
||||
|
||||
it("does not admit a semantic-node seed absent from the graph", () => {
|
||||
const result = queryJavaScriptSemanticGraph(fixtureGraph(), {
|
||||
const result = queryJavaScriptSemanticGraph(semanticFixtureGraph(), {
|
||||
seed: { kind: "semantic-node", node_id: `jsrg_node_${"f".repeat(64)}` },
|
||||
direction: "forward-influence",
|
||||
});
|
||||
@@ -331,7 +144,7 @@ it("does not admit a semantic-node seed absent from the graph", () => {
|
||||
});
|
||||
|
||||
it("returns the complete deterministic forward influence result inline", () => {
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
const input = {
|
||||
seed: { kind: "literal" as const, value: "TOKEN" },
|
||||
direction: "forward-influence" as const,
|
||||
@@ -348,7 +161,7 @@ it("returns the complete deterministic forward influence result inline", () => {
|
||||
});
|
||||
|
||||
it("returns only the referenced evidence contexts with resolvable trace facts", () => {
|
||||
const graph = fixtureGraph();
|
||||
const graph = semanticFixtureGraph();
|
||||
const result = queryJavaScriptSemanticGraph(graph, {
|
||||
seed: { kind: "literal", value: "TOKEN" },
|
||||
direction: "forward-influence",
|
||||
@@ -370,7 +183,7 @@ it("returns only the referenced evidence contexts with resolvable trace facts",
|
||||
});
|
||||
|
||||
it("traverses paths beyond the former fixed depth ceiling", () => {
|
||||
const original = fixtureGraph();
|
||||
const original = semanticFixtureGraph();
|
||||
const input = constructionInput(original);
|
||||
const evidenceContexts = registryForGraph(original);
|
||||
const request = original.nodes.find(({ kind }) => kind === "request");
|
||||
@@ -386,7 +199,7 @@ it("traverses paths beyond the former fixed depth ceiling", () => {
|
||||
relation: "reads",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
@@ -435,7 +248,7 @@ it("accepts deeply nested semantic properties", () => {
|
||||
});
|
||||
|
||||
it("keeps relevant dynamic frontiers unknown", () => {
|
||||
const result = queryJavaScriptSemanticGraph(fixtureGraph(true), {
|
||||
const result = queryJavaScriptSemanticGraph(semanticFixtureGraph(true), {
|
||||
seed: { kind: "literal", value: "TOKEN" },
|
||||
direction: "forward-influence",
|
||||
});
|
||||
@@ -502,7 +315,7 @@ it("retains all explicit unresolved candidate node identifiers", () => {
|
||||
{ length: 1_001 },
|
||||
(_, index) => `jsrg_node_${index.toString(16).padStart(64, "0")}`,
|
||||
),
|
||||
evidence: unknownEvidence(),
|
||||
evidence: unknownSemanticGraphTestEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
@@ -559,7 +372,7 @@ const relationBetween = (
|
||||
relation,
|
||||
resolution,
|
||||
properties: {},
|
||||
evidence: evidence("inferred"),
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
import type { JsonValue } from "../../src/domain/jsonValue.js";
|
||||
import type { ApplicationGraphEvidence } from "../../src/domain/javascript/javascriptApplicationEvidenceSchemas.js";
|
||||
import {
|
||||
JAVASCRIPT_SEMANTIC_NODE_KINDS,
|
||||
JAVASCRIPT_SEMANTIC_RELATION_FAMILIES,
|
||||
type JavaScriptSemanticGraphInput,
|
||||
type JavaScriptSemanticGraphNode,
|
||||
} from "../../src/domain/javascript/javascriptSemanticGraphSchemas.js";
|
||||
import {
|
||||
createJavaScriptSemanticFingerprint,
|
||||
createJavaScriptSemanticGraph,
|
||||
createJavaScriptSemanticGraphNode,
|
||||
createJavaScriptSemanticGraphRelation,
|
||||
createJavaScriptSemanticGraphUnknown,
|
||||
JavaScriptSemanticEvidenceContextRegistry,
|
||||
type JavaScriptSemanticGraph,
|
||||
} from "../../src/domain/javascript/javascriptSemanticGraph.js";
|
||||
|
||||
const SHA = "a".repeat(64);
|
||||
const JAG_ID = `jag_${"b".repeat(64)}`;
|
||||
const completeCoverage = {
|
||||
status: "complete",
|
||||
truncated: false,
|
||||
omitted_count: 0,
|
||||
limits: [],
|
||||
} satisfies ApplicationGraphEvidence["coverage"];
|
||||
|
||||
export const semanticGraphTestEvidence = (
|
||||
state: "observed" | "inferred" = "observed",
|
||||
): ApplicationGraphEvidence => ({
|
||||
authority:
|
||||
state === "observed"
|
||||
? "ast-static-analysis"
|
||||
: "static-relationship-inference",
|
||||
state,
|
||||
confidence: state === "observed" ? "exact" : "high",
|
||||
artifact: { available: true, artifact_id: `art_${SHA}`, sha256: SHA },
|
||||
location: {
|
||||
available: true,
|
||||
value: {
|
||||
kind: "source-range",
|
||||
source: "bundle.js",
|
||||
start: { line: 1, column: 0 },
|
||||
end: { line: 1, column: 10 },
|
||||
},
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "recover-semantic-relation",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: completeCoverage,
|
||||
limitations:
|
||||
state === "observed"
|
||||
? []
|
||||
: ["Static reachability does not prove runtime execution."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
export const unknownSemanticGraphTestEvidence =
|
||||
(): ApplicationGraphEvidence => ({
|
||||
authority: "unknown",
|
||||
state: "unknown",
|
||||
confidence: "unknown",
|
||||
artifact: {
|
||||
available: false,
|
||||
reason: "unknown",
|
||||
detail: "Unknown artifact.",
|
||||
},
|
||||
location: {
|
||||
available: false,
|
||||
reason: "unresolved",
|
||||
detail: "Dynamic call target.",
|
||||
},
|
||||
extractor: {
|
||||
name: "test",
|
||||
version: "1",
|
||||
operation: "retain-dynamic-call",
|
||||
executable_sha256: null,
|
||||
},
|
||||
coverage: {
|
||||
status: "unknown",
|
||||
truncated: false,
|
||||
omitted_count: null,
|
||||
limits: [],
|
||||
},
|
||||
limitations: ["Dynamic call target remains unknown."],
|
||||
evidence_ids: [],
|
||||
});
|
||||
|
||||
const fixtureNode = (
|
||||
kind: (typeof JAVASCRIPT_SEMANTIC_NODE_KINDS)[number],
|
||||
role: string,
|
||||
properties: Record<string, JsonValue> = {},
|
||||
evidenceContexts: JavaScriptSemanticEvidenceContextRegistry,
|
||||
): JavaScriptSemanticGraphNode =>
|
||||
createJavaScriptSemanticGraphNode(
|
||||
{
|
||||
kind,
|
||||
identity: {
|
||||
artifact_sha256: SHA,
|
||||
module_path: "bundle.js",
|
||||
source_range: {
|
||||
start: { line: 1, column: role.length },
|
||||
end: { line: 1, column: role.length + 1 },
|
||||
},
|
||||
role_key: role,
|
||||
},
|
||||
function_node_id: null,
|
||||
application_node_ids: [],
|
||||
label: role,
|
||||
properties,
|
||||
evidence: semanticGraphTestEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
|
||||
export const semanticFixtureInput = (
|
||||
withUnknown = false,
|
||||
): JavaScriptSemanticGraphInput => {
|
||||
const evidenceContexts = new JavaScriptSemanticEvidenceContextRegistry();
|
||||
const module = fixtureNode("module", "module", {}, evidenceContexts);
|
||||
const literal = fixtureNode(
|
||||
"literal",
|
||||
"literal",
|
||||
{ value: "TOKEN" },
|
||||
evidenceContexts,
|
||||
);
|
||||
const binding = fixtureNode("binding", "binding", {}, evidenceContexts);
|
||||
const callable = fixtureNode("function", "function", {}, evidenceContexts);
|
||||
const request = fixtureNode(
|
||||
"request",
|
||||
"request",
|
||||
{
|
||||
endpoint: "https://example.invalid/v1",
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
const relations = [
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: literal.node_id,
|
||||
target_node_id: binding.node_id,
|
||||
relation: "defines",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: binding.node_id,
|
||||
target_node_id: callable.node_id,
|
||||
relation: "captures",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
createJavaScriptSemanticGraphRelation(
|
||||
{
|
||||
source_node_id: callable.node_id,
|
||||
target_node_id: request.node_id,
|
||||
relation: "constructs-request",
|
||||
resolution: "resolved",
|
||||
properties: {},
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
),
|
||||
];
|
||||
const dynamic = withUnknown
|
||||
? createJavaScriptSemanticGraphUnknown(
|
||||
{
|
||||
node_id: callable.node_id,
|
||||
family: "call-flow",
|
||||
relation_kinds: ["calls"],
|
||||
reason: "dynamic-call",
|
||||
detail: "Computed callee is unresolved.",
|
||||
candidate_node_ids: [],
|
||||
evidence: unknownSemanticGraphTestEvidence(),
|
||||
},
|
||||
evidenceContexts,
|
||||
)
|
||||
: null;
|
||||
const unknowns = dynamic === null ? [] : [dynamic];
|
||||
const coverageFamilies = JAVASCRIPT_SEMANTIC_RELATION_FAMILIES.map(
|
||||
(family) => ({
|
||||
family,
|
||||
status:
|
||||
withUnknown && family === "call-flow"
|
||||
? ("unknown" as const)
|
||||
: ("complete" as const),
|
||||
retained_relations: relations.filter(({ relation }) =>
|
||||
relation === "defines"
|
||||
? family === "data-flow"
|
||||
: relation === "captures"
|
||||
? family === "closure"
|
||||
: family === "request",
|
||||
).length,
|
||||
omitted_relations: withUnknown && family === "call-flow" ? null : 0,
|
||||
unknown_ids:
|
||||
dynamic !== null && family === "call-flow" ? [dynamic.unknown_id] : [],
|
||||
}),
|
||||
);
|
||||
const fingerprint = createJavaScriptSemanticFingerprint(
|
||||
{
|
||||
function_node_id: callable.node_id,
|
||||
algorithm: "rea.javascript-semantic-function",
|
||||
status: "complete",
|
||||
components: {
|
||||
parameter_arity: 0,
|
||||
normalized_ast_sha256: "1".repeat(64),
|
||||
control_flow_sha256: "2".repeat(64),
|
||||
relation_shape_sha256: "3".repeat(64),
|
||||
literal_set_sha256: "4".repeat(64),
|
||||
effects: ["network"],
|
||||
},
|
||||
limitations: [],
|
||||
evidence: semanticGraphTestEvidence("inferred"),
|
||||
},
|
||||
evidenceContexts,
|
||||
);
|
||||
return {
|
||||
schema: "JavaScriptSemanticRelationGraph",
|
||||
root_artifact_sha256: SHA,
|
||||
application_graph_id: JAG_ID,
|
||||
root_node_ids: [module.node_id],
|
||||
evidence_contexts: evidenceContexts.contexts,
|
||||
nodes: [request, callable, binding, literal, module],
|
||||
relations,
|
||||
fingerprints: [fingerprint],
|
||||
unknowns,
|
||||
coverage: {
|
||||
status: withUnknown ? "partial" : "complete",
|
||||
truncated: false,
|
||||
omitted_nodes: withUnknown ? null : 0,
|
||||
omitted_relations: withUnknown ? null : 0,
|
||||
limits: [],
|
||||
families: coverageFamilies,
|
||||
},
|
||||
limitations: withUnknown ? ["Dynamic call target remains unknown."] : [],
|
||||
};
|
||||
};
|
||||
|
||||
export const semanticFixtureGraph = (
|
||||
withUnknown = false,
|
||||
): JavaScriptSemanticGraph =>
|
||||
createJavaScriptSemanticGraph(semanticFixtureInput(withUnknown));
|
||||
Reference in New Issue
Block a user