import type { ReactNode } from 'react'; import { act, renderHook, waitFor } from '@testing-library/react'; import { QueryClientProvider } from '@tanstack/react-query'; import { afterEach, describe, expect, it, vi } from 'vitest'; import { useAllSettings } from '@/api/queries/useAllSettings'; import { keys } from '@/api/queryKeys'; import { makeTestQueryClient } from '@/test/test-utils'; import { HttpUtil, Msg } from '@/utils'; afterEach(() => { vi.restoreAllMocks(); }); describe('useAllSettings', () => { it('accepts legacy overlength regex settings without logging a response validation warning', async () => { const subJsonUserAgentRegex = 'x'.repeat(2_049); vi.spyOn(HttpUtil, 'post').mockResolvedValue(new Msg(true, '', { subJsonUserAgentRegex })); const warning = vi.spyOn(console, 'warn').mockImplementation(() => undefined); const queryClient = makeTestQueryClient(); const wrapper = ({ children }: { children: ReactNode }) => ( {children} ); const { result } = renderHook(() => useAllSettings(), { wrapper }); await waitFor(() => expect(result.current.fetched).toBe(true)); expect(result.current.allSetting.subJsonUserAgentRegex).toBe(subJsonUserAgentRegex); expect(warning).not.toHaveBeenCalled(); }); it('keeps an edited setting when a refetch returns older server data', async () => { const values = [ { webPort: 2053 }, { webPort: 2054 }, ]; let index = 0; vi.spyOn(HttpUtil, 'post').mockImplementation(async () => new Msg(true, '', values[index++])); const queryClient = makeTestQueryClient(); const wrapper = ({ children }: { children: ReactNode }) => ( {children} ); const { result } = renderHook(() => useAllSettings(), { wrapper }); await waitFor(() => expect(result.current.fetched).toBe(true)); act(() => result.current.updateSetting({ webPort: 3000 })); await queryClient.invalidateQueries({ queryKey: keys.settings.all() }); await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(2)); expect(result.current.allSetting.webPort).toBe(3000); expect(result.current.saveDisabled).toBe(false); }); it('hydrates redacted secrets after a successful save', async () => { let fetchCount = 0; vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => { if (url === '/panel/api/setting/all') { fetchCount += 1; return new Msg(true, '', fetchCount === 1 ? { hasTgBotToken: false } : { hasTgBotToken: true, tgBotToken: '' }); } return new Msg(true, ''); }); const queryClient = makeTestQueryClient(); const wrapper = ({ children }: { children: ReactNode }) => ( {children} ); const { result } = renderHook(() => useAllSettings(), { wrapper }); await waitFor(() => expect(result.current.fetched).toBe(true)); act(() => result.current.updateSetting({ tgBotToken: 'secret' })); await act(async () => { await result.current.saveAll(); }); await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(3)); expect(result.current.allSetting.tgBotToken).toBe(''); expect(result.current.allSetting.hasTgBotToken).toBe(true); expect(result.current.saveDisabled).toBe(true); }); it('establishes a saved baseline for a full-payload security save', async () => { let fetchCount = 0; vi.spyOn(HttpUtil, 'post').mockImplementation(async (url) => { if (url === '/panel/api/setting/all') { fetchCount += 1; return new Msg(true, '', fetchCount === 1 ? { hasTgBotToken: false } : { hasTgBotToken: true, tgBotToken: '' }); } return new Msg(true, ''); }); const queryClient = makeTestQueryClient(); const wrapper = ({ children }: { children: ReactNode }) => ( {children} ); const { result } = renderHook(() => useAllSettings(), { wrapper }); await waitFor(() => expect(result.current.fetched).toBe(true)); act(() => result.current.updateSetting({ tgBotToken: 'secret' })); await act(async () => { await result.current.savePayload({ ...result.current.allSetting, twoFactorEnable: false, twoFactorToken: '' }); }); await waitFor(() => expect(HttpUtil.post).toHaveBeenCalledTimes(3)); expect(result.current.allSetting.tgBotToken).toBe(''); expect(result.current.allSetting.hasTgBotToken).toBe(true); expect(result.current.saveDisabled).toBe(true); }); });