Files
rea/tests/acceptance/applications/javascriptTimerWritesCli.test.ts
morlutoandCodex 6183544e8b fix(javascript): select timer handles at cancellation reads
Use the shared binding-at-read owner instead of grouping schedules across the
whole binding lifetime. Match selected initializer nodes, retain uncertain
origins as candidate links, and preserve immutable closure captures.

Adapt the diagnosis in PR #3794 by @Xinglan233, whose source commit was authored
by Codex. Verify stale writes, later schedules, replacements and CLI graph links.

Refs #3358, #3794

Co-authored-by: Codex <codex@openai.com>
2026-10-16 06:06:11 +08:00

55 lines
1.9 KiB
TypeScript

import { expect } from "vitest";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js";
cliTest(
"links cancellation to the timer handle reaching its read through the CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-timer-reaching-write-");
await writeFixtureFiles(root, {
"app.mjs": `
function cleared() {
let handle = setTimeout(work, 10);
handle = null;
clearTimeout(handle);
}
function latest() {
let handle = setTimeout(work, 20);
handle = setTimeout(work, 30);
clearTimeout(handle);
}
function conditional() {
let handle = setTimeout(work, 40);
if (condition) handle = null;
clearTimeout(handle);
}
`,
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
const graph = result.semantic_graph;
expect(graph).not.toBeNull();
if (graph === null) throw new Error("Expected a semantic graph");
const cancellationTargets = graph.relations
.filter(({ relation }) => relation === "cancels-timer")
.map(({ resolution, target_node_id }) => ({
resolution,
delay: graph.nodes.find(({ node_id }) => node_id === target_node_id)
?.properties.delay_milliseconds,
}));
expect(cancellationTargets).toEqual(
expect.arrayContaining([
{ resolution: "resolved", delay: 30 },
{ resolution: "candidate", delay: 40 },
]),
);
expect(cancellationTargets).toHaveLength(2);
expect(
graph.unknowns.filter(({ family }) => family === "timer"),
).toHaveLength(2);
},
);