68 lines
2.6 KiB
TypeScript
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/);
|
|
});
|