test(mcp): verify recovery before reading retained views (#7029)

This commit is contained in:
gensokyo-dev
2026-10-25 01:54:27 +08:00
committed by GitHub
parent ab30df08f7
commit bc9efc9279
@@ -50,7 +50,7 @@ it("delivers deadline recovery guidance at initialization before any tool call",
}
});
it("retains every static finding through public module views when application expansion exceeds memory, then accepts more work", async () => {
it("accepts more work after application expansion exceeds memory and retains every static finding", async () => {
const root = await createTestTempDirectory("rea-js-resource-boundary-");
const nextRoot = await createTestTempDirectory("rea-js-resource-recovery-");
await writeFile(join(nextRoot, "main.js"), "export const recovered = 9;\n");
@@ -92,6 +92,7 @@ it("retains every static finding through public module views when application ex
})
.parse(response.structuredContent).normalized_result;
expect(summary.summary.coverage.application.status).toBe("partial");
await verifyRecovery(client, nextRoot, diagnostics);
const retained = {
kind: "retained-evidence",
evidence_id: summary.parent_evidence_id,
@@ -177,18 +178,6 @@ it("retains every static finding through public module views when application ex
).toEqual(
Array.from({ length: 4000 }, (_, index) => ["./target.js", index + 1]),
);
await client.ping();
const next = await client.callTool({
name: "analyze_javascript_application",
arguments: {
input_path: nextRoot,
format: "directory",
max_heap_mb: 128,
detail: "summary",
},
});
expectRecoverySuccess(next, diagnostics);
await client.ping();
expect(
createHash("sha256")
.update(await readFile(path))
@@ -201,14 +190,17 @@ it("retains every static finding through public module views when application ex
}
}, 90000);
const repeatedSourceCount = 1500;
const repeatedSourceText = "export const observed = 7;\n";
it("returns a directly usable partial analysis reference for an oversized timeout observation", async () => {
const root = await createTestTempDirectory("rea-js-large-timeout-");
for (let offset = 0; offset < 1500; offset += 50)
for (let offset = 0; offset < repeatedSourceCount; offset += 50)
await Promise.all(
Array.from({ length: 50 }, (_, index) =>
writeFile(
join(root, `file-${String(offset + index)}.js`),
"export const observed = 7;\n",
repeatedSourceText,
),
),
);
@@ -250,6 +242,10 @@ it("returns a directly usable partial analysis reference for an oversized timeou
operation: "analyze_javascript_application",
timeout_ms: 1,
});
// Check worker recovery before materializing retained views: those views
// independently consume parent capacity. Then prove the old Evidence is
// still usable after the server has completed a different analysis.
await verifyRecovery(client, nextRoot, diagnostics);
const summaryResponse = await client.callTool({
name: "inspect_analysis_view",
arguments: {
@@ -273,24 +269,14 @@ it("returns a directly usable partial analysis reference for an oversized timeou
})
.parse(summaryResponse.structuredContent).normalized_result.summary;
expect(summary.statistics).toEqual({
relevant_files: 1500,
relevant_files: repeatedSourceCount,
parsed_javascript_files: 0,
parse_failures: 0,
});
expect(summary.coverage.semantic_unknowns).toBeGreaterThan(1500);
expect(summary.coverage.semantic_unknowns).toBeGreaterThan(
repeatedSourceCount,
);
await verifyRepeatedSourcePaths(client, partial);
await client.ping();
const next = await client.callTool({
name: "analyze_javascript_application",
arguments: {
input_path: nextRoot,
format: "directory",
max_heap_mb: 128,
detail: "summary",
},
});
expectRecoverySuccess(next, diagnostics);
await client.ping();
expect(diagnostics()).not.toMatch(/FATAL ERROR:.*heap/iu);
} finally {
await client.close();
@@ -298,14 +284,38 @@ it("returns a directly usable partial analysis reference for an oversized timeou
}
}, 90000);
const expectRecoverySuccess = (
next: Awaited<ReturnType<Client["callTool"]>>,
const verifyRecovery = async (
client: Client,
nextRoot: string,
diagnostics: () => string,
): void => {
): Promise<void> => {
await client.ping();
const next = await client.callTool({
name: "analyze_javascript_application",
arguments: {
input_path: nextRoot,
format: "directory",
max_heap_mb: 128,
detail: "summary",
},
});
expect(
next.isError,
JSON.stringify({ next, diagnostics: diagnostics() }),
).not.toBe(true);
expect(next.structuredContent).toMatchObject({
normalized_result: {
summary: {
input_path: nextRoot,
statistics: {
relevant_files: 1,
parsed_javascript_files: 1,
parse_failures: 0,
},
},
},
});
await client.ping();
};
const createResourceClient = () => {
@@ -331,52 +341,65 @@ const verifyRepeatedSourcePaths = async (
client: Client,
source: { readonly kind: "retained-evidence"; readonly evidence_id: string },
): Promise<void> => {
const page = await client.callTool({
name: "inspect_analysis_view",
arguments: {
source,
view: { kind: "page", collection: "modules", offset: 0, limit: 4 },
},
});
expect(page.isError).not.toBe(true);
const modules = z
.object({
normalized_result: z.object({
items: z.array(z.object({ kind: z.string(), node_id: z.string() })),
}),
})
.parse(page.structuredContent);
const asset = modules.normalized_result.items.find(
({ kind }) => kind === "javascript-asset",
);
if (asset === undefined)
throw new Error(
"Identical source files disappeared from the retained graph",
);
const item = await client.callTool({
const response = await client.callTool({
name: "inspect_analysis_view",
arguments: {
source,
view: {
kind: "item",
collection: "modules",
selector: { node_id: asset.node_id },
kind: "javascript-facts",
where: { kinds: ["javascript-asset"] },
property_keys: [],
limit: repeatedSourceCount,
},
},
});
expect(item.isError).not.toBe(true);
const observations = z
expect(response.isError).not.toBe(true);
// Every occurrence is independently identified and sourced. No module
// properties are needed, and a full item would retain unrelated data in
// the same deliberately heap-constrained server used for recovery.
const digest = createHash("sha256").update(repeatedSourceText).digest("hex");
const facts = z
.object({
normalized_result: z.object({
item: z.object({
observations: z.array(z.object({ label: z.string() })),
rows: z.array(
z.object({
node_id: z.string(),
observation_id: z.string(),
identity: z.object({
strategy: z.literal("content-digest"),
sha256: z.literal(digest),
}),
properties: z.strictObject({}),
evidence: z.object({
location: z.object({
available: z.literal(true),
value: z.object({
kind: z.literal("artifact-path"),
path: z.string(),
}),
}),
}),
}),
),
coverage: z.object({
examined: z.literal(repeatedSourceCount),
total: z.literal(repeatedSourceCount),
exhausted: z.literal(true),
next_offset: z.null(),
}),
}),
})
.parse(item.structuredContent).normalized_result.item.observations;
expect(observations.map(({ label }) => label).sort()).toEqual(
.parse(response.structuredContent).normalized_result;
expect(facts.rows).toHaveLength(repeatedSourceCount);
expect(new Set(facts.rows.map(({ node_id }) => node_id)).size).toBe(1);
expect(
new Set(facts.rows.map(({ observation_id }) => observation_id)).size,
).toBe(repeatedSourceCount);
expect(
facts.rows.map(({ evidence }) => evidence.location.value.path).sort(),
).toEqual(
Array.from(
{ length: 1500 },
{ length: repeatedSourceCount },
(_, index) => `file-${String(index)}.js`,
).sort(),
);