veza/apps/web/src/features/auth/services/authService.test.ts

351 lines
9.2 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, vi, beforeEach } from 'vitest';
import axios, { AxiosError } from 'axios';
import {
login,
register,
logout,
refreshToken,
requestPasswordReset,
resetPassword,
verifyEmail,
resendVerificationEmail,
type AuthResponse,
type ApiError,
} from './authService';
import { apiClient } from '@/services/api/client';
// Mock apiClient
vi.mock('@/services/api/client', () => ({
apiClient: {
post: vi.fn(),
get: vi.fn(),
},
}));
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 = {
accessToken: 'access-token-123',
refreshToken: 'refresh-token-123',
user: {
id: 1,
email: 'test@example.com',
username: 'testuser',
},
};
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 ApiError 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.toMatchObject({
message: 'Invalid credentials',
code: '401',
});
});
it('should handle 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.toMatchObject({
message: 'Network error: Unable to connect to server',
code: 'NETWORK_ERROR',
});
});
});
describe('register', () => {
it('should successfully register and return auth response', async () => {
const mockResponse: AuthResponse = {
accessToken: 'access-token-123',
refreshToken: 'refresh-token-123',
user: {
id: 1,
email: 'newuser@example.com',
username: 'newuser',
},
};
mockedApiClient.post.mockResolvedValue({ data: mockResponse });
const result = await register({
email: 'newuser@example.com',
password: 'password123',
confirmPassword: 'password123',
username: 'newuser',
});
expect(result).toEqual(mockResponse);
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/register', {
email: 'newuser@example.com',
password: 'password123',
username: 'newuser',
});
});
it('should throw ApiError 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',
confirmPassword: 'password123',
username: 'existinguser',
})
).rejects.toMatchObject({
message: 'Email already exists',
code: '409',
});
});
});
describe('logout', () => {
it('should successfully logout', async () => {
mockedApiClient.post.mockResolvedValue({ data: {} });
await logout();
expect(mockedApiClient.post).toHaveBeenCalledWith('/auth/logout');
});
it('should throw ApiError 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.toMatchObject({
message: 'Internal server error',
code: '500',
});
});
});
describe('refreshToken', () => {
it('should successfully refresh token and return auth response', async () => {
const mockResponse: AuthResponse = {
accessToken: 'new-access-token-123',
refreshToken: 'new-refresh-token-123',
user: {
id: 1,
email: 'test@example.com',
username: 'testuser',
},
};
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 ApiError 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.toMatchObject({
message: 'Invalid refresh token',
code: '401',
});
});
});
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 ApiError 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.toMatchObject({
message: 'User not found',
code: '404',
});
});
});
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 ApiError 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.toMatchObject({
message: 'Invalid or expired token',
code: '400',
});
});
});
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 ApiError 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.toMatchObject({
message: 'Invalid or expired token',
code: '400',
});
});
});
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 ApiError 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.toMatchObject({
message: 'Too many requests',
code: '429',
});
});
});
});