import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { adminClient, createHrUser, createTestPosition, deleteTestEmployee, deleteTestPosition, deleteTestUser, hireTestEmployee, isoDateOffset, pickSeededUnit, signInAs, type TestUser, } from "./helpers"; import type { SupabaseClient } from "@supabase/supabase-js"; import type { Database } from "@/lib/supabase/types"; // Two guards added by supabase/migrations/20260714120600_data_integrity_guards.sql // that previously had no enforcement anywhere: a Karenz return date may not // precede its own Karenz start, and a history entry may not predate the // employee's entry date. describe("data integrity guards", () => { let hrUser: TestUser; let hrClient: SupabaseClient; let unitA: { id: string }; const employeeIds: string[] = []; const positionIds: string[] = []; beforeAll(async () => { hrUser = await createHrUser({ active: true }); hrClient = await signInAs(hrUser); unitA = await pickSeededUnit(); }); afterAll(async () => { for (const id of employeeIds) await deleteTestEmployee(id); for (const id of positionIds) await deleteTestPosition(id); await deleteTestUser(hrUser); }); // Jede Einstellung braucht im OM-Modell eine freie Zielplanstelle; eine // geteilte wäre nach der ersten besetzt. async function freshPosition(): Promise { const id = await createTestPosition(hrClient, unitA.id, { valid_from: isoDateOffset(-40) }); positionIds.push(id); return id; } async function freshEmployeeOnKarenz(): Promise<{ employeeId: string; karenzStartDate: string }> { const employeeId = await hireTestEmployee(hrClient, await freshPosition()); employeeIds.push(employeeId); const karenzStartDate = isoDateOffset(-5); const { error } = await hrClient.rpc("start_karenz", { payload: { employee_id: employeeId, karenz_start_date: karenzStartDate, planned_return_date: isoDateOffset(100) }, }); if (error) throw new Error(`start_karenz setup failed: ${error.message}`); return { employeeId, karenzStartDate }; } it("adjust_karenz_return rejects a return date on or before karenz_start_date", async () => { const { employeeId, karenzStartDate } = await freshEmployeeOnKarenz(); const { error } = await hrClient.rpc("adjust_karenz_return", { payload: { employee_id: employeeId, new_return_date: karenzStartDate }, }); expect(error?.message).toMatch(/Rückkehrdatum muss nach dem Karenzbeginn/); }); it("adjust_karenz_return accepts a return date after karenz_start_date", async () => { const { employeeId } = await freshEmployeeOnKarenz(); const newReturnDate = isoDateOffset(50); const { error } = await hrClient.rpc("adjust_karenz_return", { payload: { employee_id: employeeId, new_return_date: newReturnDate }, }); expect(error).toBeNull(); const { data: employee } = await adminClient.from("employees").select("karenz_return_date").eq("id", employeeId).single(); expect(employee?.karenz_return_date).toBe(newReturnDate); }); it("record_karenz_return rejects a return date on or before karenz_start_date", async () => { const { employeeId, karenzStartDate } = await freshEmployeeOnKarenz(); const { error } = await hrClient.rpc("record_karenz_return", { payload: { employee_id: employeeId, return_date: karenzStartDate, employment_mode: "unverändert" }, }); expect(error?.message).toMatch(/Rückkehrdatum muss nach dem Karenzbeginn/); }); it("record_karenz_return with a valid date flips status back to Aktiv and clears karenz_start_date", async () => { const { employeeId } = await freshEmployeeOnKarenz(); const { error } = await hrClient.rpc("record_karenz_return", { payload: { employee_id: employeeId, return_date: isoDateOffset(0), employment_mode: "unverändert" }, }); expect(error).toBeNull(); const { data: employee } = await adminClient .from("employees") .select("status, karenz_start_date, karenz_return_date") .eq("id", employeeId) .single(); expect(employee?.status).toBe("Aktiv"); expect(employee?.karenz_start_date).toBeNull(); expect(employee?.karenz_return_date).toBeNull(); }); it("rejects an employee_history row dated before the employee's entry_date", async () => { const employeeId = await hireTestEmployee(hrClient, await freshPosition(), { entry_date: isoDateOffset(-10) }); employeeIds.push(employeeId); const { error } = await adminClient.from("employee_history").insert({ employee_id: employeeId, event_date: isoDateOffset(-11), event_type: "Vertragsänderung", description: "Sollte vom Trigger abgelehnt werden", }); expect(error?.message).toMatch(/darf nicht vor dem Eintrittsdatum/); }); it("accepts an employee_history row dated exactly on the entry_date", async () => { const entryDate = isoDateOffset(-10); const employeeId = await hireTestEmployee(hrClient, await freshPosition(), { entry_date: entryDate }); employeeIds.push(employeeId); const { error } = await adminClient.from("employee_history").insert({ employee_id: employeeId, event_date: entryDate, event_type: "Vertragsänderung", description: "Am Eintrittsdatum selbst, sollte erlaubt sein", }); expect(error).toBeNull(); }); });