veza/apps/web/src/services/2fa-service.test.ts
senke 2efaa1432b test: fix and improve unit tests across multiple features
Fix mocking issues, add missing test cases, and align tests with
current component APIs for analytics, chat, marketplace, player,
playlists, settings, tracks, and auth features.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 23:34:42 +01:00

149 lines
4.2 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { AxiosError } from 'axios';
import { twoFactorService } from './2fa-service';
import { apiClient } from './api/client';
import { requireFeature } from '@/config/features';
// Mock apiClient
vi.mock('./api/client', () => ({
apiClient: {
get: vi.fn(),
post: vi.fn(),
},
}));
// Mock feature config
vi.mock('@/config/features', () => ({
requireFeature: vi.fn(),
FEATURES: {
TWO_FACTOR_AUTH: true,
},
}));
const mockedApiClient = apiClient as {
get: ReturnType<typeof vi.fn>;
post: ReturnType<typeof vi.fn>;
};
describe('twoFactorService', () => {
beforeEach(() => {
vi.clearAllMocks();
});
describe('getStatus', () => {
it('should get 2FA status successfully', async () => {
const mockStatus = { enabled: true };
mockedApiClient.get.mockResolvedValue({
data: mockStatus,
});
const result = await twoFactorService.getStatus();
expect(result).toEqual(mockStatus);
expect(requireFeature).toHaveBeenCalledWith('TWO_FACTOR_AUTH');
expect(mockedApiClient.get).toHaveBeenCalledWith('/auth/2fa/status', { _disableToast: true });
});
it('should throw error on status fetch failure', async () => {
const mockError = new AxiosError('Status fetch failed');
mockError.response = {
status: 500,
data: { error: 'Internal server error' },
} as any;
mockedApiClient.get.mockRejectedValue(mockError);
await expect(twoFactorService.getStatus()).rejects.toThrow();
});
});
describe('setup', () => {
it('should setup 2FA successfully', async () => {
const mockSetupResponse = {
secret: 'JBSWY3DPEHPK3PXP',
qr_code_url: 'https://example.com/qr.png',
recovery_codes: ['code1', 'code2', 'code3'],
};
mockedApiClient.post.mockResolvedValue({
data: mockSetupResponse,
});
const result = await twoFactorService.setup();
expect(result).toEqual(mockSetupResponse);
expect(requireFeature).toHaveBeenCalledWith('TWO_FACTOR_AUTH');
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/2fa/setup');
});
it('should throw error on setup failure', async () => {
const mockError = new AxiosError('Setup failed');
mockError.response = {
status: 400,
data: { error: '2FA already enabled' },
} as any;
mockedApiClient.post.mockRejectedValue(mockError);
await expect(twoFactorService.setup()).rejects.toThrow();
});
});
describe('verify', () => {
it('should verify 2FA code successfully', async () => {
mockedApiClient.post.mockResolvedValue({
data: { message: '2FA enabled successfully' },
});
await twoFactorService.verify('JBSWY3DPEHPK3PXP', '123456');
expect(requireFeature).toHaveBeenCalledWith('TWO_FACTOR_AUTH');
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/2fa/verify', {
secret: 'JBSWY3DPEHPK3PXP',
code: '123456',
});
});
it('should throw error on verification failure', async () => {
const mockError = new AxiosError('Verification failed');
mockError.response = {
status: 400,
data: { error: 'Invalid code' },
} as any;
mockedApiClient.post.mockRejectedValue(mockError);
await expect(
twoFactorService.verify('JBSWY3DPEHPK3PXP', 'invalid'),
).rejects.toThrow();
});
});
describe('disable', () => {
it('should disable 2FA successfully', async () => {
mockedApiClient.post.mockResolvedValue({
data: { message: '2FA disabled successfully' },
});
await twoFactorService.disable('password123');
expect(requireFeature).toHaveBeenCalledWith('TWO_FACTOR_AUTH');
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/2fa/disable', {
password: 'password123',
});
});
it('should throw error on disable failure', async () => {
const mockError = new AxiosError('Disable failed');
mockError.response = {
status: 401,
data: { error: 'Invalid password' },
} as any;
mockedApiClient.post.mockRejectedValue(mockError);
await expect(twoFactorService.disable('wrongpassword')).rejects.toThrow();
});
});
});