paperclip/tests/runner-e2e/process-tree.test.ts

107 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
observeDescendantProcessTree,
refreshContinuouslyLiveProcessGroups,
revalidateObservedProcessGroups,
safeProcessGroupTerminationOrder,
type ProcessObservation,
} from "./process-tree.js";
function process(
pid: number,
parentPid: number,
processGroupId: number,
started = `start-${pid}`,
): ProcessObservation {
return { pid, parentPid, processGroupId, started, kind: "node" };
}
describe("runner E2E process-tree cleanup", () => {
it("orders verified nested groups before the outer group and excludes unsafe groups", () => {
const table = [
process(100, 10, 100),
process(101, 100, 100),
process(200, 101, 200),
process(300, 200, 300),
process(301, 300, 300),
process(400, 101, 10),
process(500, 999, 500),
];
const observed = observeDescendantProcessTree(table, 100);
expect(
safeProcessGroupTerminationOrder({
rootProcessGroupId: 100,
currentProcessGroupId: 10,
groups: observed.groups,
}),
).toEqual([300, 200, 100]);
});
it("rejects a reused pid whose start identity no longer matches", () => {
const observed = observeDescendantProcessTree(
[process(100, 10, 100), process(200, 100, 200)],
100,
);
expect(
revalidateObservedProcessGroups(observed.groups, [
process(100, 10, 100),
process(200, 1, 200, "reused-process"),
]).map((group) => group.processGroupId),
).toEqual([100]);
});
it("refuses every group when launcher identity is unavailable", () => {
const observed = observeDescendantProcessTree(
[process(100, 10, 100), process(200, 100, 200)],
100,
);
expect(
safeProcessGroupTerminationOrder({
rootProcessGroupId: 100,
currentProcessGroupId: null,
groups: observed.groups,
}),
).toEqual([]);
});
it("does not add an unobserved root process group after revalidation", () => {
expect(
safeProcessGroupTerminationOrder({
rootProcessGroupId: 100,
currentProcessGroupId: 10,
groups: [
{
processGroupId: 200,
depth: 1,
members: [{ pid: 200, started: "start-200" }],
},
],
}),
).toEqual([200]);
});
it("retains a continuously live group when a cleanup helper replaces its original member", () => {
const observed = observeDescendantProcessTree(
[process(100, 10, 100), process(200, 100, 200)],
100,
);
const verified = revalidateObservedProcessGroups(observed.groups, [
process(100, 10, 100),
process(200, 100, 200),
]);
const refreshed = refreshContinuouslyLiveProcessGroups(verified, [
process(100, 10, 100),
process(201, 1, 200),
]);
expect(
refreshed.find((group) => group.processGroupId === 200)?.members,
).toEqual([{ pid: 201, started: "start-201" }]);
expect(
refreshContinuouslyLiveProcessGroups(refreshed, [
process(100, 10, 100),
]).map((group) => group.processGroupId),
).toEqual([100]);
});
});