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>
149 lines
4.2 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|