rea/tests/composition/process-capture/processTraceSpecification.t...

186 lines
4.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
processTraceSpecificationSchema,
type ProcessTraceSpecification,
} from "../../../src/domain/processTraceComparison.js";
const terminal = { sequence: 0, at_ms: 900, data: "Ready" };
const processStarted = {
at_ms: 1,
pid: 1,
parent_pid: 0,
command: "worker",
process_group_id: 1,
session_id: 1,
};
const http = {
sequence: 0,
at_ms: 2,
protocol: "http" as const,
direction: "request" as const,
method: "GET",
path: "/status",
data: "",
outcome: "unmatched" as const,
};
const websocket = {
sequence: 1,
at_ms: 3,
protocol: "websocket" as const,
direction: "received" as const,
method: null,
path: "/ws",
data: "done",
outcome: "matched" as const,
};
const partialSpecification = (): ProcessTraceSpecification => ({
version: 1,
events: [
{
id: "ready",
source: "terminal_raw",
exact: terminal,
cardinality: { kind: "required" },
},
{
id: "worker",
source: "process",
exact: processStarted,
cardinality: { kind: "required" },
},
{
id: "status",
source: "http",
exact: http,
cardinality: { kind: "required" },
},
{
id: "done",
source: "websocket",
exact: websocket,
cardinality: { kind: "required" },
},
],
language: {
kind: "partial_order",
happens_before: [
{ before: "ready", after: "worker" },
{ before: "ready", after: "status" },
{ before: "worker", after: "done" },
{ before: "status", after: "done" },
],
not_before: [{ event: "done", anchor: "ready" }],
unordered_groups: [{ events: ["worker", "status"] }],
prefix: ["ready"],
suffix: ["done"],
},
});
describe("process trace specification", () => {
it("rejects cycles, implicit concurrency, overlap, and unsatisfiable variants", () => {
const base = partialSpecification();
expect(
processTraceSpecificationSchema.safeParse({
...base,
language: {
kind: "partial_order",
happens_before: [
{ before: "ready", after: "worker" },
{ before: "worker", after: "ready" },
],
unordered_groups: [{ events: ["status", "done"] }],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: base.events.slice(0, 3),
language: {
kind: "partial_order",
happens_before: [{ before: "ready", after: "status" }],
not_before: [{ event: "worker", anchor: "ready" }],
unordered_groups: [],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
id: "invalid-ignore",
source: "terminal_raw",
exact: "ready",
ignore_fields: ["at_ms"],
},
],
language: {
kind: "finite_traces",
variants: [{ id: "one", trace: ["invalid-ignore"] }],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
id: "invalid-ignore",
source: "terminal_raw",
exact: terminal,
ignore_fields: ["at_ms"],
},
],
language: {
kind: "finite_traces",
variants: [{ id: "one", trace: ["invalid-ignore"] }],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: base.events.slice(0, 2),
language: { kind: "partial_order" },
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [base.events[0], { ...base.events[0], id: "duplicate" }],
language: {
kind: "partial_order",
unordered_groups: [{ events: ["ready", "duplicate"] }],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [
{
...base.events[0],
cardinality: { kind: "exact", count: 2 },
},
],
language: {
kind: "finite_traces",
variants: [{ id: "once", trace: ["ready"] }],
},
}).success,
).toBe(false);
expect(
processTraceSpecificationSchema.safeParse({
version: 1,
events: [base.events[0]],
language: {
kind: "finite_traces",
variants: [
{ id: "first", trace: ["ready"] },
{ id: "duplicate", trace: ["ready"] },
],
},
}).success,
).toBe(false);
});
});