fix(javascript): preserve cleanup failures during preparation cancellation (#1654)
This commit is contained in:
1 parent
bc7846db91
commit
890dfa49f6
3 files changed
+78
-13
No files matched your search
@@ -1,6 +1,9 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { expect, it } from "vitest";
|
||||
import { JavaScriptAnalysisWorker } from "../../../src/javascript/analysis/JavaScriptAnalysisWorker.js";
|
||||
import { cleanupOwnedProcessGroup } from "../../../src/process/ProcessOwnership.js";
|
||||
import { ProviderCleanupError } from "../../../src/domain/providerCleanupError.js";
|
||||
import { projectAnalysisError } from "../../../src/domain/analysisErrorProjection.js";
|
||||
import { spawnOwnedProviderProcess } from "../../../src/process/ProviderProcess.js";
|
||||
import type { JavaScriptArtifactFile } from "../../../src/domain/javascript/javascriptArtifactFiles.js";
|
||||
|
||||
@@ -91,3 +94,51 @@ it("reports an actual startup deadline and closes its owned resources before sub
|
||||
await next.close();
|
||||
}
|
||||
});
|
||||
|
||||
it("preserves a failed owned cleanup retry when the next caller cancels before acquisition", async () => {
|
||||
const controller = new AbortController();
|
||||
let releaseAllowed = false;
|
||||
let cleanupAttempts = 0;
|
||||
const worker = new JavaScriptAnalysisWorker(
|
||||
{ heapMb: 128, timeoutMs: 30_000 },
|
||||
async (request) => {
|
||||
const launched = await spawnOwnedProviderProcess(request);
|
||||
return {
|
||||
...launched,
|
||||
async cleanup() {
|
||||
cleanupAttempts += 1;
|
||||
if (!releaseAllowed) {
|
||||
if (cleanupAttempts > 1) controller.abort();
|
||||
throw new Error("Fixture owned cleanup is unavailable");
|
||||
}
|
||||
return launched.cleanup === undefined
|
||||
? cleanupOwnedProcessGroup(launched.ownership)
|
||||
: launched.cleanup();
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
const next = new JavaScriptAnalysisWorker({ heapMb: 128, timeoutMs: 30_000 });
|
||||
try {
|
||||
const file = source("app.mjs", "export const observed = 7;");
|
||||
const first = await worker.analyze(file, 100);
|
||||
if (!first.ok) throw first.error.error;
|
||||
await expect(worker.close()).rejects.toBeInstanceOf(ProviderCleanupError);
|
||||
const interrupted = await next.analyze(file, 100, controller.signal);
|
||||
if (interrupted.ok)
|
||||
throw new Error("Unreleased ownership must block subsequent analysis");
|
||||
expect(controller.signal.aborted).toBe(true);
|
||||
expect(projectAnalysisError(interrupted.error.error)).toMatchObject({
|
||||
code: "cleanup_incomplete",
|
||||
});
|
||||
releaseAllowed = true;
|
||||
await worker.close();
|
||||
const recovered = await next.analyze(file, 100);
|
||||
if (!recovered.ok) throw recovered.error.error;
|
||||
expect(recovered.value.javascript.parse_status).toBe("complete");
|
||||
} finally {
|
||||
releaseAllowed = true;
|
||||
await worker.close();
|
||||
await next.close();
|
||||
}
|
||||
});
|
||||
Reference in new issue
Block a user