mirror of
https://github.com/morluto/rea.git
synced 2026-11-12 09:52:27 +00:00
* fix(process): retain CLI input cleanup ownership * test(ci): align lifecycle fixtures with typed ownership contracts --------- Co-authored-by: Nianze Wu <wunianze666@gmail.com>
105 lines
4.5 KiB
TypeScript
105 lines
4.5 KiB
TypeScript
import { mkdir, readFile, stat } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it, vi } from "vitest";
|
|
|
|
import { ArtifactResourceScope } from "../../../src/artifacts/ArtifactResourceScope.js";
|
|
import { runDesktopBuildOwnedCommand } from "../../../src/desktop/DesktopBuildOwnedCommand.js";
|
|
import { prepareProcessOwnershipInspection } from "../../../src/process/ProcessOwnershipObservation.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
it.skipIf(process.platform === "win32")(
|
|
"stops the packing process family before removing its partial archive on timeout",
|
|
{ timeout: 15_000 },
|
|
async () => {
|
|
await prepareProcessOwnershipInspection();
|
|
const directory = await createTestTempDirectory("rea-owned-pack-");
|
|
const partial = join(directory, "partial.tar.xz");
|
|
const workspace = join(directory, "work");
|
|
await mkdir(workspace);
|
|
const pidFile = join(directory, "child.pid");
|
|
const child = `require('node:fs').writeFileSync(process.argv[1], String(process.pid)); require('node:fs').writeFileSync(require('node:path').join(require('node:os').tmpdir(), 'child-work'), 'temporary'); setInterval(() => {}, 1000);`;
|
|
const parent = `const {spawn} = require('node:child_process'); require('node:fs').writeFileSync(process.argv[1], 'partial'); spawn(process.execPath, ['-e', process.argv[3], process.argv[2]], {stdio:'inherit'}); setInterval(() => {}, 1000);`;
|
|
const resources = new ArtifactResourceScope();
|
|
try {
|
|
await expect(
|
|
runDesktopBuildOwnedCommand({
|
|
command: process.execPath,
|
|
arguments: ["-e", parent, partial, pidFile, child],
|
|
environment: {
|
|
...process.env,
|
|
TMPDIR: workspace,
|
|
TMP: workspace,
|
|
TEMP: workspace,
|
|
},
|
|
timeoutMs: 2000,
|
|
maxOutputBytes: 4096,
|
|
resources,
|
|
partialPath: partial,
|
|
workspace,
|
|
}),
|
|
).rejects.toMatchObject({
|
|
_tag: "AnalysisOutputError",
|
|
operation: "setup",
|
|
diagnostics: { command_failure: "timeout" },
|
|
});
|
|
const pid = Number(await readFile(pidFile, "utf8"));
|
|
expect(Number.isInteger(pid)).toBe(true);
|
|
expect(() => process.kill(pid, 0)).toThrow();
|
|
await expect(stat(partial)).rejects.toMatchObject({ code: "ENOENT" });
|
|
await expect(stat(workspace)).rejects.toMatchObject({ code: "ENOENT" });
|
|
expect(resources.hasPending).toBe(false);
|
|
} finally {
|
|
await resources.close();
|
|
}
|
|
},
|
|
);
|
|
|
|
it.skipIf(process.platform === "win32")(
|
|
"cancels a running setup process family before removing its partial output",
|
|
{ timeout: 15_000 },
|
|
async () => {
|
|
await prepareProcessOwnershipInspection();
|
|
const directory = await createTestTempDirectory("rea-owned-pack-cancel-");
|
|
const partial = join(directory, "partial.tar.xz");
|
|
const pidFile = join(directory, "child.pid");
|
|
const child = `require('node:fs').writeFileSync(process.argv[1], String(process.pid)); setInterval(() => {}, 1000);`;
|
|
const parent = `const {spawn} = require('node:child_process'); require('node:fs').writeFileSync(process.argv[1], 'partial'); spawn(process.execPath, ['-e', process.argv[3], process.argv[2]], {stdio:'inherit'}); setInterval(() => {}, 1000);`;
|
|
const resources = new ArtifactResourceScope();
|
|
const controller = new AbortController();
|
|
const reason = new Error("caller stopped setup");
|
|
const running = runDesktopBuildOwnedCommand({
|
|
command: process.execPath,
|
|
arguments: ["-e", parent, partial, pidFile, child],
|
|
environment: process.env,
|
|
timeoutMs: 10_000,
|
|
maxOutputBytes: 4096,
|
|
resources,
|
|
partialPath: partial,
|
|
signal: controller.signal,
|
|
});
|
|
void running.catch(() => undefined);
|
|
try {
|
|
await vi.waitFor(
|
|
async () => {
|
|
expect(Number(await readFile(pidFile, "utf8"))).toBeGreaterThan(0);
|
|
},
|
|
{ timeout: 5000 },
|
|
);
|
|
const pid = Number(await readFile(pidFile, "utf8"));
|
|
controller.abort(reason);
|
|
await expect(running).rejects.toMatchObject({
|
|
_tag: "AnalysisCancelledError",
|
|
operation: "setup",
|
|
cause: reason,
|
|
diagnostics: { command_failure: "cancelled" },
|
|
});
|
|
expect(() => process.kill(pid, 0)).toThrow();
|
|
await expect(stat(partial)).rejects.toMatchObject({ code: "ENOENT" });
|
|
} finally {
|
|
controller.abort(reason);
|
|
await running.catch(() => undefined);
|
|
await resources.close();
|
|
}
|
|
},
|
|
);
|