bacc7c5e24
Переработка register под новый бэкенд SP1 (код на почту), новый ConfirmEmailView, капча-шов, роут /confirm-email. Проверено Playwright: register→код→confirm→dashboard, негатив, fallback email. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
146 lines
6.3 KiB
TypeScript
146 lines
6.3 KiB
TypeScript
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-флагами + captcha (G1/SP3a)', async () => {
|
||
vi.mocked(apiClient.post).mockResolvedValue({
|
||
data: { status: 'pending_email_confirm', email: 'a@x.ru', expires_at: '2026-06-18T12:00:00+00:00' },
|
||
});
|
||
await register({ email: 'a@x.ru', password: 'pw', accept_offer: true, accept_pdn: true, captcha_token: 'tok' });
|
||
expect(ensureCsrfCookie).toHaveBeenCalledOnce();
|
||
expect(apiClient.post).toHaveBeenCalledWith('/api/auth/register', {
|
||
email: 'a@x.ru',
|
||
password: 'pw',
|
||
accept_offer: true,
|
||
accept_pdn: true,
|
||
captcha_token: 'tok',
|
||
});
|
||
});
|
||
|
||
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);
|
||
});
|
||
});
|