- apps/web: test updates (Vitest/setup), playbackAnalyticsService, TrackGrid, serviceErrorHandler - veza-common: logging, metrics, traits, validation, random - veza-stream-server: audio pipeline, codecs, cache, monitoring, routes - apps/web/dist_verification: refresh build assets (content-hashed filenames) Co-authored-by: Cursor <cursoragent@cursor.com>
338 lines
9 KiB
TypeScript
338 lines
9 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { AxiosError } from 'axios';
|
|
import {
|
|
login,
|
|
register,
|
|
logout,
|
|
refreshToken,
|
|
requestPasswordReset,
|
|
resetPassword,
|
|
verifyEmail,
|
|
resendVerificationEmail,
|
|
type AuthResponse,
|
|
} from './authService';
|
|
import { apiClient } from '@/services/api/client';
|
|
|
|
// Mock apiClient
|
|
vi.mock('@/services/api/client', () => ({
|
|
apiClient: {
|
|
post: vi.fn(),
|
|
get: vi.fn(),
|
|
},
|
|
}));
|
|
|
|
// Mock handleApiServiceError - it always throws, so we let it through
|
|
// but need the real implementation for error handling tests
|
|
vi.mock('@/utils/serviceErrorHandler', async () => {
|
|
const actual = await vi.importActual('@/utils/serviceErrorHandler');
|
|
return actual;
|
|
});
|
|
|
|
const mockedApiClient = apiClient as {
|
|
post: ReturnType<typeof vi.fn>;
|
|
get: ReturnType<typeof vi.fn>;
|
|
};
|
|
|
|
describe('authService', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
});
|
|
|
|
describe('login', () => {
|
|
it('should successfully login and return auth response', async () => {
|
|
const mockResponse: AuthResponse = {
|
|
user: {
|
|
id: '1',
|
|
email: 'test@example.com',
|
|
username: 'testuser',
|
|
},
|
|
token: {
|
|
access_token: 'access-token-123',
|
|
refresh_token: 'refresh-token-123',
|
|
expires_in: 3600,
|
|
},
|
|
};
|
|
|
|
mockedApiClient.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await login({
|
|
email: 'test@example.com',
|
|
password: 'password123',
|
|
});
|
|
|
|
expect(result).toEqual(mockResponse);
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/login', {
|
|
email: 'test@example.com',
|
|
password: 'password123',
|
|
});
|
|
});
|
|
|
|
it('should throw on login failure', async () => {
|
|
const mockError = new AxiosError('Login failed');
|
|
mockError.response = {
|
|
status: 401,
|
|
data: { error: 'Invalid credentials' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
login({
|
|
email: 'test@example.com',
|
|
password: 'wrongpassword',
|
|
}),
|
|
).rejects.toThrow();
|
|
});
|
|
|
|
it('should throw on network errors', async () => {
|
|
const mockError = new AxiosError('Network Error');
|
|
mockError.request = {};
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
login({
|
|
email: 'test@example.com',
|
|
password: 'password123',
|
|
}),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('register', () => {
|
|
it('should successfully register and return auth response', async () => {
|
|
const mockResponse: AuthResponse = {
|
|
user: {
|
|
id: '1',
|
|
email: 'newuser@example.com',
|
|
username: 'newuser',
|
|
},
|
|
token: {
|
|
access_token: 'access-token-123',
|
|
refresh_token: 'refresh-token-123',
|
|
expires_in: 3600,
|
|
},
|
|
};
|
|
|
|
mockedApiClient.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await register({
|
|
email: 'newuser@example.com',
|
|
password: 'password123',
|
|
password_confirm: 'password123',
|
|
username: 'newuser',
|
|
});
|
|
|
|
expect(result).toEqual(mockResponse);
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/register', {
|
|
email: 'newuser@example.com',
|
|
password: 'password123',
|
|
password_confirm: 'password123',
|
|
username: 'newuser',
|
|
});
|
|
});
|
|
|
|
it('should throw on registration failure', async () => {
|
|
const mockError = new AxiosError('Registration failed');
|
|
mockError.response = {
|
|
status: 409,
|
|
data: { error: 'Email already exists' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
register({
|
|
email: 'existing@example.com',
|
|
password: 'password123',
|
|
password_confirm: 'password123',
|
|
username: 'existinguser',
|
|
}),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('logout', () => {
|
|
it('should successfully logout', async () => {
|
|
mockedApiClient.post.mockResolvedValue({ data: {} });
|
|
|
|
await logout();
|
|
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/logout');
|
|
});
|
|
|
|
it('should throw on logout failure', async () => {
|
|
const mockError = new AxiosError('Logout failed');
|
|
mockError.response = {
|
|
status: 500,
|
|
data: { error: 'Internal server error' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(logout()).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('refreshToken', () => {
|
|
it('should successfully refresh token and return auth response', async () => {
|
|
const mockResponse: AuthResponse = {
|
|
user: {
|
|
id: '1',
|
|
email: 'test@example.com',
|
|
username: 'testuser',
|
|
},
|
|
token: {
|
|
access_token: 'new-access-token-123',
|
|
refresh_token: 'new-refresh-token-123',
|
|
expires_in: 3600,
|
|
},
|
|
};
|
|
|
|
mockedApiClient.post.mockResolvedValue({ data: mockResponse });
|
|
|
|
const result = await refreshToken('refresh-token-123');
|
|
|
|
expect(result).toEqual(mockResponse);
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/refresh', {
|
|
refreshToken: 'refresh-token-123',
|
|
});
|
|
});
|
|
|
|
it('should throw on refresh failure', async () => {
|
|
const mockError = new AxiosError('Refresh failed');
|
|
mockError.response = {
|
|
status: 401,
|
|
data: { error: 'Invalid refresh token' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(refreshToken('invalid-token')).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('requestPasswordReset', () => {
|
|
it('should successfully request password reset', async () => {
|
|
mockedApiClient.post.mockResolvedValue({ data: {} });
|
|
|
|
await requestPasswordReset({
|
|
email: 'test@example.com',
|
|
});
|
|
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith(
|
|
'/auth/password/reset-request',
|
|
{
|
|
email: 'test@example.com',
|
|
},
|
|
);
|
|
});
|
|
|
|
it('should throw on request failure', async () => {
|
|
const mockError = new AxiosError('Request failed');
|
|
mockError.response = {
|
|
status: 404,
|
|
data: { error: 'User not found' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
requestPasswordReset({
|
|
email: 'nonexistent@example.com',
|
|
}),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('resetPassword', () => {
|
|
it('should successfully reset password', async () => {
|
|
mockedApiClient.post.mockResolvedValue({ data: {} });
|
|
|
|
await resetPassword({
|
|
token: 'reset-token-123',
|
|
password: 'newpassword123',
|
|
confirmPassword: 'newpassword123',
|
|
});
|
|
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith(
|
|
'/auth/password/reset',
|
|
{
|
|
token: 'reset-token-123',
|
|
password: 'newpassword123',
|
|
},
|
|
);
|
|
});
|
|
|
|
it('should throw on reset failure', async () => {
|
|
const mockError = new AxiosError('Reset failed');
|
|
mockError.response = {
|
|
status: 400,
|
|
data: { error: 'Invalid or expired token' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
resetPassword({
|
|
token: 'invalid-token',
|
|
password: 'newpassword123',
|
|
confirmPassword: 'newpassword123',
|
|
}),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('verifyEmail', () => {
|
|
it('should successfully verify email', async () => {
|
|
mockedApiClient.get.mockResolvedValue({ data: {} });
|
|
|
|
await verifyEmail('verification-token-123');
|
|
|
|
expect(mockedApiClient.get).toHaveBeenCalledWith(
|
|
'/auth/verify-email?token=verification-token-123',
|
|
);
|
|
});
|
|
|
|
it('should throw on verification failure', async () => {
|
|
const mockError = new AxiosError('Verification failed');
|
|
mockError.response = {
|
|
status: 400,
|
|
data: { error: 'Invalid or expired token' },
|
|
} as any;
|
|
|
|
mockedApiClient.get.mockRejectedValue(mockError);
|
|
|
|
await expect(verifyEmail('invalid-token')).rejects.toThrow();
|
|
});
|
|
});
|
|
|
|
describe('resendVerificationEmail', () => {
|
|
it('should successfully resend verification email', async () => {
|
|
mockedApiClient.post.mockResolvedValue({ data: {} });
|
|
|
|
await resendVerificationEmail('test@example.com');
|
|
|
|
expect(mockedApiClient.post).toHaveBeenCalledWith(
|
|
'/auth/resend-verification',
|
|
{
|
|
email: 'test@example.com',
|
|
},
|
|
);
|
|
});
|
|
|
|
it('should throw on resend failure', async () => {
|
|
const mockError = new AxiosError('Resend failed');
|
|
mockError.response = {
|
|
status: 429,
|
|
data: { error: 'Too many requests' },
|
|
} as any;
|
|
|
|
mockedApiClient.post.mockRejectedValue(mockError);
|
|
|
|
await expect(
|
|
resendVerificationEmail('test@example.com'),
|
|
).rejects.toThrow();
|
|
});
|
|
});
|
|
});
|