Preserve the twenty-entry diagnostic prefix and report the full count,
omitted count and truncation fact at the owning report projection. Document
the recovery limitation and cover the public HAR-to-comparison workflow.
Fixes #7621
Validation: 121 source tests; three selected CLI regressions (five other
cases unselected); nine README conformance cases; full --noEmit typecheck;
documentation facts/metadata/schema/licenses; module boundaries and
architecture guards. VitePress rendering is blocked by Windows EPERM on its
worktree-local Vue symlink. Full aggregate coverage remains for Draft CI.
Independent code review: APPROVE, relayed by parent task
01a17716-6299-75fe-87e9-b0398b8c9019. Trigger: >=50 changed lines / >=3 files.
Reviewed tree: ba964efcd6
Reviewed full-index patch SHA256:
972dcfc219dd7b9606175b1848068f92210fa3f7d0a8a244c1ac7ce296ae32d9
753 lines
23 KiB
TypeScript
753 lines
23 KiB
TypeScript
import { readFile, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
|
|
import { describe, expect } from "vitest";
|
|
|
|
import type { CompareReport } from "../../../src/application/environment/environmentCompare.js";
|
|
import {
|
|
analysisViewCaptureEvidence,
|
|
environmentManifestCaptureFixture,
|
|
timedCaptureEvidence,
|
|
type TimedCaptureRow,
|
|
} from "../../fixtures/analysisView.js";
|
|
import { environmentStateServeFixture } from "../../fixtures/environmentServe.js";
|
|
import {
|
|
BOOKMARK_ROWS,
|
|
bookmark,
|
|
TAG_ROWS,
|
|
} from "../../fixtures/environmentStateDerivedValues.js";
|
|
import {
|
|
BOOK_NOT_FOUND,
|
|
BOOK_ROWS,
|
|
FIRST_BOOK,
|
|
SECOND_BOOK,
|
|
THIRD_BOOK,
|
|
book,
|
|
numberedUuid,
|
|
renamedBook,
|
|
} from "../../fixtures/environmentStateWrites.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { cliTest } from "../../support/cli/cliFixture.js";
|
|
|
|
describe("rea environment-compare difference coverage", () => {
|
|
cliTest(
|
|
"discloses the bounded differences in ordinary and full output",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-environment-compare-coverage-",
|
|
);
|
|
const counts = [0, 19, 20, 21, 25];
|
|
const originals = new Map<string, string>();
|
|
for (const [name, value] of [
|
|
["capture", 0],
|
|
["probe", 1],
|
|
] as const) {
|
|
const entries = counts.map((count, index) => {
|
|
const text = JSON.stringify(
|
|
Object.fromEntries(
|
|
Array.from({ length: count }, (_, field) => [
|
|
`field${String(field).padStart(2, "0")}`,
|
|
value,
|
|
]),
|
|
),
|
|
);
|
|
return {
|
|
startedDateTime: new Date(
|
|
Date.parse("2026-10-27T20:00:00Z") + index * 1000,
|
|
).toISOString(),
|
|
time: 0,
|
|
cache: {},
|
|
timings: { send: 0, wait: 0, receive: 0 },
|
|
request: {
|
|
method: "GET",
|
|
url: `https://app.example/api/config${String(count)}`,
|
|
httpVersion: "HTTP/1.1",
|
|
headers: [],
|
|
queryString: [],
|
|
cookies: [],
|
|
headersSize: -1,
|
|
bodySize: 0,
|
|
},
|
|
response: {
|
|
status: 200,
|
|
statusText: "OK",
|
|
httpVersion: "HTTP/1.1",
|
|
headers: [],
|
|
cookies: [],
|
|
redirectURL: "",
|
|
headersSize: -1,
|
|
bodySize: -1,
|
|
content: {
|
|
mimeType: "application/json",
|
|
size: Buffer.byteLength(text),
|
|
text,
|
|
},
|
|
},
|
|
};
|
|
});
|
|
const har = join(root, `${name}.har`);
|
|
const text = JSON.stringify({
|
|
log: {
|
|
version: "1.2",
|
|
creator: { name: "coverage-test", version: "1" },
|
|
entries,
|
|
},
|
|
});
|
|
await writeFile(har, text);
|
|
originals.set(har, text);
|
|
const captured = await cli.run({
|
|
arguments: ["inspect-web-network-capture", har, "har", "--json"],
|
|
});
|
|
expect(captured.exitCode, captured.stderr).toBe(0);
|
|
const path = join(root, `${name}.json`);
|
|
const evidence = JSON.stringify(captured.json);
|
|
await writeFile(path, evidence);
|
|
originals.set(path, evidence);
|
|
}
|
|
const view = join(root, "view.json");
|
|
await writeFile(
|
|
view,
|
|
JSON.stringify({
|
|
source: {
|
|
kind: "inline",
|
|
evidence: JSON.parse(originals.get(join(root, "capture.json"))!),
|
|
},
|
|
view: { kind: "environment-manifest" },
|
|
}),
|
|
);
|
|
const projected = await cli.run({
|
|
arguments: ["inspect-analysis-view", view, "--json"],
|
|
});
|
|
expect(projected.exitCode, projected.stderr).toBe(0);
|
|
const manifest = join(root, "manifest.json");
|
|
const manifestText = JSON.stringify(projected.json);
|
|
await writeFile(manifest, manifestText);
|
|
originals.set(manifest, manifestText);
|
|
const args = [
|
|
"environment-compare",
|
|
manifest,
|
|
"--evidence",
|
|
join(root, "capture.json"),
|
|
"--probe",
|
|
join(root, "probe.json"),
|
|
"--json",
|
|
];
|
|
const result = await cli.run({ arguments: args });
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
const report = result.json as CompareReport;
|
|
expect(report).toMatchObject({
|
|
status: "complete",
|
|
summary: { steps: 5, equal: 1, categories: { value: 4 } },
|
|
});
|
|
expect(report.probes[0]!.results).toHaveLength(counts.length);
|
|
for (const [index, count] of counts.entries()) {
|
|
const step = report.probes[0]!.results[index]!;
|
|
expect(step).toMatchObject({
|
|
equal: count === 0,
|
|
equal_ignoring_order: count === 0,
|
|
categories: count === 0 ? [] : ["value"],
|
|
differences_total: count,
|
|
differences_omitted: Math.max(0, count - 20),
|
|
differences_truncated: count > 20,
|
|
});
|
|
expect(step.differences).toHaveLength(Math.min(count, 20));
|
|
if (count >= 20)
|
|
expect(step.differences.at(-1)?.pointer).toBe("/field19");
|
|
}
|
|
const full = await cli.run({ arguments: [...args, "--full-output"] });
|
|
expect(full.exitCode, full.stderr).toBe(0);
|
|
expect(full.json).toMatchObject({ data: report });
|
|
for (const [path, text] of originals)
|
|
expect(await readFile(path, "utf8")).toBe(text);
|
|
},
|
|
);
|
|
});
|
|
|
|
/** The books environment and a probe of the application re-seeded under other ids, written as Evidence files. */
|
|
const writeCompareFiles = async (root: string) => {
|
|
const fixture = environmentStateServeFixture(timedCaptureEvidence(BOOK_ROWS));
|
|
const first = book(101, "First Book", "2026-10-20T09:00:00Z");
|
|
const second = book(102, "Second Book", "2026-10-20T10:00:00Z");
|
|
const probe = timedCaptureEvidence([
|
|
["GET", "/api/books", [first, second]],
|
|
["DELETE", "/api/books/102", "", 204],
|
|
["GET", "/api/books/102", BOOK_NOT_FOUND, 404],
|
|
]);
|
|
const paths = {
|
|
manifest: join(root, "manifest.json"),
|
|
capture: join(root, "capture.json"),
|
|
probe: join(root, "probe.json"),
|
|
};
|
|
await writeFile(paths.manifest, JSON.stringify(fixture.manifest));
|
|
await writeFile(paths.capture, JSON.stringify(fixture.capture));
|
|
await writeFile(paths.probe, JSON.stringify(probe));
|
|
return paths;
|
|
};
|
|
|
|
/** The exact chronology and clock-format probes share one unchanged source capture. */
|
|
const writeExactClockFiles = async (
|
|
root: string,
|
|
): Promise<Map<string, string>> => {
|
|
const capture = timedCaptureEvidence([
|
|
["GET", "/api/books", [FIRST_BOOK]],
|
|
["GET", "/api/books/1", FIRST_BOOK],
|
|
["DELETE", "/api/books/1", "", 204],
|
|
["GET", "/api/books/1", BOOK_NOT_FOUND, 404],
|
|
]);
|
|
const fixture = environmentStateServeFixture(capture);
|
|
const chronologyProbe = (later: string, earlier: string) =>
|
|
analysisViewCaptureEvidence(
|
|
environmentManifestCaptureFixture([
|
|
{
|
|
startedDateTime: later,
|
|
request: { method: "GET", url: "http://app.example/api/books/1" },
|
|
response: {
|
|
status: 404,
|
|
content: {
|
|
mimeType: "application/json",
|
|
text: JSON.stringify(BOOK_NOT_FOUND),
|
|
},
|
|
},
|
|
},
|
|
{
|
|
startedDateTime: earlier,
|
|
request: {
|
|
method: "DELETE",
|
|
url: "http://app.example/api/books/1",
|
|
},
|
|
response: {
|
|
status: 204,
|
|
content: { mimeType: "application/json", text: "" },
|
|
},
|
|
},
|
|
]),
|
|
);
|
|
const ordered = chronologyProbe(
|
|
"0001-01-01T00:00:00.0000000002Z",
|
|
"0001-01-01T01:00:00.0000000001+01:00",
|
|
);
|
|
const legacyOrdered = chronologyProbe(
|
|
"2026-10-20 10:00:00.0002+0100",
|
|
"2026-10-20 09:00:00.0001+0000",
|
|
);
|
|
const clocks = timedCaptureEvidence([
|
|
[
|
|
"GET",
|
|
"/api/books/1",
|
|
{ ...FIRST_BOOK, created_at: "0001-01-01 00:00:00.000Z" },
|
|
],
|
|
]);
|
|
const originals = new Map<string, string>();
|
|
for (const [name, value] of [
|
|
["manifest.json", fixture.manifest],
|
|
["capture.json", capture],
|
|
["order.json", ordered],
|
|
["legacy-order.json", legacyOrdered],
|
|
["clocks.json", clocks],
|
|
] as const) {
|
|
const path = join(root, name);
|
|
const text = JSON.stringify(value);
|
|
originals.set(path, text);
|
|
await writeFile(path, text);
|
|
}
|
|
return originals;
|
|
};
|
|
|
|
/** Capture-confirmed generated writes and literal/clock probes, retained as unchanged Evidence files. */
|
|
const writeValueContextFiles = async (
|
|
root: string,
|
|
): Promise<Map<string, string>> => {
|
|
const recorded = {
|
|
samples: [1700000000],
|
|
nested_samples: [[1700000000]],
|
|
scalar_samples: 1700000000,
|
|
sku: "product1",
|
|
catalog: ["product1"],
|
|
serial: numberedUuid(1),
|
|
updated_at: [1700000000],
|
|
nested: { updated_at: 1700000000 },
|
|
fixed_samples: [1700000000],
|
|
};
|
|
const clocks = {
|
|
...recorded,
|
|
updated_at: [1800000000],
|
|
nested: { updated_at: 1800000000 },
|
|
};
|
|
const changed = {
|
|
...clocks,
|
|
samples: [1800000000],
|
|
nested_samples: [[1800000000]],
|
|
scalar_samples: 1800000000,
|
|
sku: "product2",
|
|
catalog: ["product2"],
|
|
serial: numberedUuid(2),
|
|
};
|
|
const first = { ...FIRST_BOOK, ref: numberedUuid(1) };
|
|
const second = { ...SECOND_BOOK, ref: numberedUuid(2) };
|
|
const third = { ...THIRD_BOOK, ref: numberedUuid(3) };
|
|
const renamed = {
|
|
...renamedBook(first, "Renamed Book", 6),
|
|
ref: numberedUuid(4),
|
|
};
|
|
const missing = { error: "missing", ref: numberedUuid(99) };
|
|
const rows: TimedCaptureRow[] = [
|
|
["GET", "/api/config", recorded],
|
|
["GET", "/api/books", [first, second]],
|
|
["GET", "/api/books/1", first],
|
|
["GET", "/api/books/2", second],
|
|
["POST", "/api/books", third, 201, { name: "Third Book" }],
|
|
["GET", "/api/books/3", third],
|
|
["PATCH", "/api/books/1", renamed, 200, { name: "Renamed Book" }],
|
|
["GET", "/api/books/1", renamed],
|
|
["GET", "/api/books/99", missing, 404],
|
|
];
|
|
const capture = timedCaptureEvidence(rows);
|
|
const fixture = environmentStateServeFixture(capture);
|
|
expect(fixture.state.normalized_result).toMatchObject({
|
|
writes: expect.arrayContaining([
|
|
expect.objectContaining({
|
|
tool: "post_books",
|
|
served: true,
|
|
response: { ordinal: 4, shape: "entity", pointer: "" },
|
|
rules: expect.arrayContaining([{ field: "ref", rule: "unique" }]),
|
|
}),
|
|
expect.objectContaining({
|
|
tool: "patch_books_by_id",
|
|
served: true,
|
|
response: { ordinal: 6, shape: "entity", pointer: "" },
|
|
rules: expect.arrayContaining([{ field: "ref", rule: "unique" }]),
|
|
}),
|
|
]),
|
|
});
|
|
const generated = {
|
|
...book(101, "Third Book", "2026-11-01T12:00:00Z"),
|
|
ref: numberedUuid(101),
|
|
};
|
|
const originals = new Map<string, string>();
|
|
for (const [name, value] of [
|
|
["manifest.json", fixture.manifest],
|
|
["capture.json", capture],
|
|
["changed.json", timedCaptureEvidence([["GET", "/api/config", changed]])],
|
|
["clocks.json", timedCaptureEvidence([["GET", "/api/config", clocks]])],
|
|
[
|
|
"generated.json",
|
|
timedCaptureEvidence([
|
|
["POST", "/api/books", generated, 201, { name: "Third Book" }],
|
|
[
|
|
"PATCH",
|
|
"/api/books/1",
|
|
{ ...renamed, ref: numberedUuid(102) },
|
|
200,
|
|
{ name: "Renamed Book" },
|
|
],
|
|
]),
|
|
],
|
|
[
|
|
"missing.json",
|
|
timedCaptureEvidence([
|
|
[
|
|
"PATCH",
|
|
"/api/books/99",
|
|
{ ...missing, ref: numberedUuid(100) },
|
|
404,
|
|
{ name: "Ghost" },
|
|
],
|
|
]),
|
|
],
|
|
] as const) {
|
|
const path = join(root, name);
|
|
const text = JSON.stringify(value);
|
|
originals.set(path, text);
|
|
await writeFile(path, text);
|
|
}
|
|
return originals;
|
|
};
|
|
|
|
describe("rea environment-compare value context", () => {
|
|
cliTest(
|
|
"keeps business values and numeric measurements literal while normalizing confirmed generation and clocks",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-environment-value-context-",
|
|
);
|
|
const originals = await writeValueContextFiles(root);
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
join(root, "manifest.json"),
|
|
"--evidence",
|
|
join(root, "capture.json"),
|
|
"--probe",
|
|
join(root, "changed.json"),
|
|
"--probe",
|
|
join(root, "clocks.json"),
|
|
"--probe",
|
|
join(root, "generated.json"),
|
|
"--probe",
|
|
join(root, "missing.json"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
status: "complete",
|
|
summary: {
|
|
probes: 4,
|
|
steps: 5,
|
|
equal: 3,
|
|
agreement: 0.6,
|
|
normalised: { generated: 2 },
|
|
},
|
|
probes: [
|
|
{
|
|
label: "changed.json",
|
|
normalised: { generated: 0, clocks: 2, ids: 0 },
|
|
results: [
|
|
{
|
|
equal: false,
|
|
categories: ["value"],
|
|
differences: [
|
|
{
|
|
category: "value",
|
|
pointer: "/samples/0",
|
|
live: "1800000000",
|
|
env: "1700000000",
|
|
},
|
|
{
|
|
category: "value",
|
|
pointer: "/nested_samples/0/0",
|
|
live: "1800000000",
|
|
env: "1700000000",
|
|
},
|
|
{
|
|
category: "value",
|
|
pointer: "/scalar_samples",
|
|
live: "1800000000",
|
|
env: "1700000000",
|
|
},
|
|
{
|
|
category: "value",
|
|
pointer: "/sku",
|
|
live: '"product2"',
|
|
env: '"product1"',
|
|
},
|
|
{
|
|
category: "value",
|
|
pointer: "/catalog/0",
|
|
live: '"product2"',
|
|
env: '"product1"',
|
|
},
|
|
{
|
|
category: "value",
|
|
pointer: "/serial",
|
|
live: JSON.stringify(numberedUuid(2)),
|
|
env: JSON.stringify(numberedUuid(1)),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "clocks.json",
|
|
normalised: { clocks: 2, generated: 0 },
|
|
results: [{ equal: true, differences: [] }],
|
|
},
|
|
{
|
|
label: "generated.json",
|
|
normalised: { generated: 2, ids: 1 },
|
|
results: [
|
|
{ equal: true, differences: [] },
|
|
{ equal: true, differences: [] },
|
|
],
|
|
},
|
|
{
|
|
label: "missing.json",
|
|
normalised: { generated: 0 },
|
|
results: [
|
|
{
|
|
tool: "patch_books_by_id",
|
|
live_status: 404,
|
|
env_status: 404,
|
|
equal: false,
|
|
differences: [
|
|
{
|
|
category: "value",
|
|
pointer: "/ref",
|
|
live: JSON.stringify(numberedUuid(100)),
|
|
env: JSON.stringify(numberedUuid(99)),
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
for (const [path, text] of originals)
|
|
expect(await readFile(path, "utf8")).toBe(text);
|
|
},
|
|
);
|
|
});
|
|
|
|
describe("rea environment-compare recorded clock spelling", () => {
|
|
for (const scenario of [
|
|
{
|
|
name: "variable fractional widths under a confirmed trimmed write plan",
|
|
rows: TAG_ROWS,
|
|
probe: [
|
|
[
|
|
"POST",
|
|
"/api/tags",
|
|
{
|
|
id: numberedUuid(20),
|
|
name: "Spare",
|
|
createdAt: "2026-10-20T10:00:00.12345678Z",
|
|
updatedAt: "2026-10-20T10:00:00.12345678Z",
|
|
},
|
|
201,
|
|
{ name: "Spare" },
|
|
],
|
|
] as const,
|
|
generated: 0,
|
|
},
|
|
{
|
|
name: "a composed bookmark URL through its creation clock",
|
|
rows: BOOKMARK_ROWS,
|
|
probe: [
|
|
[
|
|
"POST",
|
|
"/api/bookmarks",
|
|
bookmark(
|
|
20,
|
|
"https://pier.example.test/inspection",
|
|
"Pier",
|
|
"2026-10-20T10:00:00Z",
|
|
),
|
|
201,
|
|
{ url: "https://pier.example.test/inspection", title: "Pier" },
|
|
],
|
|
] as const,
|
|
generated: 1,
|
|
},
|
|
]) {
|
|
cliTest(`accepts ${scenario.name}`, async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-environment-derived-compare-",
|
|
);
|
|
const capture = timedCaptureEvidence(scenario.rows);
|
|
const fixture = environmentStateServeFixture(capture);
|
|
const probe = timedCaptureEvidence(scenario.probe);
|
|
const originals = new Map<string, string>();
|
|
for (const [name, value] of [
|
|
["manifest.json", fixture.manifest],
|
|
["capture.json", capture],
|
|
["probe.json", probe],
|
|
] as const) {
|
|
const path = join(root, name);
|
|
const text = JSON.stringify(value);
|
|
originals.set(path, text);
|
|
await writeFile(path, text);
|
|
}
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
join(root, "manifest.json"),
|
|
"--evidence",
|
|
join(root, "capture.json"),
|
|
"--probe",
|
|
join(root, "probe.json"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
status: "complete",
|
|
summary: { probes: 1, steps: 1, equal: 1, agreement: 1 },
|
|
probes: [
|
|
{
|
|
normalised: { clocks: 2, generated: scenario.generated },
|
|
results: [{ method: "POST", equal: true, differences: [] }],
|
|
},
|
|
],
|
|
});
|
|
for (const [path, text] of originals)
|
|
expect(await readFile(path, "utf8")).toBe(text);
|
|
});
|
|
}
|
|
|
|
cliTest(
|
|
"replays compact-offset submillisecond probe timestamps in exact order",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-environment-legacy-chronology-",
|
|
);
|
|
const originals = await writeExactClockFiles(root);
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
join(root, "manifest.json"),
|
|
"--evidence",
|
|
join(root, "capture.json"),
|
|
"--probe",
|
|
join(root, "legacy-order.json"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
status: "complete",
|
|
environment: { state: "served" },
|
|
summary: { probes: 1, steps: 2, equal: 2, agreement: 1 },
|
|
probes: [
|
|
{
|
|
label: "legacy-order.json",
|
|
results: [
|
|
{
|
|
ordinal: 1,
|
|
method: "DELETE",
|
|
live_status: 204,
|
|
env_status: 204,
|
|
equal: true,
|
|
},
|
|
{
|
|
ordinal: 0,
|
|
method: "GET",
|
|
live_status: 404,
|
|
env_status: 404,
|
|
equal: true,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
for (const [path, text] of originals)
|
|
expect(await readFile(path, "utf8")).toBe(text);
|
|
},
|
|
);
|
|
});
|
|
|
|
describe("rea environment-compare replay and chronology", () => {
|
|
cliTest(
|
|
"preserves exact probe chronology and first-century clock-format differences",
|
|
async ({ cli }) => {
|
|
const root = await createTestTempDirectory(
|
|
"rea-environment-exact-clock-",
|
|
);
|
|
const originals = await writeExactClockFiles(root);
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
join(root, "manifest.json"),
|
|
"--evidence",
|
|
join(root, "capture.json"),
|
|
"--probe",
|
|
join(root, "order.json"),
|
|
"--probe",
|
|
join(root, "clocks.json"),
|
|
"--json",
|
|
],
|
|
});
|
|
expect(result.exitCode, result.stderr).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
status: "complete",
|
|
environment: { state: "served" },
|
|
summary: { probes: 2, steps: 3, equal: 2 },
|
|
probes: [
|
|
{
|
|
label: "order.json",
|
|
results: [
|
|
{
|
|
ordinal: 1,
|
|
method: "DELETE",
|
|
live_status: 204,
|
|
env_status: 204,
|
|
equal: true,
|
|
},
|
|
{
|
|
ordinal: 0,
|
|
method: "GET",
|
|
live_status: 404,
|
|
env_status: 404,
|
|
equal: true,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
label: "clocks.json",
|
|
results: [
|
|
{
|
|
categories: ["clock-format"],
|
|
differences: [
|
|
{
|
|
category: "clock-format",
|
|
pointer: "/created_at",
|
|
live: '"0001-01-01 00:00:00.000Z"',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
for (const [path, text] of originals)
|
|
expect(await readFile(path, "utf8")).toBe(text);
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"compares the served environment with a probe of the re-seeded application and prints the report",
|
|
async ({ cli }) => {
|
|
const files = await writeCompareFiles(
|
|
await createTestTempDirectory("rea-environment-compare-cli-"),
|
|
);
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
files.manifest,
|
|
"--evidence",
|
|
files.capture,
|
|
"--probe",
|
|
files.probe,
|
|
"--json",
|
|
],
|
|
timeoutMs: 30_000,
|
|
});
|
|
expect(result.exitCode).toBe(0);
|
|
expect(result.json).toMatchObject({
|
|
status: "complete",
|
|
environment: { state: "served" },
|
|
summary: { probes: 1, steps: 3, equal: 3, agreement: 1 },
|
|
probes: [{ label: "probe.json", ids_mapped: 2 }],
|
|
});
|
|
},
|
|
);
|
|
|
|
cliTest(
|
|
"refuses a probe that is not capture Evidence and names it",
|
|
async ({ cli }) => {
|
|
const files = await writeCompareFiles(
|
|
await createTestTempDirectory("rea-environment-compare-refusal-cli-"),
|
|
);
|
|
const result = await cli.run({
|
|
arguments: [
|
|
"environment-compare",
|
|
files.manifest,
|
|
"--evidence",
|
|
files.capture,
|
|
"--probe",
|
|
files.manifest,
|
|
"--json",
|
|
],
|
|
timeoutMs: 30_000,
|
|
});
|
|
expect(result.exitCode).toBe(1);
|
|
expect(result.json).toMatchObject({
|
|
code: "invalid_request",
|
|
details: {
|
|
operation: "environment-compare",
|
|
issues: [{ path: ["probe", 0] }],
|
|
},
|
|
});
|
|
},
|
|
);
|
|
});
|