Files
rea/src/domain/process/processTraceLifecycleFollowup.test.ts
T

175 lines
5.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { compareProcessCaptures } from "./processComparison.js";
import {
capture,
processStarted,
terminal,
} from "./processTraceComparison.fixture.js";
import { parseProcessCapture } from "./processCaptureParsing.js";
import { processTraceSpecificationSchema } from "./processTraceSpecification.js";
const typed = {
sequence: 0,
scheduled_at_ms: 20,
dispatched_at_ms: 21,
type: "input",
data: "go",
outcome: "dispatched",
};
type Step = "ready" | "worker" | "typed";
const observation = (order: readonly Step[], data = typed.data) => {
const base = capture({
frames: [terminal],
process_samples: order.includes("worker") ? [processStarted] : [],
event_journal: [
{ collection: "frames", index: 0 },
...(order.includes("worker")
? [{ collection: "process_samples" as const, index: 0 }]
: []),
],
});
return parseProcessCapture({
...base,
interaction_events: [{ ...typed, data }],
event_journal: [
{ collection: "filesystem_checkpoints", index: 0 },
...order.map((id) => ({
collection:
id === "ready"
? "frames"
: id === "worker"
? "process_samples"
: "interaction_events",
index: 0,
})),
{ collection: "lifecycle", index: 0 },
{ collection: "lifecycle", index: 1 },
{ collection: "filesystem_checkpoints", index: 1 },
].map((entry, capture_order) => ({ ...entry, capture_order })),
});
};
const specification = (required = false, tail = false, unordered = false) =>
processTraceSpecificationSchema.parse({
events: [
{
id: "ready",
source: "terminal_raw",
exact: terminal,
cardinality: { kind: "required" },
},
{
id: "worker",
source: "process",
exact: processStarted,
cardinality: { kind: required ? "required" : "optional" },
},
{
id: "typed",
source: "interaction",
exact: typed,
cardinality: { kind: "required" },
},
],
language: {
kind: "finite_traces",
variants: [
{
id: "declared",
trace: unordered
? [{ unordered: ["ready", "worker"] }, "typed"]
: tail
? ["ready", "typed", "worker"]
: ["ready", "worker", "typed"],
},
],
},
});
describe("process trace omission and localization boundaries", () => {
it.each([
{ reverse: false, required: false, tail: false, unordered: false },
{ reverse: true, required: false, tail: false, unordered: false },
{ reverse: false, required: true, tail: false, unordered: false },
{ reverse: false, required: false, tail: true, unordered: false },
{ reverse: false, required: false, tail: false, unordered: true },
])(
"attributes only the omitted process observation ($reverse/$required/$tail/$unordered)",
({ reverse, required, tail, unordered }) => {
const full = observation(
tail ? ["ready", "typed", "worker"] : ["ready", "worker", "typed"],
);
const missing = observation(["ready", "typed"]);
expect(full.interaction_events).toEqual(missing.interaction_events);
const result = compareProcessCaptures(
reverse ? missing : full,
reverse ? full : missing,
{ traceSpecification: specification(required, tail, unordered) },
);
expect(result).toMatchObject({
status: "changed",
interaction: "unchanged",
process: "changed",
terminal: "unchanged",
first_divergence: { status: "found", dimension: "process", index: 0 },
trace: {
verdict: "different",
diagnostic: { event_ids: ["worker"], locations: [] },
},
});
},
);
it("keeps a genuine cross-source reorder changed and localization unknown", () => {
const result = compareProcessCaptures(
observation(["ready", "worker", "typed"]),
observation(["ready", "typed", "worker"]),
{ traceSpecification: specification() },
);
expect(result).toMatchObject({
status: "changed",
interaction: "changed",
process: "changed",
first_divergence: { status: "unknown" },
trace: { verdict: "different" },
});
});
it("retains a real interaction payload difference and its location", () => {
const result = compareProcessCaptures(
observation(["ready", "worker", "typed"]),
observation(["ready", "worker", "typed"], "stop"),
{ traceSpecification: specification() },
);
expect(result).toMatchObject({
interaction: "changed",
first_divergence: { status: "found", dimension: "interaction", index: 0 },
});
});
it("does not claim no divergence when the requested journal is unavailable", () => {
const full = observation(["ready", "worker", "typed"]);
const historical = parseProcessCapture({ ...full, event_journal: [] });
expect(
compareProcessCaptures(full, historical, {
traceSpecification: specification(),
}),
).toMatchObject({
first_divergence: { status: "unknown" },
trace: { verdict: "unknown" },
});
expect(
compareProcessCaptures(full, full, {
traceSpecification: specification(),
}),
).toMatchObject({
status: "unchanged",
first_divergence: { status: "none" },
trace: { verdict: "equivalent" },
});
expect(compareProcessCaptures(full, historical)).toMatchObject({
status: "unchanged",
first_divergence: { status: "none" },
});
});
});