- web/retention-admin: eigenstaendige Next.js/React/TypeScript-App (kein Backend-Annex), auf web/shl (SHL-01) aufbauend - classes: Aufbewahrungsklasse anlegen/aendern/deaktivieren (AC1) - preview: Vorschauliste 30 Tage, nutzt denselben Endpunkt wie der periodische Job (AC2) - lib/api.ts: ForbiddenError bei 403, getrennt behandelt, keine eigene Autorisierungslogik im Frontend (RBAC-Entscheidung liegt bei RET-08/RBAC-06) - expliziter 403-Nachweis (nicht nur der 200-Fall): lib/api.test.ts, ClassesPage/PreviewPage zeigen 'Zugriff verweigert' statt leerer Seite - lokal getestet: tsc clean, next build clean, vitest 3/3 gruen Pruefungen siehe archive/docs/RET-06-PRUEFPROTOKOLL.md
52 lines
1.7 KiB
TypeScript
52 lines
1.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import { ForbiddenError, fetchClassRules, configureClassRule } from "./api";
|
|
|
|
const ORIGINAL_ENV = process.env;
|
|
|
|
beforeEach(() => {
|
|
process.env = { ...ORIGINAL_ENV, NEXT_PUBLIC_RETENTION_API_URL: "http://backend.test" };
|
|
});
|
|
|
|
afterEach(() => {
|
|
process.env = ORIGINAL_ENV;
|
|
vi.restoreAllMocks();
|
|
});
|
|
|
|
// Akzeptanzkriterium 3 ("nur berechtigten Rollen zugaenglich"): der
|
|
// Negativfall MUSS explizit gepruft werden, nicht nur der Erfolgsfall -
|
|
// Frontend wirft ForbiddenError, wenn RET-06-API (real gegen RBAC-06,
|
|
// siehe RET-08) mit 403 antwortet.
|
|
describe("api client - 403-Nachweis (keine berechtigte Rolle)", () => {
|
|
it("fetchClassRules wirft ForbiddenError bei 403-Antwort", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockResolvedValue(new Response(null, { status: 403 }))
|
|
);
|
|
|
|
await expect(fetchClassRules()).rejects.toBeInstanceOf(ForbiddenError);
|
|
});
|
|
|
|
it("configureClassRule wirft ForbiddenError bei 403-Antwort", async () => {
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockResolvedValue(new Response(null, { status: 403 }))
|
|
);
|
|
|
|
await expect(configureClassRule("klasse-x", "1 year")).rejects.toBeInstanceOf(ForbiddenError);
|
|
});
|
|
});
|
|
|
|
describe("api client - erlaubte Rolle", () => {
|
|
it("fetchClassRules liefert die Liste bei 200-Antwort", async () => {
|
|
const rules = [{ RetentionClass: "klasse-a", Duration: "5 years", Active: true }];
|
|
vi.stubGlobal(
|
|
"fetch",
|
|
vi.fn().mockResolvedValue(
|
|
new Response(JSON.stringify(rules), { status: 200, headers: { "Content-Type": "application/json" } })
|
|
)
|
|
);
|
|
|
|
await expect(fetchClassRules()).resolves.toEqual(rules);
|
|
});
|
|
});
|