({ createApiClient: vi.fn() }));
+
+import { createApiClient } from '$lib/api.server';
+
+const mockFetch = vi.fn() as unknown as typeof fetch;
+
+beforeEach(() => vi.clearAllMocks());
+
+// ─── happy path ───────────────────────────────────────────────────────────────
+
+describe('person detail load — happy path', () => {
+ it('returns person, sentDocuments, and receivedDocuments on success', async () => {
+ vi.mocked(createApiClient).mockReturnValue({
+ GET: vi
+ .fn()
+ .mockResolvedValueOnce({
+ response: { ok: true, status: 200 },
+ data: { id: 'p1', firstName: 'Hans', lastName: 'Müller' }
+ })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [{ id: 'd1', title: 'Brief' }] })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [] })
+ } as ReturnType);
+
+ const result = await load({ params: { id: 'p1' }, fetch: mockFetch });
+
+ expect(result.person.firstName).toBe('Hans');
+ expect(result.sentDocuments).toHaveLength(1);
+ expect(result.receivedDocuments).toEqual([]);
+ });
+
+ it('returns empty arrays when sent/received document APIs fail', async () => {
+ vi.mocked(createApiClient).mockReturnValue({
+ GET: vi
+ .fn()
+ .mockResolvedValueOnce({
+ response: { ok: true, status: 200 },
+ data: { id: 'p1', firstName: 'Anna', lastName: 'Schmidt' }
+ })
+ .mockResolvedValueOnce({ response: { ok: false }, data: null })
+ .mockResolvedValueOnce({ response: { ok: false }, data: null })
+ } as ReturnType);
+
+ const result = await load({ params: { id: 'p1' }, fetch: mockFetch });
+
+ expect(result.sentDocuments).toEqual([]);
+ expect(result.receivedDocuments).toEqual([]);
+ });
+});
+
+// ─── error paths ──────────────────────────────────────────────────────────────
+
+describe('person detail load — error paths', () => {
+ it('throws 404 when person does not exist', async () => {
+ vi.mocked(createApiClient).mockReturnValue({
+ GET: vi
+ .fn()
+ .mockResolvedValueOnce({ response: { ok: false, status: 404 }, error: null })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [] })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [] })
+ } as ReturnType);
+
+ await expect(load({ params: { id: 'missing' }, fetch: mockFetch })).rejects.toMatchObject({
+ status: 404
+ });
+ });
+
+ it('throws 403 when person is not accessible', async () => {
+ vi.mocked(createApiClient).mockReturnValue({
+ GET: vi
+ .fn()
+ .mockResolvedValueOnce({ response: { ok: false, status: 403 }, error: null })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [] })
+ .mockResolvedValueOnce({ response: { ok: true }, data: [] })
+ } as ReturnType);
+
+ await expect(load({ params: { id: 'forbidden' }, fetch: mockFetch })).rejects.toMatchObject({
+ status: 403
+ });
+ });
+});
diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts
index c5f71a26..38a3d537 100644
--- a/frontend/vite.config.ts
+++ b/frontend/vite.config.ts
@@ -43,6 +43,16 @@ export default defineConfig({
],
test: {
expect: { requireAssertions: true },
+ coverage: {
+ provider: 'v8',
+ reporter: ['text', 'lcov'],
+ // Measure utility and server-side logic only — Svelte components
+ // run in the browser project and are excluded here intentionally.
+ include: ['src/lib/utils/**', 'src/lib/server/**'],
+ thresholds: {
+ branches: 80
+ }
+ },
projects: [
{
extends: './vite.config.ts',