140 lines
8.0 KiB
JavaScript
140 lines
8.0 KiB
JavaScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { tmpdir } from "node:os";
|
|
import { mkdtemp } from "node:fs/promises";
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import {
|
|
complianceRetentionInventoryPatterns,
|
|
complianceRetentionInventoryScanRoots,
|
|
failureCount,
|
|
gates,
|
|
renderReport,
|
|
reportPath,
|
|
runComplianceRetentionInventory,
|
|
} from "./verify-compliance-retention-baseline.mjs";
|
|
|
|
describe("Compliance Retention baseline gate contract", () => {
|
|
it("writes the baseline report to the Compliance Retention health artifact path", () => {
|
|
expect(reportPath.replaceAll("\\", "/")).toMatch(/\.tc\/baselines\/compliance-retention\/baseline-health\.md$/);
|
|
});
|
|
|
|
it("keeps the expected non-E2E gate IDs in order", () => {
|
|
expect(gates.map((gate) => gate.id)).toEqual(["typecheck", "lint", "vitest-docs-schema-export", "vitest-worker-queue", "compliance-retention-inventory"]);
|
|
expect(gates.some((gate) => gate.args?.some((arg) => /playwright|cypress|browser|e2e|test:e2e/i.test(arg)))).toBe(false);
|
|
});
|
|
|
|
it("pins focused Vitest gates to explicit unit test file arguments", () => {
|
|
expect(gates.find((gate) => gate.id === "vitest-docs-schema-export")?.args).toEqual([
|
|
"exec",
|
|
"vitest",
|
|
"run",
|
|
"packages/db/src/export-jobs.server.test.ts",
|
|
"packages/db/src/file-object-schema.test.ts",
|
|
"packages/exports/src/index.test.ts",
|
|
"packages/exports/src/csv/index.test.ts",
|
|
"packages/exports/src/json/index.test.ts",
|
|
"packages/exports/src/xlsx/index.test.ts",
|
|
]);
|
|
expect(gates.find((gate) => gate.id === "vitest-worker-queue")?.args).toEqual([
|
|
"exec",
|
|
"vitest",
|
|
"run",
|
|
"packages/queue/src/producers.test.ts",
|
|
"packages/queue/src/export-contract.test.ts",
|
|
"apps/worker/src/handlers.test.ts",
|
|
"apps/worker/src/runtime.test.ts",
|
|
]);
|
|
});
|
|
|
|
it("covers compliance/retention inventory roots and fingerprint pattern metadata", () => {
|
|
expect(complianceRetentionInventoryScanRoots).toEqual([
|
|
"docs",
|
|
"apps/web/app/routes",
|
|
"apps/api/src",
|
|
"apps/worker/src",
|
|
"packages/db/src",
|
|
"packages/queue/src",
|
|
"packages/exports/src",
|
|
"packages/storage/src",
|
|
"prisma/schema",
|
|
]);
|
|
expect(complianceRetentionInventoryPatterns.map(({ id }) => id)).toEqual([
|
|
"security-disclosure-marker",
|
|
"gdpr-data-inventory-marker",
|
|
"retention-policy-marker",
|
|
"soft-delete-hard-delete-marker",
|
|
"export-hook-marker",
|
|
"admin-pause-resume-marker",
|
|
"audit-subscription-risk-marker",
|
|
"generated-client-reference",
|
|
]);
|
|
const patternSources = Object.fromEntries(complianceRetentionInventoryPatterns.map(({ id, pattern }) => [id, pattern.source]));
|
|
expect(patternSources["security-disclosure-marker"]).toMatch(/security|vulnerability/);
|
|
expect(patternSources["gdpr-data-inventory-marker"]).toMatch(/gdpr|personal/);
|
|
expect(patternSources["retention-policy-marker"]).toMatch(/retention|ttl/);
|
|
expect(patternSources["soft-delete-hard-delete-marker"]).toMatch(/soft|hard/);
|
|
expect(patternSources["export-hook-marker"]).toMatch(/exportJob|csv|pdf/);
|
|
expect(patternSources["admin-pause-resume-marker"]).toMatch(/pause|resume/);
|
|
expect(patternSources["audit-subscription-risk-marker"]).toMatch(/audit|subscription/);
|
|
expect(patternSources["generated-client-reference"]).toContain("packages\\/db\\/generated");
|
|
expect(patternSources["generated-client-reference"]).toContain("client\\/models");
|
|
});
|
|
|
|
it("counts command failures while treating read-only inventory findings as non-failing", () => {
|
|
expect(failureCount("typecheck", "src/a.ts(1,1): error TS2322: wrong\nerror TS7006", "", 2)).toBe(2);
|
|
expect(failureCount("lint", "✖ 5 problems (3 errors, 2 warnings)", "", 1)).toBe(3);
|
|
expect(failureCount("vitest-docs-schema-export", "Tests 2 failed | 4 passed", "", 1)).toBe(2);
|
|
expect(failureCount("vitest-worker-queue", "Test Files 1 failed | 2 passed", "", 1)).toBe(1);
|
|
expect(failureCount("compliance-retention-inventory", "generated-client-reference: 4", "", 1)).toBe(0);
|
|
});
|
|
|
|
it("renders baseline report diagnostics without snapshots", () => {
|
|
const report = renderReport([
|
|
{ id: "compliance-retention-inventory", command: "read-only compliance/retention inventory", exitCode: 0, durationMs: 42, failureCount: 0, fingerprint: { hash: "abc123def456", excerpt: "gdpr-data-inventory-marker: 7" } },
|
|
{ id: "typecheck", command: "pnpm run typecheck", exitCode: 2, durationMs: 99, failureCount: 3, fingerprint: { hash: "badc0ffee123", excerpt: "error TS2322" } },
|
|
]);
|
|
|
|
expect(report).toContain("# Compliance Retention Baseline Health");
|
|
expect(report).toContain("non-E2E baseline gates for Compliance Retention baseline");
|
|
expect(report).toContain("security disclosure, GDPR data inventory, retention policy");
|
|
expect(report).toContain("## compliance-retention-inventory");
|
|
expect(report).toContain("- Exitcode: 2");
|
|
expect(report).toContain("- Failure Count: 3");
|
|
expect(report).toContain("- Output Fingerprint: badc0ffee123");
|
|
});
|
|
|
|
it("scans bounded compliance roots while excluding generated Prisma client noise", async () => {
|
|
const root = await mkdtemp(join(tmpdir(), "complianceRetention-compliance-inventory-"));
|
|
await mkdir(join(root, "docs"), { recursive: true });
|
|
await mkdir(join(root, "apps", "web", "app", "routes"), { recursive: true });
|
|
await mkdir(join(root, "apps", "worker", "src"), { recursive: true });
|
|
await mkdir(join(root, "packages", "db", "src"), { recursive: true });
|
|
await mkdir(join(root, "packages", "db", "generated", "prisma", "client", "models"), { recursive: true });
|
|
await mkdir(join(root, "packages", "exports", "src"), { recursive: true });
|
|
await mkdir(join(root, "packages", "storage", "src"), { recursive: true });
|
|
await mkdir(join(root, "prisma", "schema"), { recursive: true });
|
|
|
|
await writeFile(join(root, "docs", "security.md"), "Responsible security disclosure and GDPR personal data inventory retention policy.\n", "utf8");
|
|
await writeFile(join(root, "apps", "web", "app", "routes", "admin.tsx"), "export async function action() { return pause ? resume() : null; }\n", "utf8");
|
|
await writeFile(join(root, "apps", "worker", "src", "retention.ts"), "const exportJob = queue.add('export', { ttl: 7, audit: true, subscription: true });\n", "utf8");
|
|
await writeFile(join(root, "packages", "db", "src", "delete.ts"), "const deletedAt = softDelete ? new Date() : hardDelete; const actorId = tenantId;\n", "utf8");
|
|
await writeFile(join(root, "packages", "db", "generated", "prisma", "client", "models", "User.ts"), "PrismaClient client/models/User generated/prisma should be ignored\n", "utf8");
|
|
await writeFile(join(root, "packages", "exports", "src", "index.ts"), "export const format = 'csv pdf json xlsx';\n", "utf8");
|
|
await writeFile(join(root, "packages", "storage", "src", "cleanup.ts"), "const deleteAfter = 'retention';\n", "utf8");
|
|
await writeFile(join(root, "prisma", "schema", "schema.prisma"), "model AuditLog { tenantId String subscriptionId String }\n", "utf8");
|
|
|
|
const inventory = await runComplianceRetentionInventory(root);
|
|
|
|
expect(inventory.roots).toEqual(expect.arrayContaining(["docs", "apps/web/app/routes", "apps/worker/src", "packages/db/src", "packages/exports/src", "packages/storage/src", "prisma/schema"]));
|
|
expect(inventory.counts["security-disclosure-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["gdpr-data-inventory-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["retention-policy-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["soft-delete-hard-delete-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["export-hook-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["admin-pause-resume-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.counts["audit-subscription-risk-marker"]).toBeGreaterThan(0);
|
|
expect(inventory.matches.join("\n")).not.toContain("packages/db/generated/prisma/client/models/User.ts");
|
|
});
|
|
});
|