Files
webdev-pipeline/tests/public-audit-contract.test.ts
2026-06-05 14:14:07 +02:00

68 lines
2.6 KiB
TypeScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { join } from "node:path";
import test from "node:test";
const source = async (relativePath: string) => {
return await readFile(
join(process.cwd(), ...relativePath.split("/")),
"utf8",
);
};
test("public audit schema stores reviewed public observations and offer separately", async () => {
const schemaSource = await source("convex/schema.ts");
assert.match(schemaSource, /publicObservations/);
assert.match(schemaSource, /observation:\s*v\.string\(\)/);
assert.match(schemaSource, /impact:\s*v\.string\(\)/);
assert.match(schemaSource, /suggestion:\s*v\.string\(\)/);
assert.match(schemaSource, /screenshotIds:\s*v\.optional\(v\.array\(v\.id\("_storage"\)\)\)/);
assert.match(schemaSource, /publicOffer/);
assert.match(schemaSource, /ctaLabel:\s*v\.optional\(v\.string\(\)\)/);
assert.match(schemaSource, /ctaHref:\s*v\.optional\(v\.string\(\)\)/);
});
test("public audit convex query only exposes published, bounded, public data", async () => {
const auditsSource = await source("convex/audits.ts");
assert.match(auditsSource, /export const getPublicBySlug = query/);
assert.match(
auditsSource,
/\.withIndex\("by_slug",\s*\(q\)\s*=>\s*q\.eq\("slug",\s*args\.slug\)\)\s*\.unique\(\)/,
"Public lookup should use the unique slug index.",
);
assert.match(
auditsSource,
/audit\.status !== "published"/,
"Draft and approved audits should not expose public content.",
);
assert.match(auditsSource, /audit\.status === "deactivated"/);
assert.match(auditsSource, /\.withIndex\("by_auditId"/);
assert.match(auditsSource, /\.take\(\s*8\s*\)/);
assert.match(auditsSource, /ctx\.storage\.getUrl\(screenshot\.storageId\)/);
assert.doesNotMatch(
auditsSource.match(/export const getPublicBySlug[\s\S]*?export const/)?.[0] ?? "",
/usedSkills|internalSummary|skillSummaries|pageSpeedSummary/,
"The public query should not return internal audit fields.",
);
});
test("public audit write mutations require authenticated operators", async () => {
const auditsSource = await source("convex/audits.ts");
for (const exportName of [
"savePublicAuditContent",
"publishPublicAudit",
"reapprovePublicAudit",
"deactivatePublicAudit",
]) {
assert.match(auditsSource, new RegExp(`export const ${exportName} = mutation`));
}
assert.match(auditsSource, /ctx\.auth\.getUserIdentity\(\)/);
assert.match(auditsSource, /Nicht autorisiert/);
assert.match(auditsSource, /publishedAt:\s*now/);
assert.match(auditsSource, /deactivatedAt:\s*now/);
});