/*
RFC-1281 AC-3…AC-6 + RFC-1328 AC-1…AC-3: rule-level tests for
validateIntakeBundle — one describe per rule INTAKE-01…15, each building the
valid bundle via buildIntakeBundle and breaking exactly one rule. Covers the
committed fixtures/valid-minimal pass case, the single-byte verbatim mutation
(INTAKE-05), the invalid-PBP path shape (/pbp/, INTAKE-03), and the
INTAKE-11 warning-only contract (status stays pass).
- Do not test the kernel flag plumbing — handler-level usage checks are covered by the module declaration and manual probes.
- RFC-1328: INTAKE-12…15 describes — public-safe text, locale-native statements, WebP rasters, closed palette.
- RFC-1281: created — one describe per INTAKE rule + committed-fixture pass test.
- RFC-1281: intake contract and onboarding.intake.validate
- RFC-1281: resolve valid-minimal fixture path relative to test file
*/
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url";
import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
import { describe, expect, it } from "vitest";
import {
validateIntakeBundle,
resolveIntakeExportSha256,
type IntakeRuleId,
} from "./intake-validate.ts";
import { buildIntakeBundle, type BuildIntakeBundleOptions } from "./test-bundle.ts";
const { io } = createDefaultIO();
function tmp(): string {
return mkdtempSync(join(tmpdir(), "intake-"));
}
async function buildAndValidate(options: BuildIntakeBundleOptions = {}, dir: string = tmp()) {
await buildIntakeBundle(dir, io, options);
const exportInfo = await resolveIntakeExportSha256(io);
if (!exportInfo) throw new Error("schema export missing — run onboarding.intake.schema.export");
return validateIntakeBundle(dir, io, { exportSha256: exportInfo.sha256 });
}
const rules = (
result: Awaited>,
severity: "error" | "warning" = "error",
): IntakeRuleId[] => result.diagnostics.filter((d) => d.severity === severity).map((d) => d.rule);
describe("committed fixture (AC-3)", () => {
it("fixtures/valid-minimal passes with zero errors", async () => {
const dir = join(dirname(fileURLToPath(import.meta.url)), "fixtures/valid-minimal");
const exportInfo = await resolveIntakeExportSha256(io);
const result = await validateIntakeBundle(dir, io, {
exportSha256: exportInfo!.sha256,
});
expect(result.status).toBe("pass");
expect(result.diagnostics.filter((d) => d.severity === "error")).toHaveLength(0);
expect(result.intakeId).toMatch(/^int_/);
expect(result.systemId).toBe("dp7");
expect(result.contractSnapshot.actual).toBe(result.contractSnapshot.expected);
});
});
describe("INTAKE-01 — manifest/hash integrity", () => {
it("tampered manifest hash fails", async () => {
const result = await buildAndValidate({
manifestTransform: (m) => m.replace(/[0-9a-f]/, "0"),
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-01");
});
it("unlisted file fails", async () => {
const dir = tmp();
await buildIntakeBundle(dir, io);
await io.writeFile(join(dir, "stray.bin"), "x");
const exportInfo = await resolveIntakeExportSha256(io);
const result = await validateIntakeBundle(dir, io, { exportSha256: exportInfo!.sha256 });
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-01");
});
});
describe("INTAKE-02 — envelope schema", () => {
it("invalid envelope field fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
seed.envelope.systemId = "Bad System!";
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-02");
});
it("unknown major fails closed", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
seed.envelope.schema = "werkstatt:intake/v2";
},
});
expect(result.status).toBe("fail");
const intake02 = result.diagnostics.filter((d) => d.rule === "INTAKE-02");
expect(intake02.length).toBeGreaterThanOrEqual(1);
expect(intake02.some((d) => d.message.includes("unsupported intake schema"))).toBe(true);
});
});
describe("INTAKE-03 — canonical PBP validation", () => {
it("invalid entity reports INTAKE-03 with /pbp/ path (AC-6)", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const pbp = seed.envelope.pbp as Record[];
pbp[0].name = ""; // business.name must be non-empty
},
});
expect(result.status).toBe("fail");
const d03 = result.diagnostics.filter((d) => d.rule === "INTAKE-03");
expect(d03.length).toBeGreaterThanOrEqual(1);
expect(d03.some((d) => /^\/pbp\/0(\/|$)/.test(d.path ?? ""))).toBe(true);
});
});
describe("INTAKE-04 — referential integrity", () => {
it("annotation key without a pbp entity fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.annotations as Record)["claim/ghost"] = {
internal: true,
};
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-04");
});
it("gap.blocks pointing at a missing entity fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
((seed.envelope.gaps as Record[])[0].blocks as string[]).push(
"claim/ghost",
);
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-04");
});
});
describe("INTAKE-05 — byte-exact verbatim quotes", () => {
it("single-byte quote mutation fails (AC-5)", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const annotations = seed.envelope.annotations as Record<
string,
{ verbatim?: { text: string }[] }
>;
annotations["claim/european-quality"].verbatim![0].text += "x";
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-05");
});
});
describe("INTAKE-06 — draft entities name a gap or are internal", () => {
it("draft claim without gapRef/internal fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.annotations as Record)["claim/installation-warranty"] = {};
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-06");
});
});
describe("INTAKE-07 — locales", () => {
it("two primary locales fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const classification = seed.envelope.classification as {
locales: Record[];
};
classification.locales.push({
tag: "de",
role: "primary",
sourceLanguageBy: "studio",
reviewer: "studio",
});
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-07");
});
});
describe("INTAKE-08 — biome axes", () => {
it("invalid axis value fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const visual = seed.envelope.visual as { axes: Record };
visual.axes.warmth = "tropical";
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-08");
});
});
describe("INTAKE-09 — asset files", () => {
it("declared sha256 mismatch fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.assets as Record[])[0].sha256 = `sha256:${"0".repeat(64)}`;
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-09");
});
});
describe("INTAKE-10 — provenance completeness", () => {
it("contract snapshot mismatch fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.provenance as Record).contractSnapshot =
`sha256:${"f".repeat(64)}`;
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-10");
});
});
describe("INTAKE-12 — public-safe free text (RFC-1328 AC-1)", () => {
it("published summary containing GAP-08 fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.pbp as Record[])[0].summary =
"Pending client answer — see GAP-08";
},
});
expect(result.status).toBe("fail");
const d12 = result.diagnostics.filter((d) => d.rule === "INTAKE-12");
expect(d12.length).toBeGreaterThanOrEqual(1);
expect(d12[0].path).toBe("/pbp/0/summary");
});
it("draft entity free text is not scanned", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const pbp = seed.envelope.pbp as Record[];
const draft = pbp.find((e) => e.status === "draft")!;
draft.statement = "Still blocked by GAP-04 internally";
},
});
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-12")).toBe(false);
});
it("bare word 'source' never trips the anchored marker", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const business = (seed.envelope.pbp as Record[])[0];
business.summary = "Your source for quality hardware";
business.statementByLocale = { "uz-Latn": "Sifatli uskunalar manbai" };
},
});
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-12")).toBe(false);
});
});
describe("INTAKE-13 — locale-native statements (RFC-1328)", () => {
it("published statement without the site locale fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const pbp = seed.envelope.pbp as Record[];
delete pbp.find((e) => e.id === "claim/european-quality")!.statementByLocale;
},
});
expect(result.status).toBe("fail");
const d13 = result.diagnostics.filter((d) => d.rule === "INTAKE-13");
expect(d13.length).toBeGreaterThanOrEqual(1);
expect(d13.some((d) => d.message.includes("uz-Latn"))).toBe(true);
});
it("localeNeutral exempts a statement-bearing entity", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const pbp = seed.envelope.pbp as Record[];
const claim = pbp.find((e) => e.id === "claim/european-quality")!;
delete claim.statementByLocale;
claim.localeNeutral = true;
},
});
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-13")).toBe(false);
});
it("entities without a statement field are exempt", async () => {
const result = await buildAndValidate();
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-13")).toBe(false);
});
});
describe("INTAKE-14 — raster assets are WebP (RFC-1328 AC-2)", () => {
it("image/jpeg asset fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(seed.envelope.assets as Record[])[0].mediaType = "image/jpeg";
},
});
expect(result.status).toBe("fail");
expect(rules(result)).toContain("INTAKE-14");
});
it("image/svg+xml vector is exempt", async () => {
const result = await buildAndValidate();
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-14")).toBe(false);
});
});
describe("INTAKE-15 — closed palette vocabulary (RFC-1328 AC-3)", () => {
it("palette key 'secondary' fails", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const visual = seed.envelope.visual as { palette: Record };
visual.palette.secondary = "#ff8800";
},
});
expect(result.status).toBe("fail");
const d15 = result.diagnostics.filter((d) => d.rule === "INTAKE-15");
expect(d15.length).toBeGreaterThanOrEqual(1);
expect(d15[0].path).toBe("/visual/palette/secondary");
});
it("'background' is inside the closed vocabulary", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
const visual = seed.envelope.visual as { palette: Record };
visual.palette.background = "#ffffff";
},
});
expect(result.status).toBe("pass");
expect(result.diagnostics.some((d) => d.rule === "INTAKE-15")).toBe(false);
});
});
describe("INTAKE-11 — cost report warnings never block", () => {
it("over-budget total warns while status stays pass", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
(
(seed.envelope.provenance as Record).cost as Record
).total = 99;
},
});
expect(result.status).toBe("pass");
expect(rules(result, "warning")).toContain("INTAKE-11");
expect(result.diagnostics.some((d) => d.severity === "error")).toBe(false);
});
it("missing cost-report.json warns while status stays pass", async () => {
const result = await buildAndValidate({
mutate: (seed) => {
seed.files.delete("cost-report.json");
},
});
expect(result.status).toBe("pass");
expect(rules(result, "warning")).toContain("INTAKE-11");
});
});