Files
rea/tests/boundary/process/processCaptureCallerReason.test.ts
morlutoandDeryFerd d6ca7b3f20 fix(process): gate launch and retain cancellation provenance
Check cancellation at the PTY execution boundary after executable sampling
and descriptor cleanup settle. Completed file observations cannot authorize
launch for a cancelled request. Preserve exact caller reasons in the shared
capture cancellation constructor and its admission, exit and finalization
consumers instead of reconstructing them downstream.

Exercise real executable reads and closes through the capture API with a
throwing PTY sentinel at five schedules. Retain a real completed Node capture
and its cleanup facts when the final progress callback cancels the request.
Strengthen existing metadata and baseline cancellation controls.

Adapted after review of #4836 at 014afa3892.
Raw snapshot/sampler descriptor cleanup ownership remains tracked in #5140;
this execution-admission change does not claim to solve failed close recovery.

Closes #4512
Closes #5142

Co-authored-by: DeryFerd <deryferdikao125@gmail.com>
2026-10-19 09:14:00 +08:00

49 lines
1.6 KiB
TypeScript

import { expect } from "vitest";
import { parseProcessScenario } from "../../../src/domain/process/processScenario.js";
import { captureProcessScenario } from "../../../src/process/capture/ProcessHarness.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { itWithCaptureCapability } from "./processCaptureCapability.js";
itWithCaptureCapability(
"retains completed capture facts and the exact reason when cleanup reporting cancels",
async () => {
const root = await createTestTempDirectory("rea-capture-reason-");
const controller = new AbortController();
const reason = { request: "cancel after cleanup completed" };
let cleanupReported = false;
const result = await captureProcessScenario(
parseProcessScenario({
executable: process.execPath,
arguments: ["--eval", "console.log('completed capture')"],
working_directory: root,
settle_ms: 0,
}),
controller.signal,
process.platform,
process.env,
undefined,
undefined,
{
report: async (update) => {
if (update.phase === "cleanup" && update.terminal === true) {
cleanupReported = true;
controller.abort(reason);
}
},
},
);
expect(cleanupReported).toBe(true);
if (result.ok) throw new Error("Expected finalization cancellation");
expect(result.error.cause).toBe(reason);
expect(result.error).toMatchObject({
reason: "cancelled",
partialObservation: {
capture: { exit: { code: 0 } },
cleanup: { temporary_root: { state: "cleaned" } },
},
});
},
20_000,
);