import { describe, it, expect, vi, beforeEach } from 'vitest'; import { AxiosError } from 'axios'; import { getSettings, getDefaultSettings, updateSettings } from './settingsService'; import { apiClient } from '@/services/api/client'; import { UserSettings, UpdateSettingsRequest } from '../types/settings'; // Mock apiClient vi.mock('@/services/api/client', () => ({ apiClient: { get: vi.fn(), put: vi.fn(), }, })); describe('settingsService', () => { const mockSettings: UserSettings = { notifications: { email_notifications: true, push_notifications: true, browser_notifications: true, email_on_follow: true, email_on_like: true, email_on_comment: true, email_on_message: true, email_on_mention: true, email_marketing: false, }, privacy: { allow_search_indexing: true, show_activity: true, }, content: { explicit_content: false, autoplay: true, }, preferences: { language: 'en', timezone: 'UTC', theme: 'auto', }, }; beforeEach(() => { vi.clearAllMocks(); }); describe('getSettings', () => { it('should return settings successfully', async () => { vi.mocked(apiClient.get).mockResolvedValue({ data: mockSettings, status: 200, statusText: 'OK', headers: {}, config: {} as any, }); const result = await getSettings(1); expect(result).toEqual(mockSettings); expect(apiClient.get).toHaveBeenCalledWith('/users/1/settings'); }); it('should throw error on 401 Unauthorized', async () => { const error = new AxiosError('Unauthorized'); error.response = { status: 401, data: {}, statusText: 'Unauthorized', headers: {}, config: {} as any, }; vi.mocked(apiClient.get).mockRejectedValue(error); await expect(getSettings(1)).rejects.toThrow( 'Unauthorized: Please log in to access settings', ); }); it('should throw error on 403 Forbidden', async () => { const error = new AxiosError('Forbidden'); error.response = { status: 403, data: {}, statusText: 'Forbidden', headers: {}, config: {} as any, }; vi.mocked(apiClient.get).mockRejectedValue(error); await expect(getSettings(1)).rejects.toThrow( 'Forbidden: You cannot access these settings', ); }); it('should return default settings on 404 Not Found', async () => { const error = new AxiosError('Not Found'); error.response = { status: 404, data: {}, statusText: 'Not Found', headers: {}, config: {} as any, }; vi.mocked(apiClient.get).mockRejectedValue(error); const result = await getSettings('1'); const defaults = getDefaultSettings(); expect(result.preferences.language).toBe(defaults.preferences.language); expect(result.preferences.theme).toBe(defaults.preferences.theme); expect(result.notifications).toEqual(defaults.notifications); expect(result.privacy).toEqual(defaults.privacy); expect(result.content).toEqual(defaults.content); }); it('should throw error with custom message from response', async () => { const error = new AxiosError('Error'); error.response = { status: 500, data: { error: 'Internal server error' }, statusText: 'Internal Server Error', headers: {}, config: {} as any, }; vi.mocked(apiClient.get).mockRejectedValue(error); await expect(getSettings(1)).rejects.toThrow('Internal server error'); }); it('should throw generic error on unknown error', async () => { const error = new Error('Network error'); vi.mocked(apiClient.get).mockRejectedValue(error); await expect(getSettings(1)).rejects.toThrow(error); }); }); describe('updateSettings', () => { const updateRequest: UpdateSettingsRequest = { preferences: { language: 'fr', timezone: 'Europe/Paris', theme: 'dark', }, }; it('should update settings successfully', async () => { vi.mocked(apiClient.put).mockResolvedValue({ data: {}, status: 200, statusText: 'OK', headers: {}, config: {} as any, }); await updateSettings(1, updateRequest); expect(apiClient.put).toHaveBeenCalledWith( '/users/settings', updateRequest, ); }); it('should throw error on 400 Bad Request', async () => { const error = new AxiosError('Bad Request'); error.response = { status: 400, data: { error: 'Invalid settings data' }, statusText: 'Bad Request', headers: {}, config: {} as any, }; vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow( 'Invalid settings data', ); }); it('should throw error on 401 Unauthorized', async () => { const error = new AxiosError('Unauthorized'); error.response = { status: 401, data: {}, statusText: 'Unauthorized', headers: {}, config: {} as any, }; vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow( 'Unauthorized: Please log in to update settings', ); }); it('should throw error on 403 Forbidden', async () => { const error = new AxiosError('Forbidden'); error.response = { status: 403, data: {}, statusText: 'Forbidden', headers: {}, config: {} as any, }; vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow( 'Forbidden: You cannot update these settings', ); }); it('should throw error on 404 Not Found', async () => { const error = new AxiosError('Not Found'); error.response = { status: 404, data: {}, statusText: 'Not Found', headers: {}, config: {} as any, }; vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow( 'Settings not found', ); }); it('should throw error with custom message from response', async () => { const error = new AxiosError('Error'); error.response = { status: 500, data: { error: 'Internal server error' }, statusText: 'Internal Server Error', headers: {}, config: {} as any, }; vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow( 'Internal server error', ); }); it('should throw generic error on unknown error', async () => { const error = new Error('Network error'); vi.mocked(apiClient.put).mockRejectedValue(error); await expect(updateSettings(1, updateRequest)).rejects.toThrow(error); }); it('should update only notifications', async () => { const notificationsOnly: UpdateSettingsRequest = { notifications: { email_notifications: false, }, }; vi.mocked(apiClient.put).mockResolvedValue({ data: {}, status: 200, statusText: 'OK', headers: {}, config: {} as any, }); await updateSettings(1, notificationsOnly); expect(apiClient.put).toHaveBeenCalledWith( '/users/settings', notificationsOnly, ); }); it('should update only privacy', async () => { const privacyOnly: UpdateSettingsRequest = { privacy: { allow_search_indexing: false, }, }; vi.mocked(apiClient.put).mockResolvedValue({ data: {}, status: 200, statusText: 'OK', headers: {}, config: {} as any, }); await updateSettings(1, privacyOnly); expect(apiClient.put).toHaveBeenCalledWith( '/users/settings', privacyOnly, ); }); it('should update only content', async () => { const contentOnly: UpdateSettingsRequest = { content: { explicit_content: true, }, }; vi.mocked(apiClient.put).mockResolvedValue({ data: {}, status: 200, statusText: 'OK', headers: {}, config: {} as any, }); await updateSettings(1, contentOnly); expect(apiClient.put).toHaveBeenCalledWith( '/users/settings', contentOnly, ); }); }); });