- Vitest multi-project setup (unit / integration / component) - Real-Postgres integration harness via in-process PGlite (TEST_DATABASE_URL override), migrations applied per worker; production code untouched - Unit: routing, locale, validation/Zod schemas, metadata, mail, site-theme, marquee, media, portfolio helpers, plus architecture-rule tests - Integration: Prisma data layer, API routes, and all server actions - Component: UI primitives and form components (jsdom + Testing Library) - 371 tests passing
123 lines
4.1 KiB
TypeScript
123 lines
4.1 KiB
TypeScript
import { readFileSync, readdirSync, statSync } from "fs";
|
|
import path from "path";
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
const ROOT = process.cwd();
|
|
|
|
function walk(dir: string, filter: (file: string) => boolean): string[] {
|
|
const absolute = path.join(ROOT, dir);
|
|
const results: string[] = [];
|
|
let entries: string[];
|
|
try {
|
|
entries = readdirSync(absolute);
|
|
} catch {
|
|
return results;
|
|
}
|
|
for (const entry of entries) {
|
|
if (entry === "node_modules" || entry === ".next") continue;
|
|
const full = path.join(absolute, entry);
|
|
const rel = path.relative(ROOT, full);
|
|
if (statSync(full).isDirectory()) {
|
|
results.push(...walk(rel, filter));
|
|
} else if (filter(full)) {
|
|
results.push(rel);
|
|
}
|
|
}
|
|
return results;
|
|
}
|
|
|
|
const read = (rel: string) => readFileSync(path.join(ROOT, rel), "utf8");
|
|
const isSource = (file: string) => /\.(ts|tsx)$/.test(file) && !file.endsWith(".d.ts");
|
|
|
|
const componentFiles = walk("components", isSource);
|
|
const appFiles = walk("app", isSource);
|
|
const libFiles = walk("lib", isSource);
|
|
const actionFiles = [...appFiles].filter((file) => /(^|\/)actions\.tsx?$/.test(file));
|
|
|
|
describe("architecture: data access boundaries", () => {
|
|
it("no component imports the Prisma client", () => {
|
|
const offenders = componentFiles.filter((file) => /["']@\/lib\/prisma["']/.test(read(file)));
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
|
|
it("no client component imports the Prisma client (defense in depth)", () => {
|
|
const offenders = [...componentFiles, ...appFiles].filter((file) => {
|
|
const source = read(file);
|
|
return /["']use client["']/.test(source) && /lib\/prisma/.test(source);
|
|
});
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
|
|
it("code imports admin server actions from the canonical _admin source, never the mirrors", () => {
|
|
const offenders = [...componentFiles, ...appFiles, ...libFiles].filter((file) =>
|
|
/from\s+["']@\/app\/(root|admin-internal)\//.test(read(file)),
|
|
);
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
|
|
it("lib modules never import from the app layer", () => {
|
|
const offenders = libFiles.filter((file) => {
|
|
const source = read(file);
|
|
return /from\s+["']@\/app\//.test(source) || /from\s+["']\.\.\/app\//.test(source);
|
|
});
|
|
expect(offenders).toEqual([]);
|
|
});
|
|
});
|
|
|
|
describe("architecture: server actions", () => {
|
|
it("finds the expected server action files", () => {
|
|
expect(actionFiles.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('every actions file starts with the "use server" directive', () => {
|
|
for (const file of actionFiles) {
|
|
const firstMeaningfulLine = read(file)
|
|
.split("\n")
|
|
.map((line) => line.trim())
|
|
.find((line) => line.length > 0);
|
|
expect(firstMeaningfulLine, file).toMatch(/^["']use server["'];?$/);
|
|
}
|
|
});
|
|
|
|
it("every admin action file enforces authentication", () => {
|
|
const adminActionFiles = actionFiles.filter((file) => file.includes(`${path.sep}_admin${path.sep}`));
|
|
expect(adminActionFiles.length).toBeGreaterThan(0);
|
|
for (const file of adminActionFiles) {
|
|
const source = read(file);
|
|
expect(
|
|
/ensureAdmin\s*\(/.test(source) ||
|
|
/requireAdminAuth\s*\(/.test(source) ||
|
|
/isAdminAuthenticated\s*\(/.test(source),
|
|
file,
|
|
).toBe(true);
|
|
}
|
|
});
|
|
});
|
|
|
|
describe("architecture: admin mirror parity", () => {
|
|
const canonicalPages = walk("app/_admin", (file) => /page\.tsx$/.test(file));
|
|
|
|
it("has admin pages to mirror", () => {
|
|
expect(canonicalPages.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
for (const mirror of ["root", "admin-internal"]) {
|
|
it(`mirrors every _admin page under app/${mirror} via a re-export`, () => {
|
|
const missing: string[] = [];
|
|
for (const page of canonicalPages) {
|
|
const mirrored = page.replace(`app${path.sep}_admin${path.sep}`, `app${path.sep}${mirror}${path.sep}`);
|
|
try {
|
|
const source = read(mirrored);
|
|
if (!source.includes("_admin")) {
|
|
missing.push(`${mirrored} (does not re-export _admin)`);
|
|
}
|
|
} catch {
|
|
missing.push(`${mirrored} (missing)`);
|
|
}
|
|
}
|
|
expect(missing).toEqual([]);
|
|
});
|
|
}
|
|
});
|