mirror of
https://github.com/morluto/rea
synced 2026-10-28 15:07:26 -04:00
198 lines
4.9 KiB
TypeScript
198 lines
4.9 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import {
|
|
compareProcessCaptures,
|
|
digestProcessCommitment,
|
|
type ProcessCapture,
|
|
} from "../../../src/domain/processCapture.js";
|
|
|
|
const emptyCapture = (): ProcessCapture => {
|
|
const normalization = {
|
|
paths: true,
|
|
pids: true,
|
|
ports: true,
|
|
time_bucket_ms: 10,
|
|
patterns: [],
|
|
};
|
|
const scenario = { executable_sha256: "0".repeat(64) };
|
|
const comparisonContract = {};
|
|
const shimPlan: readonly unknown[] = [];
|
|
const replayPlan = {};
|
|
return {
|
|
schema_version: 4,
|
|
manifest: {
|
|
rea_version: "test",
|
|
provider_version: "3",
|
|
platform: process.platform,
|
|
architecture: process.arch,
|
|
pty_backend: "node-pty",
|
|
started_at: "2026-01-01T00:00:00.000Z",
|
|
completed_at: "2026-01-01T00:00:00.001Z",
|
|
scenario,
|
|
comparison_contract: comparisonContract,
|
|
shim_plan: shimPlan,
|
|
replay_plan: replayPlan,
|
|
full_scenario_sha256: digestProcessCommitment(scenario),
|
|
comparison_contract_sha256: digestProcessCommitment(comparisonContract),
|
|
executable_sha256: "0".repeat(64),
|
|
normalization_sha256: digestProcessCommitment(normalization),
|
|
shim_plan_sha256: digestProcessCommitment(shimPlan),
|
|
replay_plan_sha256: digestProcessCommitment(replayPlan),
|
|
},
|
|
normalization,
|
|
frames: [],
|
|
rendered_frames: [],
|
|
interaction_events: [],
|
|
exit: { code: 0, signal: null, reason: "exited" },
|
|
settlement: {
|
|
state: "quiesced",
|
|
elapsed_ms: 50,
|
|
cleanup_outcome: "not_required",
|
|
},
|
|
process_samples: [],
|
|
filesystem_checkpoints: [
|
|
{ name: "before", at_ms: 0, files: [], effects: [], truncated: false },
|
|
{
|
|
name: "after_settlement",
|
|
at_ms: 50,
|
|
files: [],
|
|
effects: [],
|
|
truncated: false,
|
|
},
|
|
],
|
|
shim_events: [],
|
|
protocol_events: [],
|
|
replay_transitions: [],
|
|
reactive_run: null,
|
|
event_journal: [],
|
|
files_before: [],
|
|
files_after: [],
|
|
filesystem_effects: [],
|
|
truncated: false,
|
|
limitations: [],
|
|
residual_unknowns: [],
|
|
cleanup: {
|
|
owned_process_group: "verified",
|
|
temporary_root: "removed",
|
|
},
|
|
};
|
|
};
|
|
|
|
it("detects changes in normalized process sample metadata", () => {
|
|
const capture = {
|
|
schema_version: 4 as const,
|
|
manifest: emptyCapture().manifest,
|
|
settlement: emptyCapture().settlement,
|
|
normalization: {
|
|
paths: true,
|
|
pids: true,
|
|
ports: true,
|
|
time_bucket_ms: 10,
|
|
patterns: [],
|
|
},
|
|
frames: [],
|
|
rendered_frames: [],
|
|
interaction_events: [],
|
|
exit: { code: 0, signal: null, reason: "exited" as const },
|
|
process_samples: [
|
|
{
|
|
at_ms: 10,
|
|
pid: 1,
|
|
parent_pid: 0,
|
|
process_group_id: 1,
|
|
session_id: 1,
|
|
command: "worker",
|
|
},
|
|
],
|
|
filesystem_checkpoints: emptyCapture().filesystem_checkpoints,
|
|
shim_events: [],
|
|
protocol_events: [],
|
|
replay_transitions: [],
|
|
reactive_run: null,
|
|
files_before: [],
|
|
files_after: [],
|
|
filesystem_effects: [],
|
|
truncated: false,
|
|
limitations: [],
|
|
residual_unknowns: [],
|
|
cleanup: {
|
|
owned_process_group: "verified" as const,
|
|
temporary_root: "removed" as const,
|
|
},
|
|
};
|
|
const changed = {
|
|
...capture,
|
|
process_samples: [
|
|
{
|
|
at_ms: 20,
|
|
pid: 1,
|
|
parent_pid: 2,
|
|
process_group_id: 1,
|
|
session_id: 1,
|
|
command: "worker",
|
|
},
|
|
],
|
|
};
|
|
|
|
const comparison = compareProcessCaptures(capture, changed);
|
|
expect(comparison.process).toBe("changed");
|
|
expect(comparison.status).toBe("changed");
|
|
});
|
|
|
|
it("compares replay state transitions as protocol evidence", () => {
|
|
const replayPlan = {
|
|
machine: {
|
|
initial_state: "anonymous",
|
|
states: [
|
|
{ name: "anonymous" },
|
|
{ name: "authenticated", terminal: true },
|
|
],
|
|
transitions: [
|
|
{
|
|
id: "login",
|
|
from: "anonymous",
|
|
to: "authenticated",
|
|
trigger: { protocol: "http", method: "POST", path: "/login" },
|
|
captures: [
|
|
{
|
|
variable: "token",
|
|
value: { source: "request_json", path: ["token"] },
|
|
sensitive: true,
|
|
},
|
|
],
|
|
actions: [{ type: "http_response", status: 204, body: "" }],
|
|
max_uses: 1,
|
|
},
|
|
],
|
|
max_transitions: 1,
|
|
},
|
|
};
|
|
const empty = emptyCapture();
|
|
const baseline = {
|
|
...empty,
|
|
manifest: {
|
|
...empty.manifest,
|
|
replay_plan: replayPlan,
|
|
replay_plan_sha256: digestProcessCommitment(replayPlan),
|
|
},
|
|
};
|
|
const transitioned = {
|
|
...baseline,
|
|
replay_transitions: [
|
|
{
|
|
sequence: 0,
|
|
at_ms: 0,
|
|
transition_id: "login",
|
|
state_before: "anonymous",
|
|
state_after: "authenticated",
|
|
sensitive_aliases: ["token"],
|
|
},
|
|
],
|
|
};
|
|
|
|
expect(compareProcessCaptures(baseline, transitioned)).toMatchObject({
|
|
status: "changed",
|
|
protocol: "added",
|
|
});
|
|
});
|