test: add comprehensive automated test coverage
- 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
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
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([]);
|
||||
});
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user