Files
rea/tests/boundary/process/processCaptureNormalization.test.ts
T
Ysopwnable 6aa9afb549 refactor(process): separate capture implementation from analyst workflows (#988)
* refactor(process): separate capture implementation from analyst workflows

* fix(ci): generate source catalog before shard tests
2026-10-08 00:46:21 +08:00

94 lines
3.6 KiB
TypeScript

import { readFile } from "node:fs/promises";
import { join } from "node:path";
import { fileURLToPath } from "node:url";
import { expect, it } from "vitest";
import { z } from "zod";
import { captureProcessScenario } from "../../../src/process/capture/ProcessHarness.js";
import { createProcessCaptureEvidence } from "../../../src/application/process/ProcessEvidence.js";
import {
digestProcessCommitment,
parseProcessCapture,
parseProcessScenario,
} from "../../../src/domain/process/processCapture.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { itWithCaptureCapability } from "./processCaptureCapability.js";
const processFixture = fileURLToPath(
new URL("../../fixtures/processIdentity.mjs", import.meta.url),
);
const identitySchema = z.object({
pid: z.number().int().positive(),
parent_pid: z.number().int().positive(),
process_group_id: z.number().int().positive(),
session_id: z.number().int().positive(),
});
const itWithLinuxCaptureCapability =
process.platform === "linux" ? itWithCaptureCapability : it.skip;
itWithLinuxCaptureCapability.each([false, true])(
"honors pids=%s in owned Linux capture samples and committed Evidence",
async (pids) => {
const root = await createTestTempDirectory("rea-process-identifiers-");
const identityPath = join(root, "identity.json");
const scenario = parseProcessScenario({
executable: process.execPath,
arguments: [processFixture, identityPath],
working_directory: root,
normalization: { pids, ports: false, paths: false },
timeout_ms: 10_000,
idle_timeout_ms: 10_000,
});
const result = await captureProcessScenario(scenario);
expect(result.ok).toBe(true);
if (!result.ok) throw result.error;
const capture = result.value;
const identity = identitySchema.parse(
JSON.parse(await readFile(identityPath, "utf8")),
);
expect(identity.process_group_id).toBe(identity.pid);
expect(identity.session_id).toBe(identity.pid);
expect(identity.parent_pid).not.toBe(identity.pid);
const fixtureSamples = capture.process_samples.filter(({ command }) =>
command.includes(processFixture),
);
expect(fixtureSamples.length).toBeGreaterThan(0);
for (const sample of fixtureSamples)
expect(sample).toMatchObject(
pids
? { pid: 1, parent_pid: 2, process_group_id: 1, session_id: 1 }
: identity,
);
const output = capture.frames.map(({ data }) => data).join("");
if (pids) expect(output).toContain('"pid":<pid>');
else expect(output).toContain(JSON.stringify(identity));
const evidence = createProcessCaptureEvidence(scenario, capture);
const serializedCapture = parseProcessCapture(evidence.normalized_result);
expect(evidence.parameters).toMatchObject({ normalization: { pids } });
expect(serializedCapture.process_samples).toEqual(capture.process_samples);
expect(serializedCapture.normalization.pids).toBe(pids);
expect(serializedCapture.manifest.scenario).toMatchObject({
normalization: { pids },
});
expect(serializedCapture.manifest.comparison_contract).toMatchObject({
normalization: { pids },
});
expect(serializedCapture.manifest.normalization_sha256).toBe(
digestProcessCommitment(scenario.normalization),
);
expect(capture.exit).toMatchObject({ code: 0, reason: "exited" });
expect([null, 0]).toContain(capture.exit.signal);
expect(capture.settlement).toMatchObject({
state: "quiesced",
cleanup_outcome: "not_required",
});
expect(capture.cleanup).toEqual({
owned_process_group: "verified",
temporary_root: "removed",
});
},
20_000,
);