import { act, renderHook, waitFor } from '@testing-library/react' import { beforeEach, describe, expect, it, vi } from 'vitest' import type { AssistantQueryResponse } from '../types' const mocks = vi.hoisted(() => ({ status: vi.fn(), models: vi.fn(), query: vi.fn(), })) vi.mock('../services/api/assistant', () => ({ assistantApi: mocks, })) import { useGeoAssistant } from './useGeoAssistant' function deferred() { let resolve!: (value: T) => void let reject!: (reason?: unknown) => void const promise = new Promise((resolvePromise, rejectPromise) => { resolve = resolvePromise reject = rejectPromise }) return { promise, resolve, reject } } function response(answer: string): AssistantQueryResponse { return { answer, model: 'geo-model', scope_label: 'testgebied', context_metrics: [], temporal_series: [], source_dataset_ids: [], warnings: [], generated_at: '2026-08-23T12:00:00Z', } } describe('useGeoAssistant request scope', () => { beforeEach(() => { window.localStorage.clear() mocks.status.mockResolvedValue({ enabled: true, reachable: true, status: 'ready', base_url: 'http://localhost', default_model: 'geo-model', model_count: 1, limitation_message: '', }) mocks.models.mockResolvedValue({ items: [{ name: 'geo-model', capabilities: ['chat'] }], total: 1, default_model: 'geo-model', }) }) it('ignores an answer that returns after the active project changed', async () => { const pending = deferred() mocks.query.mockReturnValueOnce(pending.promise) const { result, rerender } = renderHook( ({ projectId }) => useGeoAssistant({ selectedProjectId: projectId, selectedAreaId: null, selectionBbox: null, }), { initialProps: { projectId: 'project-1' } }, ) await waitFor(() => expect(result.current.selectedModel).toBe('geo-model')) let request!: Promise act(() => { request = result.current.ask('Wat staat hier?') }) rerender({ projectId: 'project-2' }) await act(async () => { pending.resolve(response('antwoord uit project 1')) await request }) expect(result.current.messages).toEqual([]) expect(result.current.loading).toBe(false) expect(result.current.error).toBeNull() }) it('lets only the newest request update a conversation', async () => { const older = deferred() const newer = deferred() mocks.query .mockReturnValueOnce(older.promise) .mockReturnValueOnce(newer.promise) const { result } = renderHook(() => useGeoAssistant({ selectedProjectId: 'project-1', selectedAreaId: null, selectionBbox: null, })) await waitFor(() => expect(result.current.selectedModel).toBe('geo-model')) let olderRequest!: Promise let newerRequest!: Promise act(() => { olderRequest = result.current.ask('Eerste vraag') }) act(() => { newerRequest = result.current.ask('Tweede vraag') }) await act(async () => { newer.resolve(response('nieuwste antwoord')) await newerRequest }) await act(async () => { older.resolve(response('verouderd antwoord')) await olderRequest }) const assistantMessages = result.current.messages.filter((message) => message.role === 'assistant') expect(assistantMessages.map((message) => message.content)).toEqual(['nieuwste antwoord']) expect(result.current.loading).toBe(false) }) })