fix(javascript): accept empty semantic property seeds (#4239)
This commit is contained in:
1 parent
5b08d04302
commit
3eeada71fd
2 files changed
+77
-2
No files matched your search
@@ -41,7 +41,7 @@ export const javaScriptSemanticQuerySeedSchema = z.discriminatedUnion("kind", [
|
||||
}),
|
||||
z.strictObject({
|
||||
kind: z.literal("property"),
|
||||
name: z.string().min(1),
|
||||
name: z.string(),
|
||||
}),
|
||||
z.strictObject({
|
||||
kind: z.literal("endpoint"),
|
||||
|
||||
@@ -197,6 +197,78 @@ it("excludes local exports from writes-only traces through CLI and MCP", async (
|
||||
}
|
||||
});
|
||||
|
||||
it("traces exact empty property names through CLI and MCP", async () => {
|
||||
const root = await createTestTempDirectory("rea-semantic-empty-property-");
|
||||
await writeFile(
|
||||
join(root, "main.js"),
|
||||
"const routes = {'': 'HOME', x: 'NAMED'}; const root = routes['']; const named = routes.x;\n",
|
||||
);
|
||||
const cli = createCli({});
|
||||
const analyzed = await serveCli(cli, [
|
||||
"analyze-javascript-application",
|
||||
root,
|
||||
"--json",
|
||||
]);
|
||||
expect(analyzed.exitCode).toBe(0);
|
||||
const application = parseEvidence(JSON.parse(analyzed.stdout));
|
||||
const { semantic_graph: graph } =
|
||||
javascriptApplicationAnalysisResultSchema.parse(
|
||||
application.normalized_result,
|
||||
);
|
||||
const harness = await createApplicationMcpHarness();
|
||||
try {
|
||||
for (const name of ["", "x"]) {
|
||||
const slots = graph.nodes.filter(
|
||||
(node) =>
|
||||
node.kind === "property-slot" && node.properties.name === name,
|
||||
);
|
||||
expect(slots).toHaveLength(1);
|
||||
const slot = slots[0];
|
||||
if (slot === undefined)
|
||||
throw new Error("Expected retained property slot");
|
||||
const exact = await assertSuccessfulTraceParity(
|
||||
cli,
|
||||
harness,
|
||||
application,
|
||||
{
|
||||
seed: { kind: "semantic-node", node_id: slot.node_id },
|
||||
direction: "forward-influence",
|
||||
},
|
||||
);
|
||||
const selected = await assertSuccessfulTraceParity(
|
||||
cli,
|
||||
harness,
|
||||
application,
|
||||
{
|
||||
seed: { kind: "property", name },
|
||||
direction: "forward-influence",
|
||||
},
|
||||
);
|
||||
expect(selected.seed_node_ids).toEqual([slot.node_id]);
|
||||
expect(selected.nodes).toEqual(exact.nodes);
|
||||
expect(selected.relations).toEqual(exact.relations);
|
||||
expect(selected.nodes).toHaveLength(2);
|
||||
expect(selected.relations).toHaveLength(1);
|
||||
expect(selected.coverage.status).toBe("partial");
|
||||
}
|
||||
const absent = await assertSuccessfulTraceParity(
|
||||
cli,
|
||||
harness,
|
||||
application,
|
||||
{
|
||||
seed: { kind: "property", name: "missing" },
|
||||
direction: "forward-influence",
|
||||
},
|
||||
);
|
||||
expect(absent.seed_node_ids).toEqual([]);
|
||||
expect(absent.nodes).toEqual([]);
|
||||
expect(absent.relations).toEqual([]);
|
||||
expect(absent.coverage.status).toBe("partial");
|
||||
} finally {
|
||||
await harness.close();
|
||||
}
|
||||
});
|
||||
|
||||
const assertSuccessfulTraceParity = async (
|
||||
cli: ReturnType<typeof createCli>,
|
||||
harness: Awaited<ReturnType<typeof createApplicationMcpHarness>>,
|
||||
@@ -213,6 +285,10 @@ const assertSuccessfulTraceParity = async (
|
||||
JSON.stringify(arguments_),
|
||||
"--json",
|
||||
]);
|
||||
expect(cliResponse.exitCode, cliResponse.stdout).toBe(0);
|
||||
expect(JSON.parse(cliResponse.stdout), cliResponse.stdout).not.toHaveProperty(
|
||||
"error",
|
||||
);
|
||||
const cliEvidence = parseEvidence(JSON.parse(cliResponse.stdout));
|
||||
const cliResult = javaScriptSemanticTraceResultSchema.parse(
|
||||
cliEvidence.normalized_result,
|
||||
@@ -274,7 +350,6 @@ const assertTraceContextsResolve = (
|
||||
expect([...referencedIds].toSorted()).toEqual(
|
||||
[...contexts.keys()].toSorted(),
|
||||
);
|
||||
expect(contexts.size).toBeGreaterThan(0);
|
||||
};
|
||||
|
||||
const serveCli = async (
|
||||
|
||||
Reference in new issue
Block a user