- test-tenant-isolation: Compound-Key mit fremdem Mandanten – im Owner-Betrieb Throw (Tenant-Guard), unter scharfer RLS liefert die DB null; beides = kein Datenabfluss - run-tests.ts: lädt .env und leitet RLS_DATABASE_URL (Rolle craftvia_app) aus DATABASE_URL ab, wenn RLS_ENFORCED=true und keine URL gesetzt ist Nachweis: RLS_ENFORCED=true npm run test → 52/52; npm run gate → 52/52. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
2.3 KiB
TypeScript
55 lines
2.3 KiB
TypeScript
/**
|
|
* Test-Runner: führt alle `scripts/test-*.ts` nacheinander via tsx aus und liefert eine
|
|
* Zusammenfassung + Exit-Code (≠ 0, sobald ein Test scheitert).
|
|
*
|
|
* Voraussetzungen: lokale Infra (Postgres/Redis/Garage) läuft, `.env` gesetzt, Datenbank
|
|
* migriert und geseedet (`npx prisma migrate deploy && npx prisma db seed`).
|
|
*
|
|
* Lauf: npm run test (alle)
|
|
* npm run test -- mail tenant (nur Tests, deren Name einen der Filter enthält)
|
|
*/
|
|
import "dotenv/config";
|
|
import { spawnSync } from "node:child_process";
|
|
import { readdirSync } from "node:fs";
|
|
import { join, dirname } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
|
|
const filters = process.argv.slice(2);
|
|
|
|
const tests = readdirSync(SCRIPTS_DIR)
|
|
.filter((f) => /^test-.+\.ts$/.test(f))
|
|
.filter((f) => filters.length === 0 || filters.some((flt) => f.includes(flt)))
|
|
.sort();
|
|
|
|
if (tests.length === 0) {
|
|
console.error("Keine Tests gefunden.");
|
|
process.exit(1);
|
|
}
|
|
|
|
// RLS runs (RLS_ENFORCED=true): db.ts refuses to start without RLS_DATABASE_URL. Default it to the
|
|
// same database as DATABASE_URL with the restricted role (as scripts/test-rls-enforcement.ts does).
|
|
if (process.env.RLS_ENFORCED === "true" && !process.env.RLS_DATABASE_URL && process.env.DATABASE_URL) {
|
|
const url = new URL(process.env.DATABASE_URL);
|
|
url.username = "craftvia_app";
|
|
url.password = "craftvia_app_local";
|
|
process.env.RLS_DATABASE_URL = url.toString();
|
|
}
|
|
|
|
const results: { name: string; ok: boolean; ms: number }[] = [];
|
|
for (const file of tests) {
|
|
const started = Date.now();
|
|
console.log(`\n━━━ ${file} ━━━`);
|
|
const run = spawnSync(process.execPath, ["--import", "tsx", join(SCRIPTS_DIR, file)], {
|
|
stdio: "inherit",
|
|
env: process.env,
|
|
});
|
|
results.push({ name: file, ok: run.status === 0, ms: Date.now() - started });
|
|
}
|
|
|
|
const failed = results.filter((r) => !r.ok);
|
|
console.log("\n══════════ Test-Zusammenfassung ══════════");
|
|
for (const r of results) console.log(`${r.ok ? "✓" : "✗"} ${r.name.padEnd(36)} ${(r.ms / 1000).toFixed(1)}s`);
|
|
console.log(`\n${results.length - failed.length}/${results.length} Testskripte grün${failed.length ? ` — ${failed.length} fehlgeschlagen` : ""}.`);
|
|
process.exit(failed.length ? 1 : 0);
|