import { act, renderHook } from '@testing-library/react' import { deleteItem, setItem } from '../core/storage' import { HookError } from '../hooks/types' import { useSecret } from '../hooks/useSecret' import { useSecretItem } from '../hooks/useSecretItem' jest.mock('../hooks/useSecretItem') jest.mock('../core/storage', () => ({ ...jest.requireActual('../core/storage'), setItem: jest.fn(), deleteItem: jest.fn(), })) const mockedUseSecretItem = useSecretItem as jest.MockedFunction< typeof useSecretItem > const mockedSetItem = setItem as jest.MockedFunction const mockedDeleteItem = deleteItem as jest.MockedFunction describe('useSecret', () => { const baseResult = { data: null, error: null, isLoading: false, isPending: false, } beforeEach(() => { mockedSetItem.mockReset() mockedDeleteItem.mockReset() mockedUseSecretItem.mockReset() }) it('proxys data from useSecretItem', () => { const refetch = jest.fn().mockResolvedValue(undefined) mockedUseSecretItem.mockReturnValueOnce({ ...baseResult, data: { key: 'token', service: 'auth', metadata: {} as any }, refetch, }) const { result } = renderHook(() => useSecret('token', { service: 'auth', includeValue: true }) ) expect(result.current.data?.key).toBe('token') expect(result.current.refetch).toBe(refetch) }) it('saves secrets and triggers refetch', async () => { const refetch = jest.fn().mockResolvedValue(undefined) mockedUseSecretItem.mockReturnValue({ ...baseResult, refetch }) mockedSetItem.mockResolvedValue({ metadata: {} as any }) const { result } = renderHook(() => useSecret('token', { service: 'auth', includeValue: true, skip: true, }) ) await act(async () => { const outcome = await result.current.saveSecret('secret') expect(outcome).toEqual({ success: true }) }) expect(mockedSetItem).toHaveBeenCalledWith('token', 'secret', { service: 'auth', }) expect(refetch).toHaveBeenCalledTimes(1) }) it('wraps save errors as HookError', async () => { const refetch = jest.fn() mockedUseSecretItem.mockReturnValue({ ...baseResult, refetch }) mockedSetItem.mockRejectedValueOnce(new Error('save failed')) const { result } = renderHook(() => useSecret('token', { service: 'auth', includeValue: true }) ) let response: | { success: boolean; error?: HookError | undefined } | undefined await act(async () => { response = await result.current.saveSecret('secret') }) expect(response).toEqual({ success: false, error: expect.any(HookError) }) expect(refetch).not.toHaveBeenCalled() }) it('deletes secrets and triggers refetch', async () => { const refetch = jest.fn().mockResolvedValue(undefined) mockedUseSecretItem.mockReturnValue({ ...baseResult, refetch }) mockedDeleteItem.mockResolvedValue(true) const { result } = renderHook(() => useSecret('token', { service: 'auth', includeValue: true, }) ) await act(async () => { const outcome = await result.current.deleteSecret() expect(outcome).toEqual({ success: true }) }) expect(mockedDeleteItem).toHaveBeenCalledWith('token', { service: 'auth' }) expect(refetch).toHaveBeenCalledTimes(1) }) it('wraps delete errors as HookError', async () => { const refetch = jest.fn() mockedUseSecretItem.mockReturnValue({ ...baseResult, refetch }) mockedDeleteItem.mockRejectedValueOnce(new Error('delete failed')) const { result } = renderHook(() => useSecret('token', { service: 'auth' })) let response: | { success: boolean; error?: HookError | undefined } | undefined await act(async () => { response = await result.current.deleteSecret() }) expect(response).toEqual({ success: false, error: expect.any(HookError) }) expect(refetch).not.toHaveBeenCalled() }) })