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>
This commit is contained in:
67
tests/e2e/verwaltung-scoping.spec.ts
Normal file
67
tests/e2e/verwaltung-scoping.spec.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { test, expect } from "@playwright/test";
|
||||
|
||||
/**
|
||||
* Scoping & Gating des Wehr-Bereichs (Workstream 7, Querschnittsstandard 1–3,
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user