import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'; import { renderHook, act } from '@testing-library/react'; const apiFetchMock = vi.fn(); vi.mock('@/lib/api', () => ({ apiFetch: (...args: unknown[]) => apiFetchMock(...args), })); const useLicenseMock = vi.fn(); vi.mock('@/context/LicenseContext', () => ({ useLicense: () => useLicenseMock(), })); const useExperimentalMock = vi.fn(() => ({ experimental: true, experimentalReady: true })); vi.mock('@/hooks/useExperimental', () => ({ useExperimental: () => useExperimentalMock(), })); vi.mock('@/lib/utils', async () => { const actual = await vi.importActual('@/lib/utils'); return { ...actual, visibilityInterval: () => () => {}, }; }); import { useMeshDataPlane } from '../useMeshDataPlane'; function okJson(payload: unknown): Response { return new Response(JSON.stringify(payload), { status: 200, headers: { 'Content-Type': 'application/json' }, }); } function statusJson(status: number, payload: unknown = {}): Response { return new Response(JSON.stringify(payload), { status, headers: { 'Content-Type': 'application/json' }, }); } beforeEach(() => { apiFetchMock.mockReset(); useLicenseMock.mockReset(); useExperimentalMock.mockReset(); useExperimentalMock.mockReturnValue({ experimental: true, experimentalReady: true }); }); afterEach(() => { vi.clearAllMocks(); }); describe('useMeshDataPlane', () => { it('does not fetch /mesh/status when the session is on the free tier', async () => { useLicenseMock.mockReturnValue({ isPaid: false }); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(apiFetchMock).not.toHaveBeenCalled(); expect(result.current.status).toBeNull(); expect(result.current.loading).toBe(false); }); it('fetches once on mount and surfaces the localDataPlane payload for a paid tier', async () => { useLicenseMock.mockReturnValue({ isPaid: true }); apiFetchMock.mockResolvedValue(okJson({ localDataPlane: { ok: true, reason: null, lastChecked: 1000 }, })); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(apiFetchMock).toHaveBeenCalledWith('/mesh/status', expect.objectContaining({ localOnly: true })); expect(result.current.status).toEqual({ ok: true, reason: null, lastChecked: 1000 }); expect(result.current.loading).toBe(false); }); it('keeps status null on a 403 response without raising an error', async () => { useLicenseMock.mockReturnValue({ isPaid: true }); apiFetchMock.mockResolvedValue(statusJson(403, { error: 'forbidden' })); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(result.current.status).toBeNull(); expect(result.current.loading).toBe(false); }); it('falls back to null when the response omits localDataPlane', async () => { useLicenseMock.mockReturnValue({ isPaid: true }); apiFetchMock.mockResolvedValue(okJson({ nodes: [] })); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(result.current.status).toBeNull(); expect(result.current.loading).toBe(false); }); it('does not fetch when paid but experimental discovery is off', async () => { useLicenseMock.mockReturnValue({ isPaid: true }); useExperimentalMock.mockReturnValue({ experimental: false, experimentalReady: true }); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(apiFetchMock).not.toHaveBeenCalled(); expect(result.current.status).toBeNull(); expect(result.current.loading).toBe(false); }); it('does not fetch while experimental metadata is still loading', async () => { useLicenseMock.mockReturnValue({ isPaid: true }); useExperimentalMock.mockReturnValue({ experimental: false, experimentalReady: false }); const { result } = renderHook(() => useMeshDataPlane()); await act(async () => { await Promise.resolve(); await Promise.resolve(); }); expect(apiFetchMock).not.toHaveBeenCalled(); expect(result.current.status).toBeNull(); }); });