import { afterEach, describe, expect, it, vi } from 'vitest'; /** * auth-actions.test (fetchSessionState) — Sitzungswaechter (quick-260917-gyd). * * next/headers wird gemockt (cookies() liefert ein Promise auf ein Objekt * mit get/set/delete aus vi.hoisted, Muster: module-access.test.tsx), * next/navigation ebenfalls (redirect ist hier ungenutzt, muss aber * importierbar bleiben), fetch per vi.stubGlobal. Die 'use server'- * Direktive ist unter vitest wirkungslos. */ const { cookieGet, cookieSet, cookieDelete } = vi.hoisted(() => ({ cookieGet: vi.fn(), cookieSet: vi.fn(), cookieDelete: vi.fn(), })); vi.mock('next/headers', () => ({ cookies: () => Promise.resolve({ get: cookieGet, set: cookieSet, delete: cookieDelete, }), })); vi.mock('next/navigation', () => ({ redirect: vi.fn(), })); afterEach(() => { vi.clearAllMocks(); vi.unstubAllGlobals(); }); describe('fetchSessionState', () => { it('Test 1: kein Cookie -> unauthenticated, kein fetch', async () => { cookieGet.mockReturnValue(undefined); const fetchMock = vi.fn(); vi.stubGlobal('fetch', fetchMock); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unauthenticated' }); expect(fetchMock).not.toHaveBeenCalled(); }); it('Test 2: 200 mit Benutzer -> authenticated, Cookie bleibt, Cookie-Header gesetzt', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); const fetchMock = vi.fn(() => Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve('{"id":"u1","username":"schalli"}'), }), ); vi.stubGlobal('fetch', fetchMock); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result.status).toBe('authenticated'); if (result.status === 'authenticated') { expect(result.user.username).toBe('schalli'); } expect(cookieDelete).not.toHaveBeenCalled(); const [, options] = fetchMock.mock.calls[0] as unknown as [ string, RequestInit, ]; expect((options.headers as Record).Cookie).toBe( 'session=session-abc', ); }); it('Test 3: Status 401 -> unauthenticated, Cookie genau einmal geloescht', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: false, status: 401, text: () => Promise.resolve('') })), ); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unauthenticated' }); expect(cookieDelete).toHaveBeenCalledTimes(1); expect(cookieDelete).toHaveBeenCalledWith('session'); }); it('Test 4: Status 403 -> unauthenticated, Cookie geloescht', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: false, status: 403, text: () => Promise.resolve('') })), ); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unauthenticated' }); expect(cookieDelete).toHaveBeenCalledTimes(1); }); it('Test 5: Status 200 mit leerem Body (oder "null") -> unauthenticated, Cookie geloescht', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve('') })), ); const { fetchSessionState } = await import('./auth-actions'); const result1 = await fetchSessionState(); expect(result1).toEqual({ status: 'unauthenticated' }); expect(cookieDelete).toHaveBeenCalledTimes(1); vi.clearAllMocks(); cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve('null') })), ); const result2 = await fetchSessionState(); expect(result2).toEqual({ status: 'unauthenticated' }); expect(cookieDelete).toHaveBeenCalledTimes(1); }); it('Test 6: Status 500 -> unavailable, Cookie bleibt', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: false, status: 500, text: () => Promise.resolve('') })), ); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unavailable' }); expect(cookieDelete).not.toHaveBeenCalled(); }); it('Test 7: fetch wirft (Netzwerkfehler) -> unavailable, Cookie bleibt', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.reject(new TypeError('fetch failed'))), ); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unavailable' }); expect(cookieDelete).not.toHaveBeenCalled(); }); it('Test 8: Status 200 mit nicht-JSON-Body -> unavailable, Cookie bleibt', async () => { cookieGet.mockReturnValue({ value: 'session-abc' }); vi.stubGlobal( 'fetch', vi.fn(() => Promise.resolve({ ok: true, status: 200, text: () => Promise.resolve('') })), ); const { fetchSessionState } = await import('./auth-actions'); const result = await fetchSessionState(); expect(result).toEqual({ status: 'unavailable' }); expect(cookieDelete).not.toHaveBeenCalled(); }); });