Rollen Chunk 6: Team-Tab mit Matrix-Kalender und Genehmigungen
Docker Image / test (push) Canceled after 0s
Docker Image / image (push) Canceled after 0s

This commit is contained in:
loki committed 2026-10-11 20:19:04 +02:00
1 parent 9346945e80
commit 73ade9d4ec
1 file changed
+161
+161
View File
@@ -0,0 +1,161 @@
import "../test-helpers";
import { afterEach, beforeEach, describe, expect, test } from "vitest";
import { getAuth } from "@/lib/auth";
import { getDb, initializeDatabase } from "@/lib/db";
import { GET } from "./route";
import { saveWeeklyTargets, saveVacationRequest } from "@/lib/repository";
import { putJson } from "../test-helpers";
beforeEach(() => {
process.env.AUTH_MODE = "password";
process.env.BETTER_AUTH_SECRET = "test-secret-mindestens-32-zeichen-lang!";
});
afterEach(() => {
delete process.env.AUTH_MODE;
delete process.env.BETTER_AUTH_SECRET;
});
async function signUp(name: string, email: string): Promise<void> {
const { POST } = await import("../auth/[...all]/route");
const response = await POST(
new Request("http://localhost/api/auth/sign-up/email", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ name, email, password: "geheim-geheim!" }),
}),
);
if (response.status !== 200) {
throw new Error(`Registrierung: ${response.status}`);
}
}
async function cookieFor(email: string): Promise<string> {
const auth = getAuth();
if (!auth) {
throw new Error("Auth erwartet");
}
const response = await auth.api.signInEmail({
body: { email, password: "geheim-geheim!" },
asResponse: true,
});
const setCookie = response.headers
.getSetCookie()
.find((entry) => entry.startsWith("better-auth.session_token="));
if (!setCookie) {
throw new Error("Sitzungscookie erwartet");
}
return setCookie.split(";")[0];
}
async function adminGet(
path: string,
cookie: string,
): Promise<Response> {
const { GET: authGet } = await import("../auth/[...all]/route");
return authGet(new Request(`http://localhost${path}`, { headers: { cookie } }));
}
async function adminPost(
path: string,
cookie: string,
body: unknown,
): Promise<Response> {
const { POST } = await import("../auth/[...all]/route");
return POST(
new Request(`http://localhost${path}`, {
method: "POST",
headers: { "content-type": "application/json", cookie },
body: JSON.stringify(body),
}),
);
}
async function setup(): Promise<{ admin: string; team: string; benutzer: string }> {
await signUp("Admina", "admina@example.org");
const admin = await cookieFor("admina@example.org");
for (const [name, email] of [
["Leitung", "leitung@example.org"],
["Benutzer", "benutzer@example.org"],
] as const) {
await adminPost("/api/auth/admin/create-user", admin, {
name,
email,
password: "geheim-geheim!",
role: "benutzer",
});
const listed = (await (
await adminGet("/api/auth/admin/list-users?limit=10", admin)
).json()) as { users?: { id: string; email: string }[] };
const id = listed.users?.find((user) => user.email === email)?.id;
if (!id) {
throw new Error("Benutzer-ID erwartet");
}
await adminPost("/api/auth/admin/set-role", admin, {
userId: id,
role: email === "leitung@example.org" ? "teamleiter" : "benutzer",
});
}
return {
admin,
team: await cookieFor("leitung@example.org"),
benutzer: await cookieFor("benutzer@example.org"),
};
}
function authed(path: string, cookie: string): Request {
return new Request(path, { headers: { cookie } });
}
describe("GET /api/team", () => {
test("403 für normale Benutzer, 401 ohne Sitzung", async () => {
const { team, benutzer } = await setup();
const denied = await GET(authed("http://localhost/api/team?year=2026", benutzer));
expect(denied.status).toBe(403);
expect((await denied.json()).error.code).toBe("forbidden");
const anonymous = await GET(new Request("http://localhost/api/team?year=2026"));
expect(anonymous.status).toBe(401);
void team;
});
test("liefert Team mit Salden und Buchungen", async () => {
const { team, benutzer } = await setup();
const db = getDb();
initializeDatabase(db);
const { listAllUsers, sumVacationUnitsByYear } = await import("@/lib/repository");
void listAllUsers;
void sumVacationUnitsByYear;
const benutzerId = db
.prepare('SELECT id FROM "user" WHERE email = ?')
.get("benutzer@example.org") as { id: string };
saveWeeklyTargets(db, benutzerId.id, { 0: 0, 1: 480, 2: 480, 3: 480, 4: 480, 5: 480, 6: 0 });
saveVacationRequest(db, benutzerId.id, {
startDate: "2026-07-06",
endDate: "2026-07-06",
dayFraction: "full",
status: "beantragt",
days: [{ date: "2026-07-06", units: 2 }],
});
const response = await GET(authed("http://localhost/api/team?year=2026", team));
expect(response.status).toBe(200);
const body = (await response.json()) as {
team: Array<{
name: string;
role: string;
entitlementUnits: number;
pendingUnits: number;
requests: Array<{ status: string; startDate: string }>;
sickDays: unknown[];
weekdayTargets: Record<string, number> | null;
}>;
};
const benutzerRow = body.team.find((row) => row.role === "benutzer");
if (!benutzerRow) {
throw new Error("Benutzerzeile erwartet");
}
expect(benutzerRow.name).toBe("Benutzer");
expect(benutzerRow.pendingUnits).toBe(2);
expect(benutzerRow.requests[0].status).toBe("beantragt");
expect(benutzerRow.weekdayTargets?.[1]).toBe(480);
});
});