import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { getCsrfToken, setCsrfToken } from '@/api/client'; import { streamChat } from '@/api/ai'; describe('streamChat CSRF header', () => { const originalFetch = globalThis.fetch; beforeEach(() => { setCsrfToken('test-csrf-token'); globalThis.fetch = vi.fn().mockResolvedValue( new Response( 'data: {"type":"done"}\n\ndata: [DONE]\n\n', { status: 200, headers: { 'Content-Type': 'text/event-stream' }, }, ), ) as unknown as typeof fetch; }); afterEach(() => { setCsrfToken(null); globalThis.fetch = originalFetch; vi.restoreAllMocks(); }); it('sends the shared client CSRF token as X-CSRF-Token header', async () => { expect(getCsrfToken()).toBe('test-csrf-token'); const gen = streamChat('conv-1', 'Hallo'); for await (const _ of gen) { // consume stream until [DONE] } const fetchMock = globalThis.fetch as ReturnType; expect(fetchMock).toHaveBeenCalledTimes(1); const [, init] = fetchMock.mock.calls[0] as [string, RequestInit]; const headers = init.headers as Record; // Regression: token previously came from a never-written sessionStorage key → 403 expect(headers['X-CSRF-Token']).toBe('test-csrf-token'); }); it('targets the conversation stream endpoint with JSON body', async () => { const gen = streamChat('conv-42', 'Frage', 'agent-9'); for await (const _ of gen) { // consume stream until [DONE] } const fetchMock = globalThis.fetch as ReturnType; const [url, init] = fetchMock.mock.calls[0] as [string, RequestInit]; expect(url).toBe('/api/v1/ai/conversations/conv-42/stream'); expect(init.method).toBe('POST'); expect(init.credentials).toBe('include'); expect(JSON.parse(init.body as string)).toEqual({ content: 'Frage', agent_id: 'agent-9' }); }); });