fix: complete TechLog migration evidence
This commit is contained in:
@@ -192,14 +192,35 @@ describe("CI gate contract", () => {
|
||||
expect(contract.commands).toHaveLength(81);
|
||||
expect(contract.gates.reduce((total, gate) => total + gate.commandIds.length, 0)).toBe(93);
|
||||
expect(contract.commands.filter(({ expect }) => expect === "fail")).toHaveLength(23);
|
||||
expect(contract.gates.reduce((total, gate) => total + gate.evidenceArtifactIds.length, 0)).toBe(85);
|
||||
expect(contract.artifacts).toHaveLength(105);
|
||||
expect(contract.gates.reduce((total, gate) => total + gate.evidenceArtifactIds.length, 0)).toBe(106);
|
||||
expect(contract.artifacts).toHaveLength(126);
|
||||
expect(contract.stages).toHaveLength(5);
|
||||
expect(contract.retention.classes).toHaveLength(5);
|
||||
expect(index.gates.get("FE-GATE-015")?.commandIds).toHaveLength(2);
|
||||
expect(index.gates.get("FE-GATE-020")?.name).toBe("removability");
|
||||
});
|
||||
|
||||
it("rejects accessibility evidence registrations that drift from the installed route scope", async () => {
|
||||
const candidate = JSON.parse(
|
||||
JSON.stringify(await loadCiGateContract(process.cwd())),
|
||||
) as Record<string, any>;
|
||||
const gate = candidate.gates.find(
|
||||
(entry: Record<string, any>) => entry.id === "FE-GATE-009",
|
||||
);
|
||||
const artifactId =
|
||||
"artifact-artifacts-tests-a11y-manual-TECH-LOG-HOME-md";
|
||||
candidate.artifacts = candidate.artifacts.filter(
|
||||
(entry: Record<string, any>) => entry.id !== artifactId,
|
||||
);
|
||||
gate.evidenceArtifactIds = gate.evidenceArtifactIds.filter(
|
||||
(id: string) => id !== artifactId,
|
||||
);
|
||||
|
||||
expect(() =>
|
||||
parseCiGateContract(candidate, { mode: "removal-fixture" }),
|
||||
).toThrow(/FE-GATE-009 manual accessibility evidence.*installed route scope/i);
|
||||
});
|
||||
|
||||
it("keeps the action registry recursively immutable and resolves only known actions", () => {
|
||||
expect(Object.isFrozen(CI_ACTION_REGISTRY)).toBe(true);
|
||||
expect(Object.values(CI_ACTION_REGISTRY).every((action) => Object.isFrozen(action))).toBe(true);
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
import { ROUTE_REGISTRY } from "../../src/features/installed-feature-contracts.ts";
|
||||
|
||||
const reviewed = `Status: reviewed
|
||||
Route ID: APP_HOME
|
||||
Route ID: TECH_LOG_HOME
|
||||
Release ID: release-1
|
||||
Reviewer: reviewer@example.test
|
||||
Reviewed at: 2026-07-25T12:00:00Z
|
||||
|
||||
@@ -148,7 +148,7 @@ describe("selective Task 3 contract closure", () => {
|
||||
expect(canonical.gates).toHaveLength(26);
|
||||
expect(canonical.commands).toHaveLength(81);
|
||||
expect(canonical.gates.reduce((sum, gate) => sum + gate.commandIds.length, 0)).toBe(93);
|
||||
expect(canonical.artifacts).toHaveLength(105);
|
||||
expect(canonical.artifacts).toHaveLength(126);
|
||||
expect(canonical.stages).toHaveLength(5);
|
||||
expect(canonical.retention.classes).toHaveLength(5);
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { readFile } from "node:fs/promises";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("TechLog source parity evidence harness", () => {
|
||||
it("is a checked Node runner with fixture, response, recursive tree, and provenance coverage", async () => {
|
||||
const [packageJson, source] = await Promise.all([
|
||||
readFile("package.json", "utf8").then(JSON.parse),
|
||||
readFile("tests/support/browser/verify-tech-log-source-parity.ts", "utf8"),
|
||||
]);
|
||||
|
||||
expect(packageJson.scripts["verify:tech-log-source-parity"]).toBe(
|
||||
"node tests/support/browser/verify-tech-log-source-parity.ts",
|
||||
);
|
||||
expect(source).toContain("TECH_LOG_PUBLIC_FIXTURE_PATHS.map");
|
||||
expect(source).toContain("element.childNodes");
|
||||
expect(source).toContain("responseContractEqual");
|
||||
expect(source).toContain("sourceChecksums(sourceRoot)");
|
||||
expect(source).toContain("caseInventorySha256");
|
||||
expect(source).toContain("evidenceSha256");
|
||||
expect(source).toContain(
|
||||
'"docs/operations/evidence/tech-log-source-parity.json"',
|
||||
);
|
||||
expect(source).not.toContain("tsx");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { mkdtemp, readFile, rm } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
|
||||
import { writeTechLogServingArtifact } from "../../scripts/lib/tech-log-serving-artifact.ts";
|
||||
|
||||
const roots: string[] = [];
|
||||
|
||||
afterEach(async () => {
|
||||
await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true })));
|
||||
});
|
||||
|
||||
describe("TechLog serving artifact", () => {
|
||||
it("emits a self-contained deployment server and its route contract", async () => {
|
||||
const root = await mkdtemp(path.join(tmpdir(), "tech-log-serving-artifact-"));
|
||||
roots.push(root);
|
||||
const contract = {
|
||||
schemaVersion: 1 as const,
|
||||
publicSpaPaths: ["/", "/projects/auth-lab"],
|
||||
studioPathPrefix: "/studio" as const,
|
||||
studioSpaPathPatterns: ["^/studio$"],
|
||||
notFound: {
|
||||
status: 404 as const,
|
||||
contentType: "text/plain;charset=UTF-8" as const,
|
||||
body: "Not Found" as const,
|
||||
},
|
||||
};
|
||||
|
||||
await writeTechLogServingArtifact({ distRoot: root, contract });
|
||||
|
||||
expect(
|
||||
JSON.parse(
|
||||
await readFile(path.join(root, "tech-log-serving-contract.json"), "utf8"),
|
||||
),
|
||||
).toEqual(contract);
|
||||
const serverModule = await import(
|
||||
`${pathToFileURL(path.join(root, "server.mjs")).href}?test=${Date.now()}`
|
||||
);
|
||||
expect(serverModule.createTechLogProductionServer).toEqual(expect.any(Function));
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { createTechLogServingContract } from "../../scripts/lib/tech-log-serving-contract.ts";
|
||||
import {
|
||||
projects,
|
||||
publicRecords,
|
||||
releases,
|
||||
} from "../../src/features/tech-log/adapters/static/public-content.ts";
|
||||
|
||||
describe("TechLog production serving contract", () => {
|
||||
it("derives every known Public path from the installed static content", () => {
|
||||
const contract = createTechLogServingContract({
|
||||
projects,
|
||||
publicRecords,
|
||||
releases,
|
||||
});
|
||||
|
||||
expect(contract.publicSpaPaths).toEqual([
|
||||
"/",
|
||||
"/cases/collection-fetch-join-pagination",
|
||||
"/cases/redis-adapter-ttl-boundary",
|
||||
"/explore",
|
||||
"/explore/cases",
|
||||
"/explore/questions",
|
||||
"/explore/references",
|
||||
"/profile",
|
||||
"/projects",
|
||||
"/projects/auth-lab",
|
||||
"/projects/auth-lab/activity",
|
||||
"/projects/auth-lab/decisions",
|
||||
"/projects/auth-lab/records",
|
||||
"/projects/backend-skeleton",
|
||||
"/projects/backend-skeleton/activity",
|
||||
"/projects/backend-skeleton/decisions",
|
||||
"/projects/backend-skeleton/records",
|
||||
"/questions/collection-fetch-join-with-pagination",
|
||||
"/questions/validate-edge-token-again",
|
||||
"/references/jpa-list-fetch-strategy",
|
||||
"/references/state-and-nonce-boundary",
|
||||
"/releases",
|
||||
"/releases/0.1.0",
|
||||
"/search",
|
||||
"/topics/authentication",
|
||||
"/topics/jpa",
|
||||
"/topics/redis",
|
||||
]);
|
||||
expect(contract.studioPathPrefix).toBe("/studio");
|
||||
expect(contract.studioSpaPathPatterns).toEqual([
|
||||
"^/studio$",
|
||||
"^/studio/documents$",
|
||||
"^/studio/documents/new$",
|
||||
"^/studio/documents/[^/]+/(edit|validation|preview|publish)$",
|
||||
"^/studio/publications$",
|
||||
"^/studio/publications/[^/]+/preview$",
|
||||
]);
|
||||
expect(contract.notFound).toEqual({
|
||||
status: 404,
|
||||
contentType: "text/plain;charset=UTF-8",
|
||||
body: "Not Found",
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user