fix(javascript): preserve semantic query identity and source coverage

This commit is contained in:
morluto committed 2026-10-24 03:00:08 +08:00
1 parent 34552ff0df
commit cf5e4f6fa3
16 files changed
+362 -51

No files matched your search

@@ -0,0 +1,72 @@
import { expect, it } from "vitest";
import { graphForJavaScript } from "../../../tests/fixtures/javascriptSemanticGraph.js";
import { queryJavaScriptSemanticGraph } from "../../domain/javascript/javascriptSemanticQuery.js";
it.each([
"function read() { return this.speed; }",
"class Game { read() { return this.config.speed; } }",
])(
"matches retained slotless property accesses and their unknowns: %s",
(source) => {
const graph = graphForJavaScript(source);
const accesses = graph.nodes.filter(
({ kind, properties }) =>
kind === "expression" &&
properties.property_slot_resolution === "unresolved" &&
Array.isArray(properties.property_path) &&
properties.property_path.at(-1) === "speed",
);
expect(accesses.length).toBeGreaterThan(0);
const trace = queryJavaScriptSemanticGraph(graph, {
seed: { kind: "property", name: "speed" },
direction: "forward-influence",
});
expect(trace.seed_node_ids).toEqual(
accesses.map(({ node_id }) => node_id).sort(),
);
const relevant = graph.unknowns.filter(({ node_id }) =>
accesses.some((node) => node.node_id === node_id),
);
expect(relevant.length).toBeGreaterThan(0);
expect(trace.unknowns.map(({ unknown_id }) => unknown_id)).toEqual(
expect.arrayContaining(relevant.map(({ unknown_id }) => unknown_id)),
);
expect(
queryJavaScriptSemanticGraph(graph, {
seed: { kind: "property", name: "absent" },
direction: "forward-influence",
}).seed_node_ids,
).toEqual([]);
},
);
it("keeps resolved property seeds on their slot without duplicating access occurrences", () => {
const graph = graphForJavaScript(
"const state = { speed: 1 }; const result = state.speed;",
);
const trace = queryJavaScriptSemanticGraph(graph, {
seed: { kind: "property", name: "speed" },
direction: "backward-provenance",
});
expect(trace.seed_node_ids).toHaveLength(1);
expect(
graph.nodes.find(({ node_id }) => node_id === trace.seed_node_ids[0])?.kind,
).toBe("property-slot");
});
it("queries an empty event name as an exact retained event identity", () => {
const graph = graphForJavaScript(
'const { EventEmitter } = require("node:events"); const bus = new EventEmitter(); bus.on("", function emptyListener() {}); bus.on("data", function dataListener() {});',
);
const trace = queryJavaScriptSemanticGraph(graph, {
seed: { kind: "event", name: "" },
direction: "forward-influence",
});
expect(trace.seed_node_ids.length).toBeGreaterThan(0);
expect(
trace.nodes
.filter(({ node_id }) => trace.seed_node_ids.includes(node_id))
.every(({ properties }) => properties.event_name === ""),
).toBe(true);
});