mirror of
https://github.com/morluto/rea.git
synced 2026-10-26 17:35:11 +00:00
test(sqlite): preserve worker failures through cleanup retry
This commit is contained in:
1 parent
f774dc420b
commit
21a20306b4
2 files changed
+81
No files matched your search
@@ -184,6 +184,11 @@ retains the completed Evidence and the resource owner for cleanup retry. MCP
|
|||||||
records that Evidence before delivering the error, which carries its retained
|
records that Evidence before delivering the error, which carries its retained
|
||||||
evidence ID for later retrieval or export.
|
evidence ID for later retrieval or export.
|
||||||
|
|
||||||
|
If a worker fails and cleanup remains incomplete, the error retains the original
|
||||||
|
cancellation, timeout, diagnostic overflow or memory failure. Successful cleanup
|
||||||
|
retry restores that failure's classification; it does not make the inspection
|
||||||
|
successful.
|
||||||
|
|
||||||
Changed input identities, special files, malformed databases, unsupported
|
Changed input identities, special files, malformed databases, unsupported
|
||||||
formats and resource failures are reported as errors with the selected path and
|
formats and resource failures are reported as errors with the selected path and
|
||||||
meaningful reason. Worker resource refusals expose the caller-selected file in
|
meaningful reason. Worker resource refusals expose the caller-selected file in
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { fileURLToPath } from "node:url";
|
|||||||
import { expect, it as nativeTest, onTestFinished } from "vitest";
|
import { expect, it as nativeTest, onTestFinished } from "vitest";
|
||||||
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
||||||
import { ProviderCleanupError } from "../../../src/domain/providerCleanupError.js";
|
import { ProviderCleanupError } from "../../../src/domain/providerCleanupError.js";
|
||||||
|
import { SQLITE_DATABASE_LIMITS } from "../../../src/sqlite/SqliteDatabaseLimits.js";
|
||||||
import { SqliteDatabaseProvider } from "../../../src/sqlite/SqliteDatabaseProvider.js";
|
import { SqliteDatabaseProvider } from "../../../src/sqlite/SqliteDatabaseProvider.js";
|
||||||
import { inspectSqliteDatabaseSnapshot } from "../../../src/sqlite/SqliteDatabaseInspection.js";
|
import { inspectSqliteDatabaseSnapshot } from "../../../src/sqlite/SqliteDatabaseInspection.js";
|
||||||
import { cleanupOwnedProcessGroup } from "../../../src/process/ProcessOwnership.js";
|
import { cleanupOwnedProcessGroup } from "../../../src/process/ProcessOwnership.js";
|
||||||
@@ -1269,3 +1270,78 @@ nativeTest(
|
|||||||
await expectPrivateRootRemoved(ownedRoot);
|
await expectPrivateRootRemoved(ownedRoot);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
nativeTest.each([
|
||||||
|
["cancelled", "cancelled"],
|
||||||
|
["output-limit", "unreadable_output"],
|
||||||
|
["heap-diagnostic", "resource_constraint"],
|
||||||
|
] as const)(
|
||||||
|
"preserves the typed %s worker failure through retained cleanup retry",
|
||||||
|
async (scenario, code) => {
|
||||||
|
const { database } = await createFixture();
|
||||||
|
const controller = new AbortController();
|
||||||
|
const reason = { requested: "cancel retained SQLite worker" };
|
||||||
|
let cleanupAllowed = false;
|
||||||
|
let ownedRoot = "";
|
||||||
|
let launches = 0;
|
||||||
|
const provider = new SqliteDatabaseProvider({}, async (spawn) => {
|
||||||
|
launches += 1;
|
||||||
|
ownedRoot = spawn.cwd ?? "";
|
||||||
|
const script =
|
||||||
|
scenario === "heap-diagnostic"
|
||||||
|
? "process.stderr.write('JavaScript heap out of memory'); process.exitCode = 3"
|
||||||
|
: scenario === "output-limit"
|
||||||
|
? `process.stdout.write('x'.repeat(${String(SQLITE_DATABASE_LIMITS.diagnosticBytes + 1)})); setInterval(() => {}, 1000)`
|
||||||
|
: "process.stdout.write('ready\\n'); setInterval(() => {}, 1000)";
|
||||||
|
const launched = await spawnOwnedProviderProcess({
|
||||||
|
...spawn,
|
||||||
|
arguments: ["-e", script],
|
||||||
|
});
|
||||||
|
const cleanupOwned =
|
||||||
|
launched.cleanup ??
|
||||||
|
(() => cleanupOwnedProcessGroup(launched.ownership));
|
||||||
|
if (scenario === "cancelled") {
|
||||||
|
await waitForProviderProcessReady(launched.process);
|
||||||
|
setImmediate(() => controller.abort(reason));
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...launched,
|
||||||
|
cleanup: async () => {
|
||||||
|
const cleaned = await cleanupOwned();
|
||||||
|
if (!cleanupAllowed)
|
||||||
|
throw new Error("controlled SQLite worker cleanup refusal");
|
||||||
|
return cleaned;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
});
|
||||||
|
onTestFinished(async () => {
|
||||||
|
cleanupAllowed = true;
|
||||||
|
await provider.close();
|
||||||
|
});
|
||||||
|
const result = await provider.inspect(
|
||||||
|
{ path: database.path },
|
||||||
|
{ signal: controller.signal },
|
||||||
|
);
|
||||||
|
if (result.ok) throw new Error("Expected retained worker cleanup");
|
||||||
|
expect(projectAnalysisError(result.error).code).toBe("cleanup_incomplete");
|
||||||
|
const primary = result.error.cleanupPrimary;
|
||||||
|
if (primary === undefined) throw new Error("Missing typed worker failure");
|
||||||
|
expect(projectAnalysisError(primary).code).toBe(code);
|
||||||
|
if (scenario === "cancelled")
|
||||||
|
expect(result.error.cleanupPrimary?.cause).toBe(reason);
|
||||||
|
expect(await provider.inspect({ path: database.path })).toMatchObject({
|
||||||
|
ok: false,
|
||||||
|
error: { cleanupIncomplete: true },
|
||||||
|
});
|
||||||
|
expect(launches).toBe(1);
|
||||||
|
await access(ownedRoot);
|
||||||
|
cleanupAllowed = true;
|
||||||
|
await provider.close();
|
||||||
|
expect(result.error.cleanupIncomplete).toBe(false);
|
||||||
|
expect(result.error.cleanupResources).toEqual([]);
|
||||||
|
expect(projectAnalysisError(result.error).code).toBe(code);
|
||||||
|
if (scenario === "cancelled")
|
||||||
|
expect(result.error.cleanupPrimary?.cause).toBe(reason);
|
||||||
|
await expectPrivateRootRemoved(ownedRoot);
|
||||||
|
},
|
||||||
|
);
|
||||||
Reference in new issue
Block a user