Files
Florian-netz/tests/e2e/verwaltung-scoping.spec.ts
Matthias Hochmeister 5cda09c411 Workstream 7: Wehr-Bereich — Fuhrpark & Benutzer (Phase 4)
Implementiert den auf die eigene brigadeId beschränkten Wehr-Bereich:
Profil (inkl. Inline-Geocoding via geocodeAddress), Fuhrpark (Fahrzeug per
Vorlage oder frei, typisierter Merkmal-Editor), Geräte (Kategorie, Werte,
Zuordnung Fahrzeug/„im Gerätehaus") und Benutzerkonten (wehr_admin/wehr_read).

- Schema importiert (nicht neu definiert); ASCII-Property wehrfuehrer.
- Default-deny dreifach: Layout-Guard requireWehrAdmin() + jede Server Action
  beginnt mit requireWehrAdmin(); fremde Entities -> notFound() (404).
- Validierung an der Grenze (Zod): buildMerkmalValuesSchema validiert Werte
  typgerecht gegen die serverseitig aufgelösten Definitionen; Rolle auf
  wehr_admin|wehr_read beschränkt (platform_admin abgelehnt).
- upsertMerkmalValues delete-then-insert mit typisierter Drizzle-Tx (kein any);
  boolean false/num 0 gelten als gesetzt.
- argon2id-Einmalpasswort beim Benutzeranlegen; Selbst-Deaktivierung verhindert.
- Audit vollständig: brigade.profile_update, vehicle.create/update/delete/status,
  equipment.create/update/delete/status, user.create/deactivate.
- Vorgabewerte aus drei typisierten Spalten (vorgabewert_num/_text/_bool).
- i18n via zentraler de.ts; loading/empty/error-konforme Listen.

Tests: 22 neue Unit-Tests (vehicle/equipment/brigade-user-Validierung,
upsertMerkmalValues) grün; Playwright-Specs verwaltung-fuhrpark + -scoping
geschrieben (deferred: kein Server/DB in der Sandbox).

Verifikation offline: tsc --noEmit clean, eslint clean, vitest 147 passed,
next build exit 0 (alle /verwaltung/*-Routen), drizzle-kit check ohne Drift.

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

68 lines
2.1 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";
/**
* Scoping & Gating des Wehr-Bereichs (Workstream 7, Querschnittsstandard 13,
* default-deny dreifach).
*
* NICHT in der Sandbox ausführbar (kein Server/DB) — deferred. Erwartet:
* - anonym: jede /verwaltung/*-Seite -> Redirect auf /login.
* - wehr_read: jede /verwaltung/*-Seite -> 403 (forbidden()).
* - wehr_admin: erreichbar; fremdes Fahrzeug/Gerät -> 404 (notFound).
*
* Negativ-Probe: Entfernen von `requireWehrAdmin()` aus (app)/verwaltung/
* layout.tsx ODER aus einer Server-Action muss diese Suite rot machen.
*/
const VERWALTUNG_PAGES = [
"/verwaltung/profil",
"/verwaltung/fahrzeuge",
"/verwaltung/fahrzeuge/neu",
"/verwaltung/geraete",
"/verwaltung/geraete/neu",
"/verwaltung/benutzer",
];
test.describe("Verwaltung: anonym -> Redirect auf /login", () => {
test.use({ storageState: { cookies: [], origins: [] } });
for (const path of VERWALTUNG_PAGES) {
test(`anonymer Aufruf von ${path} leitet auf /login um`, async ({
page,
}) => {
await page.goto(path);
await expect(page).toHaveURL(/\/login/);
});
}
});
test.describe("Verwaltung: wehr_read -> 403", () => {
test.skip(
!process.env.E2E_WEHR_READ_STATE,
"benötigt wehr_read-Fixture (Test-Workstream)",
);
test.use({
storageState: process.env.E2E_WEHR_READ_STATE ?? { cookies: [], origins: [] },
});
for (const path of VERWALTUNG_PAGES) {
test(`wehr_read-Aufruf von ${path} -> 403`, async ({ page }) => {
const res = await page.goto(path);
expect(res?.status()).toBe(403);
});
}
});
test.describe("Verwaltung: fremde Wehr -> 404 (Scoping)", () => {
test.skip(
!process.env.E2E_WEHR_ADMIN_STATE || !process.env.E2E_FOREIGN_VEHICLE_ID,
"benötigt wehr_admin-Fixture + fremde Fahrzeug-ID (Test-Workstream)",
);
test.use({
storageState: process.env.E2E_WEHR_ADMIN_STATE ?? { cookies: [], origins: [] },
});
test("Aufruf eines fremden Fahrzeugs liefert 404", async ({ page }) => {
const res = await page.goto(
`/verwaltung/fahrzeuge/${process.env.E2E_FOREIGN_VEHICLE_ID}`,
);
expect(res?.status()).toBe(404);
});
});