import { QueryClient, QueryClientProvider } from '@tanstack/react-query' import { renderHook, waitFor } from '@testing-library/react' import { http, HttpResponse } from 'msw' import { setupServer } from 'msw/node' import React from 'react' import { MessagePlugin } from 'tdesign-react' import { useStartupSettings, useSaveStartupSettings } from '@/hooks/useSettings' import type { StartupSettings } from '@/types' vi.mock('tdesign-react', () => ({ MessagePlugin: { success: vi.fn(), error: vi.fn(), }, })) const mockDesktopSettings: StartupSettings = { mode: 'desktop', editable: true, configPath: '/home/user/.nex/config.yaml', restartRequired: true, config: { server: { port: 9826, readTimeout: '30s', writeTimeout: '30s' }, database: { driver: 'sqlite', path: '/home/user/.nex/config.db', host: '', port: 3306, user: '', password: '', dbname: 'nex', maxIdleConns: 10, maxOpenConns: 100, connMaxLifetime: '1h', }, log: { level: 'info', path: '/home/user/.nex/log', maxSize: 100, maxBackups: 10, maxAge: 30, compress: true }, }, } const handlers = [ http.get('/api/settings/startup', () => { return HttpResponse.json(mockDesktopSettings) }), http.put('/api/settings/startup', async ({ request }) => { const body = (await request.json()) as Record return HttpResponse.json({ ...mockDesktopSettings, config: (body as Record).config, }) }), ] const server = setupServer(...handlers) beforeAll(() => server.listen({ onUnhandledRequest: 'bypass' })) afterEach(() => server.resetHandlers()) afterAll(() => server.close()) function createWrapper() { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false } } }) return ({ children }: { children: React.ReactNode }) => ( {children} ) } describe('useStartupSettings', () => { it('fetches startup settings', async () => { const { result } = renderHook(() => useStartupSettings(), { wrapper: createWrapper() }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(result.current.data?.mode).toBe('desktop') expect(result.current.data?.editable).toBe(true) expect(result.current.data?.configPath).toBe('/home/user/.nex/config.yaml') expect(result.current.data?.restartRequired).toBe(true) expect(result.current.data?.config.server.port).toBe(9826) expect(result.current.data?.config.database.driver).toBe('sqlite') expect(result.current.data?.config.log.level).toBe('info') }) }) describe('useSaveStartupSettings', () => { it('saves settings and shows success message for desktop', async () => { const { result } = renderHook(() => useSaveStartupSettings(), { wrapper: createWrapper() }) result.current.mutate({ config: mockDesktopSettings.config }) await waitFor(() => expect(result.current.isSuccess).toBe(true)) expect(MessagePlugin.success).toHaveBeenCalledWith( '配置已保存到配置文件。当前运行中的服务仍使用启动时配置,重启 Desktop 后生效' ) }) it('shows error message on failure', async () => { server.use( http.put('/api/settings/startup', () => { return HttpResponse.json({ error: '保存失败' }, { status: 500 }) }) ) const { result } = renderHook(() => useSaveStartupSettings(), { wrapper: createWrapper() }) result.current.mutate({ config: mockDesktopSettings.config }) await waitFor(() => expect(result.current.isError).toBe(true)) expect(MessagePlugin.error).toHaveBeenCalled() }) })