/** * @jest-environment jsdom */ import { useAuthStore, initAuthStore } from '../stores/authStore'; import api from '../lib/api'; // Mock the API module jest.mock('../lib/api', () => ({ __esModule: true, default: { checkEmail: jest.fn(), requestOTP: jest.fn(), verifyOTP: jest.fn(), logout: jest.fn(), isAuthenticated: jest.fn(), getStoredUser: jest.fn(), getToken: jest.fn(), }, setOnUnauthorizedCallback: jest.fn(), })); describe('Auth Store', () => { beforeEach(() => { // Reset store to initial state useAuthStore.setState({ user: null, isLoading: false, isInitializing: true, isAuthenticated: false, error: null, }); // Clear all mocks jest.clearAllMocks(); // Clear cookies document.cookie = 'accessToken=; path=/; max-age=0'; }); describe('Initial State', () => { it('should have correct initial state', () => { const state = useAuthStore.getState(); expect(state.user).toBeNull(); expect(state.isLoading).toBe(false); expect(state.isInitializing).toBe(true); expect(state.isAuthenticated).toBe(false); expect(state.error).toBeNull(); }); }); describe('checkEmail', () => { it('should successfully check if email exists', async () => { (api.checkEmail as jest.Mock).mockResolvedValue({ ok: true, data: { exists: true, name: 'John Doe' }, }); const result = await useAuthStore.getState().checkEmail('test@example.com'); expect(result).toEqual({ exists: true, name: 'John Doe' }); expect(api.checkEmail).toHaveBeenCalledWith('test@example.com'); expect(useAuthStore.getState().isLoading).toBe(false); expect(useAuthStore.getState().error).toBeNull(); }); it('should handle email check failure', async () => { (api.checkEmail as jest.Mock).mockResolvedValue({ ok: false, error: { message: 'Network error' }, }); const result = await useAuthStore.getState().checkEmail('test@example.com'); expect(result).toEqual({ exists: false }); expect(useAuthStore.getState().isLoading).toBe(false); }); it('should handle network error during email check', async () => { (api.checkEmail as jest.Mock).mockRejectedValue(new Error('Network error')); const result = await useAuthStore.getState().checkEmail('test@example.com'); expect(result).toEqual({ exists: false }); expect(useAuthStore.getState().error).toBe('Network error. Please check your connection.'); }); }); describe('requestOtp', () => { it('should successfully request OTP', async () => { (api.requestOTP as jest.Mock).mockResolvedValue({ ok: true, data: { message: 'OTP sent' }, }); const result = await useAuthStore.getState().requestOtp('test@example.com'); expect(result).toEqual({ success: true }); expect(api.requestOTP).toHaveBeenCalledWith('test@example.com'); expect(useAuthStore.getState().isLoading).toBe(false); expect(useAuthStore.getState().error).toBeNull(); }); it('should handle OTP request failure', async () => { (api.requestOTP as jest.Mock).mockResolvedValue({ ok: false, error: { message: 'Failed to send OTP' }, }); const result = await useAuthStore.getState().requestOtp('test@example.com'); expect(result).toEqual({ success: false }); expect(useAuthStore.getState().error).toBe('Failed to send verification code. Please try again.'); }); }); describe('verifyOtp', () => { it('should successfully verify OTP and authenticate user', async () => { (api.verifyOTP as jest.Mock).mockResolvedValue({ ok: true, data: { token: 'mock-jwt-token', user: { id: '123', email: 'test@example.com', first_name: 'John', last_name: 'Doe', }, }, }); const result = await useAuthStore.getState().verifyOtp('test@example.com', '123456'); expect(result).toBe(true); expect(api.verifyOTP).toHaveBeenCalledWith('test@example.com', '123456'); const state = useAuthStore.getState(); expect(state.isAuthenticated).toBe(true); expect(state.user).toEqual({ user_id: 123, email: 'test@example.com', firstName: 'John', lastName: 'Doe', max_role: 'USER', privileges: '', }); expect(state.error).toBeNull(); expect(state.isLoading).toBe(false); // Check that cookie was set expect(document.cookie).toContain('accessToken=mock-jwt-token'); }); it('should handle invalid OTP code', async () => { (api.verifyOTP as jest.Mock).mockResolvedValue({ ok: false, error: { message: 'Invalid code' }, }); const result = await useAuthStore.getState().verifyOtp('test@example.com', '000000'); expect(result).toBe(false); expect(useAuthStore.getState().error).toBe('Invalid verification code. Please try again.'); expect(useAuthStore.getState().isAuthenticated).toBe(false); }); it('should handle network error during OTP verification', async () => { (api.verifyOTP as jest.Mock).mockRejectedValue(new Error('Network error')); const result = await useAuthStore.getState().verifyOtp('test@example.com', '123456'); expect(result).toBe(false); expect(useAuthStore.getState().error).toBe('Network error'); }); }); describe('logout', () => { it('should successfully logout user', async () => { // Set up authenticated state useAuthStore.setState({ user: { user_id: 123, email: 'test@example.com' }, isAuthenticated: true, }); // Set cookie document.cookie = 'accessToken=test-token; path=/'; (api.logout as jest.Mock).mockResolvedValue(undefined); // Mock window.location.href delete (window as any).location; (window as any).location = { href: '' }; await useAuthStore.getState().logout(); expect(api.logout).toHaveBeenCalled(); const state = useAuthStore.getState(); expect(state.user).toBeNull(); expect(state.isAuthenticated).toBe(false); expect(state.error).toBeNull(); // Check that cookie was cleared expect(document.cookie).not.toContain('accessToken=test-token'); }); }); describe('_checkAuth', () => { it('should set authenticated state when user is logged in', async () => { (api.isAuthenticated as jest.Mock).mockResolvedValue(true); (api.getStoredUser as jest.Mock).mockResolvedValue({ user_id: 123, email: 'test@example.com', firstName: 'John', }); (api.getToken as jest.Mock).mockResolvedValue('mock-token'); await useAuthStore.getState()._checkAuth(); const state = useAuthStore.getState(); expect(state.isAuthenticated).toBe(true); expect(state.user).toEqual({ user_id: 123, email: 'test@example.com', firstName: 'John', }); expect(state.isInitializing).toBe(false); expect(document.cookie).toContain('accessToken=mock-token'); }); it('should set unauthenticated state when user is not logged in', async () => { (api.isAuthenticated as jest.Mock).mockResolvedValue(false); await useAuthStore.getState()._checkAuth(); const state = useAuthStore.getState(); expect(state.isAuthenticated).toBe(false); expect(state.user).toBeNull(); expect(state.isInitializing).toBe(false); expect(document.cookie).not.toContain('accessToken'); }); it('should handle errors during auth check', async () => { (api.isAuthenticated as jest.Mock).mockRejectedValue(new Error('Auth check failed')); await useAuthStore.getState()._checkAuth(); const state = useAuthStore.getState(); expect(state.isAuthenticated).toBe(false); expect(state.user).toBeNull(); expect(state.error).toBe('Failed to check authentication'); }); }); describe('clearError', () => { it('should clear error state', () => { useAuthStore.setState({ error: 'Some error' }); useAuthStore.getState().clearError(); expect(useAuthStore.getState().error).toBeNull(); }); }); describe('refreshAuth', () => { it('should call _checkAuth', async () => { (api.isAuthenticated as jest.Mock).mockResolvedValue(false); await useAuthStore.getState().refreshAuth(); expect(api.isAuthenticated).toHaveBeenCalled(); }); }); describe('updateUser', () => { it('should update user profile in state', () => { useAuthStore.setState({ user: { user_id: 123, email: 'test@example.com', firstName: 'John', lastName: 'Doe', }, }); useAuthStore.getState().updateUser({ firstName: 'Jane' }); expect(useAuthStore.getState().user).toEqual({ user_id: 123, email: 'test@example.com', firstName: 'Jane', lastName: 'Doe', }); }); it('should do nothing if user is null', () => { useAuthStore.setState({ user: null }); useAuthStore.getState().updateUser({ firstName: 'Jane' }); expect(useAuthStore.getState().user).toBeNull(); }); }); describe('initAuthStore', () => { it('should initialize store and set callbacks', async () => { (api.isAuthenticated as jest.Mock).mockResolvedValue(false); initAuthStore(); // Wait for async _checkAuth to complete await new Promise(resolve => setTimeout(resolve, 100)); expect(api.isAuthenticated).toHaveBeenCalled(); }); }); });