import assert from "node:assert/strict";
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import path from "node:path";
import { test } from "node:test";
import {
applyPlan,
applyReplacements,
assertEquivalentStorage,
checkPolicy,
createSnapshot,
parseBaseline,
parseCli,
parseDate,
parseHeaderHtml,
parseReportHtml,
readPlan,
selectWorklist,
validatePlan,
} from "./ims.mjs";
const reportHtml = `
`;
test("parses IMS report rows and entities", () => {
const rows = parseReportHtml(reportHtml);
assert.equal(rows.length, 3);
assert.deepEqual(rows[0], {
id: "101",
title: "Alpha & Beta",
href: "/wiki/spaces/RD/pages/101/Alpha",
area: "RD",
documentOwner: "Kathi Borrmann",
documentOwnerIds: ["owner-1"],
riskOwner: "Risk",
riskOwnerIds: [],
assetOwner: "Asset",
assetOwnerIds: [],
classification: "Intern",
status: "Prüfung",
reviewer: "Oliver Krylow",
reviewerIds: ["oliver"],
reviewedAt: "20.08.2026",
releasedBy: "Release",
releasedAt: "21.08.2026",
});
assert.equal(rows[1].id, "102");
});
test("builds distinct reviewer and owner worklists with RD first", () => {
const rows = parseReportHtml(reportHtml);
const reviewer = selectWorklist(rows, { mode: "reviewer", person: "ignored", accountId: "oliver", area: "all", today: "2026-08-27" });
assert.deepEqual(reviewer.candidates.map((row) => [row.id, row.reasons]), [["101", ["awaiting-review"]]]);
const owner = selectWorklist(rows, { mode: "owner", person: "ignored", accountId: "oliver", area: "all", today: "2026-08-27" });
assert.deepEqual(owner.candidates.map((row) => row.id), ["103", "102"]);
assert.deepEqual(owner.candidates[0].reasons, ["self-reviewer", "annual-review-due"]);
assert.deepEqual(owner.candidates[1].reasons, ["draft", "missing-reviewer"]);
const nearName = [{ ...rows[1], documentOwner: "Max Anna Meyer", documentOwnerIds: [] }];
assert.equal(selectWorklist(nearName, { mode: "owner", person: "Anna Meyer", area: "all", today: "2026-08-27" }).roleMatchCount, 0);
});
test("rejects impossible German calendar dates", () => {
assert.equal(parseDate("31.02.2026"), undefined);
assert.equal(parseDate("2026-02-29"), undefined);
assert.equal(parseDate("29.02.2024")?.toISOString(), "2024-02-29T00:00:00.000Z");
assert.equal(parseDate("2024-02-29")?.toISOString(), "2024-02-29T00:00:00.000Z");
});
test("parses the policy baseline and detects version changes", async () => {
const baseline = parseBaseline(`
- Instruction page: \`540606500\`, version \`24\`, [Instructions](https://site.example/wiki/x/a)
- Announcement: \`1437532339\`, version \`1\`, [Announcement](https://site.example/wiki/x/b)
- Worklist report: \`899153943\`, [Report](https://site.example/wiki/x/c)
`);
assert.equal(baseline.baseUrl, "https://site.example");
const client = {
async get(requestPath) {
return { version: { number: requestPath.includes("blogposts") ? 2 : 24 } };
},
};
const result = await checkPolicy(client, baseline);
assert.equal(result.changed, true);
assert.deepEqual(result.sources.map((source) => source.changed), [false, true]);
});
test("extracts row and matrix IMS headers", () => {
const rows = `| Status: | Prüfung |
|---|
| Geprüft von | Marcus Storre |
`;
assert.deepEqual(parseHeaderHtml(rows), { Status: "Prüfung", "Geprüft von": "Marcus Storre" });
const matrix = `| Document Owner | Risk Owner |
|---|
| Oliver Krylow | Marcus Storre |
`;
assert.deepEqual(parseHeaderHtml(matrix), { "Document Owner": "Oliver Krylow", "Risk Owner": "Marcus Storre" });
});
test("check-plan is read-only and apply requires the separate mutation gate", () => {
assert.equal(parseCli(["check-plan", "plan.json"]).allowMutation, false);
assert.throws(() => parseCli(["apply", "plan.json", "--expected-version", "7", "--plan-sha256", "a".repeat(64)]), /--allow-mutation/);
assert.throws(() => parseCli(["apply", "plan.json", "--expected-version", "7", "--allow-mutation"]), /--plan-sha256/);
});
test("locks the exact validated plan bytes", (t) => {
const dir = mkdtempSync(path.join(tmpdir(), "ims-plan-test-"));
t.after(() => rmSync(dir, { recursive: true, force: true }));
const file = path.join(dir, "plan.json");
writeFileSync(file, JSON.stringify({
pageId: "123",
expectedVersion: 7,
operations: [{ kind: "page", versionComment: "geprüft", replacements: [{ from: "old", to: "new" }], expect: { contains: ["new"] } }],
}));
const first = readPlan(file);
writeFileSync(file, `${first.source}\n`);
const second = readPlan(file);
assert.notEqual(first.sha256, second.sha256);
assert.deepEqual(first.plan, second.plan);
});
test("rejects plan and replacement shapes that could silently broaden a write", () => {
assert.throws(() => validatePlan({ pageId: "x", expectedVersion: 1, operations: [] }), /pageId/);
assert.throws(() => validatePlan({ pageId: "1", expectedVersion: 1, operations: [{ kind: "page", versionComment: "x", replacements: [] }] }), /lacks replacements/);
assert.throws(() => validatePlan({ pageId: "1", expectedVersion: 1, operations: [{ kind: "page", versionComment: "x", replacements: [{ from: "a", to: "b" }], expect: { contains: "b" } }] }), /expect.contains/);
assert.throws(() => validatePlan({ pageId: "1", expectedVersion: 1, operations: [{ kind: "page", versionComment: "x", replacements: [{ from: "a", to: "b" }] }] }), /at least one expectation/);
assert.throws(() => validatePlan({ pageId: "1", expectedVersion: 1, operations: [
{ kind: "page", versionComment: "x", replacements: [{ from: "a", to: "b" }], expect: { contains: ["b"] } },
{ kind: "inline-comment", target: "b", text: "fix" },
] }), /must precede/);
assert.throws(() => applyReplacements("one old", [{ from: "old", to: "new", count: 2 }]), /found 1/);
assert.throws(
() => applyReplacements('old', [{ from: 'old', to: "new" }]),
/inline-comment markers/,
);
});
test("accepts server-only storage attributes but rejects semantic drift", () => {
const expected = 'Hello
';
const normalized = 'Hello
';
assert.doesNotThrow(() => assertEquivalentStorage(expected, normalized));
assert.throws(() => assertEquivalentStorage(expected, normalized.replace("person", "other")), /differs/);
assert.throws(() => assertEquivalentStorage("Hello world
", "Helloworld
"), /differs/);
const links = 'One Two
';
const swapped = links.replace('ri:space-key="RD"', 'ri:space-key="TEMP"').replace('ri:space-key="GA"', 'ri:space-key="RD"').replace('ri:space-key="TEMP"', 'ri:space-key="GA"');
assert.throws(() => assertEquivalentStorage(links, swapped), /differs/);
});
test("applies a version-gated page plan and emits an exact receipt", async () => {
let page = {
id: "123",
title: "Process",
status: "current",
spaceId: "space-1",
version: { number: 3, message: "before" },
body: { storage: { value: 'Old
' } },
_links: { webui: "/wiki/spaces/RD/pages/123/Process" },
};
let mutations = 0;
const client = {
async get(requestPath) {
assert.match(requestPath, /pages\/123\?body-format=storage/);
return structuredClone(page);
},
async mutate(method, requestPath, body) {
mutations += 1;
assert.equal(method, "PUT");
assert.equal(requestPath, "wiki/api/v2/pages/123");
assert.equal(body.spaceId, "space-1");
page = {
...page,
version: structuredClone(body.version),
body: { storage: { value: body.body.value.replace("", '
') } },
};
return structuredClone(page);
},
};
const plan = validatePlan({
pageId: "123",
expectedVersion: 3,
operations: [{
kind: "page",
versionComment: "Rechtschreibkorrektur",
replacements: [{ from: "Old", to: "New" }],
expect: { contains: ["New"], notContains: ["Old"] },
}],
});
const receipt = await applyPlan(plan, client, "https://site.example");
assert.equal(mutations, 1);
assert.deepEqual(receipt, {
pageId: "123",
initialVersion: 3,
operations: [{ kind: "page", version: 4, versionComment: "Rechtschreibkorrektur", validation: "passed" }],
inlineComments: [],
validation: "passed",
url: "https://site.example/wiki/spaces/RD/pages/123/Process",
finalVersion: 4,
versionComment: "Rechtschreibkorrektur",
header: { Status: "Prüfung" },
});
});
test("stops before mutation on version drift", async () => {
let mutations = 0;
const client = {
async get() {
return { id: "123", version: { number: 8 }, body: { storage: { value: "old" } } };
},
async mutate() {
mutations += 1;
},
};
const plan = validatePlan({ pageId: "123", expectedVersion: 7, operations: [{ kind: "page", versionComment: "x", replacements: [{ from: "old", to: "new" }], expect: { contains: ["new"] } }] });
await assert.rejects(applyPlan(plan, client, "https://site.example"), /version drift/);
assert.equal(mutations, 0);
});
test("creates and verifies an inline comment and its page marker", async () => {
let storage = "
Before exact target after
";
const page = (representation) => ({
id: "123",
title: "Process",
status: "current",
version: { number: 5, message: "before" },
body: representation === "storage"
? { storage: { value: storage } }
: { atlas_doc_format: { value: JSON.stringify({ type: "doc", content: [{ type: "paragraph", content: [{ type: "text", text: "Before exact " }, { type: "text", text: "target after" }] }] }) } },
_links: { webui: "/wiki/pages/123" },
});
let comment;
const client = {
async get(requestPath) {
if (requestPath.includes("inline-comments/c1")) return structuredClone(comment);
if (requestPath.includes("/inline-comments?")) return { results: [{ id: "c1" }] };
if (requestPath.includes("atlas_doc_format")) return page("atlas_doc_format");
return page("storage");
},
async mutate(method, requestPath, body) {
assert.equal(method, "POST");
assert.equal(requestPath, "wiki/api/v2/inline-comments");
assert.equal(body.inlineCommentProperties.textSelection, "exact target");
storage = storage.replace("exact target", 'exact target');
comment = {
id: "c1",
pageId: "123",
resolutionStatus: "open",
body: { storage: { value: "Prüfung nötig
" } },
properties: { inlineOriginalSelection: "exact target", inlineMarkerRef: "marker-1" },
};
return { id: "c1" };
},
};
const plan = validatePlan({
pageId: "123",
expectedVersion: 5,
operations: [{ kind: "inline-comment", target: "exact target", text: "Prüfung nötig" }],
});
const receipt = await applyPlan(plan, client, "https://site.example");
assert.equal(receipt.validation, "passed");
assert.deepEqual(receipt.inlineComments, [{ id: "c1", target: "exact target", markerRef: "marker-1", validation: "passed" }]);
assert.match(storage, /inline-comment-marker/);
});
test("rejects a snapshot changed during supplementary reads", async () => {
let storageReads = 0;
const client = {
async get(requestPath) {
if (requestPath.endsWith("?body-format=storage")) {
storageReads += 1;
return { id: "123", title: "Process", version: { number: storageReads === 1 ? 9 : 10 }, body: { storage: { value: "Storage
" } } };
}
if (requestPath.endsWith("?body-format=view")) return { id: "123", version: { number: 9 }, body: { view: { value: "View
" } } };
return { results: [] };
},
};
await assert.rejects(createSnapshot(client, "123", "https://site.example"), /changed during snapshot/);
});
test("captures one version-consistent page snapshot", async () => {
const client = {
async get(requestPath) {
if (requestPath.endsWith("?body-format=storage")) return {
id: "123",
title: "Process",
version: { number: 9, message: "x" },
body: { storage: { value: "Storage
" } },
_links: { webui: "/wiki/pages/123" },
};
if (requestPath.endsWith("?body-format=view")) return {
id: "123",
version: { number: 9 },
body: { view: { value: "" } },
};
if (requestPath.includes("/labels")) return [{ results: [{ name: "ims-dokument" }] }];
if (requestPath.includes("/inline-comments")) return { results: [{ id: "i1" }] };
if (requestPath.includes("/footer-comments")) return { results: [{ id: "f1" }] };
if (requestPath.includes("/attachments")) return { results: [{ id: "a1" }] };
throw new Error(`Unexpected path: ${requestPath}`);
},
};
const snapshot = await createSnapshot(client, "123", "https://site.example");
assert.equal(snapshot.url, "https://site.example/wiki/pages/123");
assert.deepEqual(snapshot.header, { Status: "Entwurf" });
assert.deepEqual(snapshot.labels, [{ name: "ims-dokument" }]);
assert.equal(snapshot.body.storage, "Storage
");
assert.match(snapshot.body.storageSha256, /^[0-9a-f]{64}$/);
});