mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
* fix(process): run macOS ps from /bin/ps, not REA's PATH Owned process cleanup and process sampling ran "ps" through REA's PATH, so an unusable PATH turned a confirmable cleanup into an incomplete one (spawn ps ENOENT). macOS ships ps at /bin/ps on the sealed system volume. Linux keeps the documented procps-on-REA's-PATH contract. * test(process): stop the ps fixture only after its descendant spawned * fix(process): run Linux ps from fixed system locations before REA's PATH Linux process ownership now runs the first executable of /bin/ps, /usr/bin/ps and /run/current-system/sw/bin/ps, searching REA's PATH only when none exists. Admission refusals name the ps that ran in their reason and ps_command/ps_source diagnostics; cleanup failure reasons keep the executed command. Doctor and remediation text follow the same selection. The macOS cleanup test now releases its fixture group even when the descendant wait fails. * fix(application): keep which ps ran in provider availability ownedProcessPrerequisiteAvailability copied only the admission and system codes from a Linux ps refusal, so Ghidra, JADX and Apktool status lost ps_command and ps_source. Copy the refusal's prerequisite facts from one key list.
69 lines
2.7 KiB
TypeScript
69 lines
2.7 KiB
TypeScript
import { expect, it, vi } from "vitest";
|
|
|
|
const processList = vi.hoisted(() => vi.fn());
|
|
vi.mock("./ExecFileOutput.js", async (original) => ({
|
|
...(await original<typeof import("./ExecFileOutput.js")>()),
|
|
execFileOutput: processList,
|
|
}));
|
|
|
|
import { AnalysisOutputError } from "../domain/analysisErrorCore.js";
|
|
import { ExecFileOutputCancelledError } from "./ExecFileOutput.js";
|
|
import { ProcessOwnershipInspectionCancelled } from "./ProcessInspectionCancellation.js";
|
|
import { createSystemProcessOwnershipHost } from "./ProcessOwnershipObservation.js";
|
|
import { readRunTokenCarriers } from "./ProcessRunTokenObservations.js";
|
|
|
|
const routes = ["list", "carriers"] as const;
|
|
const reasons = [
|
|
null,
|
|
{ caller: "opaque" },
|
|
new AnalysisOutputError("authored-read", "opaque error"),
|
|
] as const;
|
|
|
|
it.each(
|
|
routes.flatMap((route) => reasons.map((reason) => ({ route, reason }))),
|
|
)(
|
|
"retains explicit native command cancellation before $route parsing with opaque reason $reason",
|
|
async ({ route, reason }) => {
|
|
const controller = new AbortController();
|
|
controller.abort(reason);
|
|
const native = new ExecFileOutputCancelledError(reason);
|
|
processList.mockReset().mockRejectedValue(native);
|
|
const host = createSystemProcessOwnershipHost("linux", {});
|
|
const pending =
|
|
route === "list"
|
|
? host.listProcesses(controller.signal)
|
|
: readRunTokenCarriers(host, "authored-run", controller.signal);
|
|
const failure = await pending.catch((cause: unknown) => cause);
|
|
expect(failure).toBeInstanceOf(ProcessOwnershipInspectionCancelled);
|
|
if (!(failure instanceof ProcessOwnershipInspectionCancelled))
|
|
throw failure;
|
|
expect(failure.reason).toBe(reason);
|
|
expect(failure.cause).toBe(native);
|
|
expect(processList).toHaveBeenCalledTimes(1);
|
|
expect(processList.mock.calls[0]?.[0]).toMatch(/(?:^|\/)ps$/u);
|
|
expect(processList.mock.calls[0]?.[2]?.signal).toBe(controller.signal);
|
|
},
|
|
);
|
|
|
|
it.each(routes)(
|
|
"preserves an independent physical AbortError-shaped rejection before %s parsing when reused as the abort reason",
|
|
async (route) => {
|
|
const controller = new AbortController();
|
|
const physical = Object.assign(
|
|
new Error("physical process table read failed"),
|
|
{
|
|
name: "AbortError",
|
|
cause: { native: "independent fault" },
|
|
},
|
|
);
|
|
controller.abort(physical);
|
|
processList.mockReset().mockRejectedValue(physical);
|
|
const host = createSystemProcessOwnershipHost("linux", {});
|
|
const pending =
|
|
route === "list"
|
|
? host.listProcesses(controller.signal)
|
|
: readRunTokenCarriers(host, "authored-run", controller.signal);
|
|
await expect(pending).rejects.toBe(physical);
|
|
},
|
|
);
|