import { describe, it, expect, beforeEach, vi } from 'vitest'; vi.mock('../../resources/js/api/client', () => ({ apiClient: { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn(), }, ensureCsrfCookie: vi.fn(), })); import { login, register, me, logout, verifyTwoFactor, useRecoveryCode, twoFactorInit, twoFactorConfirm, twoFactorDisable, twoFactorRegenerateRecoveryCodes, forgotPassword, resetPassword, updateNotificationPreferences, } from '../../resources/js/api/auth'; import { apiClient, ensureCsrfCookie } from '../../resources/js/api/client'; const FAKE_USER = { id: 1, email: 'demo@x.ru', first_name: 'D', last_name: 'U', tenant_id: 1, totp_enabled: false, last_login_at: null, }; describe('api/auth', () => { beforeEach(() => vi.clearAllMocks()); it('login() POSTs /api/auth/login + calls ensureCsrfCookie + unwraps data', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { user: FAKE_USER, requires_2fa: false } }); const result = await login({ email: 'a@x.ru', password: 'pw' }); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/login', { email: 'a@x.ru', password: 'pw' }); expect(result.requires_2fa).toBe(false); expect(result.user.email).toBe('demo@x.ru'); }); it('register() POSTs /api/auth/register с accept-флагами', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { user: FAKE_USER, requires_2fa: false } }); await register({ email: 'a@x.ru', password: 'pw', accept_offer: true, accept_pdn: true }); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/register', { email: 'a@x.ru', password: 'pw', accept_offer: true, accept_pdn: true, }); }); it('me() GETs /api/auth/me + unwraps data.user', async () => { vi.mocked(apiClient.get).mockResolvedValue({ data: { user: FAKE_USER } }); const result = await me(); expect(apiClient.get).toHaveBeenCalledWith('/api/auth/me'); expect(result.id).toBe(1); }); it('logout() POSTs /api/auth/logout + ensureCsrfCookie', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: {} }); await logout(); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/logout'); }); it('verifyTwoFactor() POSTs /api/auth/2fa/verify с code', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { user: FAKE_USER, requires_2fa: false } }); await verifyTwoFactor('123456'); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/2fa/verify', { code: '123456' }); }); it('useRecoveryCode() POSTs /api/auth/2fa/recovery-use с code + возвращает remaining', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { user: FAKE_USER, requires_2fa: false, recovery_codes_remaining: 7 }, }); const r = await useRecoveryCode('xxxx-yyyy'); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/2fa/recovery-use', { code: 'xxxx-yyyy' }); expect(r.recovery_codes_remaining).toBe(7); }); it('twoFactorInit() POSTs /api/2fa/init + возвращает secret+qr_url', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { secret: 'JBSW', qr_url: 'otpauth://...' } }); const r = await twoFactorInit(); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.post).toHaveBeenCalledWith('/api/2fa/init'); expect(r.secret).toBe('JBSW'); }); it('twoFactorConfirm() POSTs /api/2fa/confirm с code', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { recovery_codes: ['a', 'b'], message: 'ok' } }); await twoFactorConfirm('000000'); expect(apiClient.post).toHaveBeenCalledWith('/api/2fa/confirm', { code: '000000' }); }); it('twoFactorDisable() POSTs /api/2fa/disable с паролем', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { message: 'disabled' } }); await twoFactorDisable('current-pw'); expect(apiClient.post).toHaveBeenCalledWith('/api/2fa/disable', { password: 'current-pw' }); }); it('twoFactorRegenerateRecoveryCodes() POSTs /api/2fa/regenerate-recovery-codes', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { recovery_codes: ['x', 'y', 'z'], message: 'ok' } }); const r = await twoFactorRegenerateRecoveryCodes('pw'); expect(apiClient.post).toHaveBeenCalledWith('/api/2fa/regenerate-recovery-codes', { password: 'pw' }); expect(r.recovery_codes.length).toBe(3); }); it('forgotPassword() POSTs /api/auth/forgot с email', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { message: 'sent' } }); await forgotPassword('a@x.ru'); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/forgot', { email: 'a@x.ru' }); }); it('resetPassword() POSTs /api/auth/reset-password с token+email+password', async () => { vi.mocked(apiClient.post).mockResolvedValue({ data: { message: 'reset' } }); const payload = { token: 't', email: 'a@x.ru', password: 'newpw', password_confirmation: 'newpw' }; await resetPassword(payload); expect(apiClient.post).toHaveBeenCalledWith('/api/auth/reset-password', payload); }); it('updateNotificationPreferences() PATCH /api/auth/me/notification-preferences', async () => { vi.mocked(apiClient.patch).mockResolvedValue({ data: { user: FAKE_USER } }); const payload = { prefs: { new_lead: { inapp: true } }, sound_enabled: false }; await updateNotificationPreferences(payload); expect(ensureCsrfCookie).toHaveBeenCalledOnce(); expect(apiClient.patch).toHaveBeenCalledWith('/api/auth/me/notification-preferences', payload); }); });