Files
Florian-netz/tests/e2e/admin-gating.spec.ts
Matthias Hochmeister e97e16d254 Workstream 6: Admin-Panel — Taxonomie & Bereitstellung (Phase 4)
Platform-Admin-only Oberflächen und Domänenlogik:

- codes.ts erweitert um allradCode/normalizeCode/codesMatch (Allrad-Infix
  kanonisch; Suche importiert weiterhin expandNameQuery). Pure-Unit-Tests.
- slug.ts (Idempotenz-Key-Erzeugung) + Tests.
- audit.ts: writeAudit mit EINER Signatur und optionalem typisierten tx.
- provisioning.ts: createBrigadeWithFirstAdmin (Geocoding inline, argon2id,
  Audit brigade.create/user.create) + resetUserPassword (Audit user.reset).
- Zod-Validierung: merkmal/template/equipment-category/brigade (+ Tests).
- Server Actions (jede mit Guard als erster Anweisung, default-deny):
  merkmale (CRUD, Delete blockiert bei Referenz), proposals (promote/merge mit
  Typ-Kompatibilität), templates (Merkmale/Vorgabewerte/Aliasse), equipment-
  categories, brigades (Bereitstellung/Reset). Audit in allen Schreib-Actions.
- (admin)-Route-Group: Layout mit requirePlatformAdmin als erster Zeile,
  AdminNav, DataTable, loading/error; Seiten für Merkmale (+Editor), Vorschläge
  (Merge), Vorlagen (+Detail mit Merkmal-/Alias-Editor und Allrad-Hinweis),
  Geräte-Kategorien (+Detail), Wehren (Liste/neu/Detail mit Passwort-Reset),
  paginierter Audit-Viewer mit Filter. Jede Seite ruft zusätzlich den Guard.
- i18n: admin-Strings in zentraler de.ts.
- Playwright-Specs (deferred, nicht ausgeführt): admin-gating,
  admin-merkmal-proposal, admin-brigade-provision.

Schema NICHT neu definiert — nur importiert. codes.ts ist hier Eigentümer.

Offline-Verifikation: tsc --noEmit grün; eslint grün; vitest run grün
(119 passed, 7 DB-roundtrip skipped); next build Exit 0; drizzle-kit check ok.
DB-/Server-/Browser-abhängige Schritte deferred (kein Postgres/Server im
Sandbox).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 10:30:52 +02:00

74 lines
2.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { test, expect } from "@playwright/test";
/**
* Admin-Gating (Workstream 6, Querschnittsstandard 13, default-deny dreifach).
*
* NICHT in der Sandbox ausführbar (kein Server/DB) — deferred. Wird über einen
* laufenden Server ausgeführt. Erwartet:
* - anonym: jede /admin/*-Seite -> Redirect auf /login.
* - wehr_admin / wehr_read: jede /admin/*-Seite -> 403 (forbidden()).
* - platform_admin: /admin erreichbar.
*
* Negativ-Probe: Entfernen von `requirePlatformAdmin()` aus (admin)/layout.tsx
* muss diese Suite rot machen.
*/
const ADMIN_PAGES = [
"/admin",
"/admin/merkmale",
"/admin/merkmale/proposals",
"/admin/vorlagen",
"/admin/geraete-kategorien",
"/admin/wehren",
"/admin/wehren/neu",
"/admin/audit",
];
test.describe("Admin: anonym -> Redirect auf /login", () => {
test.use({ storageState: { cookies: [], origins: [] } });
for (const path of ADMIN_PAGES) {
test(`anonymer Aufruf von ${path} leitet auf /login um`, async ({
page,
}) => {
await page.goto(path);
await expect(page).toHaveURL(/\/login/);
});
}
});
// Diese Projekte setzen einen storageState eines wehr_admin/wehr_read-Kontos
// voraus (Test-Workstream stellt die Fixtures bereit). Hier dokumentiert als
// erwartetes Verhalten; das tatsächliche Konto wird über --project gewählt.
test.describe("Admin: falsche Rolle -> 403", () => {
test.skip(
!process.env.E2E_WEHR_ADMIN_STATE,
"benötigt wehr_admin-Fixture (Test-Workstream)",
);
test.use({
storageState: process.env.E2E_WEHR_ADMIN_STATE ?? { cookies: [], origins: [] },
});
for (const path of ADMIN_PAGES) {
test(`wehr_admin-Aufruf von ${path} -> 403`, async ({ page }) => {
const res = await page.goto(path);
expect(res?.status()).toBe(403);
});
}
});
test.describe("Admin: platform_admin -> erreichbar", () => {
test.skip(
!process.env.E2E_PLATFORM_ADMIN_STATE,
"benötigt platform_admin-Fixture (Test-Workstream)",
);
test.use({
storageState:
process.env.E2E_PLATFORM_ADMIN_STATE ?? { cookies: [], origins: [] },
});
test("/admin ist als platform_admin erreichbar", async ({ page }) => {
await page.goto("/admin");
await expect(
page.getByRole("heading", { name: "Administration" }),
).toBeVisible();
});
});