import { act, renderHook, waitFor } from '@testing-library/react'; import { afterEach, describe, expect, it, vi } from 'vitest'; import type { ReactNode } from 'react'; import { AllProviders } from '../../setup/test-utils'; import { useCursor } from '@/hooks/use-cursor'; function createJsonResponse(body: Record, status = 200): Response { return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' }, }); } const wrapper = ({ children }: { children: ReactNode }) => {children}; describe('useCursor', () => { afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); }); it('preserves structured probe failures and refreshes live status queries', async () => { const probeFailure = { ok: false, stage: 'daemon', status: 503, duration_ms: 321, error_type: 'daemon_not_running', message: 'Cursor daemon is not running.', }; const fetchMock = vi.fn((input: RequestInfo | URL, init?: RequestInit) => { const url = String(input); if (url.endsWith('/api/cursor/status')) { return Promise.resolve( createJsonResponse({ enabled: true, authenticated: true, auth_method: 'manual', token_age: 1, token_expired: false, daemon_running: false, port: 20129, auto_start: false, ghost_mode: true, }) ); } if (url.endsWith('/api/cursor/settings')) { return Promise.resolve( createJsonResponse({ enabled: true, port: 20129, auto_start: false, ghost_mode: true, model: 'gpt-5.3-codex', }) ); } if (url.endsWith('/api/cursor/models')) { return Promise.resolve( createJsonResponse({ models: [{ id: 'gpt-5.3-codex', name: 'GPT-5.3 Codex', provider: 'openai' }], current: 'gpt-5.3-codex', }) ); } if (url.endsWith('/api/cursor/settings/raw')) { return Promise.resolve( createJsonResponse({ settings: {}, mtime: 100, path: '/tmp/cursor.settings.json', exists: true, }) ); } if (url.endsWith('/api/cursor/probe') && init?.method === 'POST') { return Promise.resolve(createJsonResponse(probeFailure, 503)); } return Promise.reject(new Error(`Unexpected fetch: ${url}`)); }); vi.stubGlobal('fetch', fetchMock); const { result } = renderHook(() => useCursor(), { wrapper }); await waitFor(() => expect(result.current.status?.enabled).toBe(true)); await waitFor(() => expect(result.current.models.length).toBe(1)); await act(async () => { const probeResult = await result.current.runProbeAsync(); expect(probeResult).toMatchObject(probeFailure); }); await waitFor(() => expect(result.current.probeResult).toMatchObject(probeFailure)); await waitFor(() => expect( fetchMock.mock.calls.filter(([input]) => String(input).endsWith('/api/cursor/status')) .length ).toBeGreaterThanOrEqual(2) ); await waitFor(() => expect( fetchMock.mock.calls.filter(([input]) => String(input).endsWith('/api/cursor/models')) .length ).toBeGreaterThanOrEqual(2) ); }); });