import { vi, describe, it, expect, beforeEach } from 'vitest'; // Мокаем только apiRequest — классы ошибок (BadRequestError/ConflictError) // оставляем настоящими, чтобы сервис распознавал их через instanceof. const mockApiRequest = vi.hoisted(() => vi.fn()); vi.mock('@/lib/queryClient', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, apiRequest: mockApiRequest, }; }); import { authService } from '../client/src/services/auth.service'; import { BadRequestError, ConflictError } from '@/lib/queryClient'; const PASSWORD_RULE_ERROR = 'Пароль должен содержать заглавные и строчные буквы, цифры и специальные символы'; const NETWORK_FALLBACK = 'Не удалось изменить пароль. Проверьте соединение и попробуйте ещё раз'; function okResponse(body: unknown) { return { json: async () => body } as Response; } describe('authService.changeUserPassword', () => { beforeEach(() => { vi.clearAllMocks(); }); it('при 400 с текстом правила возвращает { success: false, error } без throw', async () => { mockApiRequest.mockRejectedValue(new BadRequestError({ success: false, error: PASSWORD_RULE_ERROR })); const result = await authService.changeUserPassword(1, 'weak'); expect(result).toEqual({ success: false, error: PASSWORD_RULE_ERROR }); expect(mockApiRequest).toHaveBeenCalledWith('PUT', '/api/users/1/password', { password: 'weak' }); }); it('при 200 возвращает ответ сервера как есть', async () => { mockApiRequest.mockResolvedValue(okResponse({ success: true, message: 'Пароль пользователя изменен' })); const result = await authService.changeUserPassword(1, 'Strong1!'); expect(result).toEqual({ success: true, message: 'Пароль пользователя изменен' }); }); it('при сетевой ошибке возвращает общий текст', async () => { mockApiRequest.mockRejectedValue(new TypeError('Failed to fetch')); const result = await authService.changeUserPassword(1, 'Strong1!'); expect(result).toEqual({ success: false, error: NETWORK_FALLBACK }); }); it('при 400 без тела ответа возвращает общий текст, а не технический bad_request', async () => { mockApiRequest.mockRejectedValue(new BadRequestError(undefined)); const result = await authService.changeUserPassword(1, 'Strong1!'); expect(result).toEqual({ success: false, error: NETWORK_FALLBACK }); }); }); describe('authService.changePassword', () => { beforeEach(() => { vi.clearAllMocks(); }); it('при 400 с текстом правила возвращает { success: false, error } без throw', async () => { mockApiRequest.mockRejectedValue(new BadRequestError({ success: false, error: PASSWORD_RULE_ERROR })); const result = await authService.changePassword('Old1!aaaa', 'weak'); expect(result).toEqual({ success: false, error: PASSWORD_RULE_ERROR }); expect(mockApiRequest).toHaveBeenCalledWith('POST', '/api/auth/change-password', { currentPassword: 'Old1!aaaa', newPassword: 'weak', }); }); it('при 200 возвращает успех', async () => { mockApiRequest.mockResolvedValue(okResponse({ success: true, message: 'Пароль успешно изменен' })); const result = await authService.changePassword('Old1!aaaa', 'New1!bbbb'); expect(result).toEqual({ success: true, message: 'Пароль успешно изменен' }); }); it('при сетевой ошибке возвращает общий текст', async () => { mockApiRequest.mockRejectedValue(new TypeError('Failed to fetch')); const result = await authService.changePassword('Old1!aaaa', 'New1!bbbb'); expect(result).toEqual({ success: false, error: NETWORK_FALLBACK }); }); }); describe('authService.createUser', () => { beforeEach(() => { vi.clearAllMocks(); }); const userData = { email: 'new@example.com', firstName: 'Иван', lastName: 'Петров', appRole: 'user', sendInvite: true, }; it('при 400 (дубликат email / слабый пароль) возвращает текст сервера без throw', async () => { mockApiRequest.mockRejectedValue(new BadRequestError({ success: false, error: 'Пользователь с таким email уже существует в организации' })); const result = await authService.createUser(userData); expect(result).toEqual({ success: false, error: 'Пользователь с таким email уже существует в организации' }); }); it('при 409 возвращает текст конфликта без throw', async () => { mockApiRequest.mockRejectedValue(new ConflictError({ success: false, error: 'Конфликт данных' })); const result = await authService.createUser(userData); expect(result).toEqual({ success: false, error: 'Конфликт данных' }); }); it('при 201 возвращает успех с пользователем', async () => { const body = { success: true, message: 'Пользователь создан', user: { id: 5, email: userData.email } }; mockApiRequest.mockResolvedValue(okResponse(body)); const result = await authService.createUser(userData); expect(result).toEqual(body); }); it('при сетевой ошибке возвращает общий текст', async () => { mockApiRequest.mockRejectedValue(new TypeError('Failed to fetch')); const result = await authService.createUser(userData); expect(result).toEqual({ success: false, error: 'Не удалось создать пользователя. Проверьте соединение и попробуйте ещё раз' }); }); });