add tests
This commit is contained in:
346
apps/website/tests/services/auth/SessionService.test.ts
Normal file
346
apps/website/tests/services/auth/SessionService.test.ts
Normal file
@@ -0,0 +1,346 @@
|
||||
import { describe, it, expect, vi, Mocked, beforeEach } from 'vitest';
|
||||
import { SessionService } from '@/lib/services/auth/SessionService';
|
||||
import { AuthApiClient } from '@/lib/api/auth/AuthApiClient';
|
||||
import { SessionViewModel } from '@/lib/view-models/SessionViewModel';
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@/lib/config/apiBaseUrl', () => ({
|
||||
getWebsiteApiBaseUrl: () => 'http://localhost:3000',
|
||||
}));
|
||||
vi.mock('@/lib/config/env', () => ({
|
||||
isProductionEnvironment: () => false,
|
||||
}));
|
||||
|
||||
describe('SessionService', () => {
|
||||
let mockApiClient: Mocked<AuthApiClient>;
|
||||
let service: SessionService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
signup: vi.fn(),
|
||||
login: vi.fn(),
|
||||
logout: vi.fn(),
|
||||
forgotPassword: vi.fn(),
|
||||
resetPassword: vi.fn(),
|
||||
getSession: vi.fn(),
|
||||
} as Mocked<AuthApiClient>;
|
||||
|
||||
service = new SessionService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getSession', () => {
|
||||
describe('happy paths', () => {
|
||||
it('should call apiClient.getSession and return SessionViewModel when session exists', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(mockApiClient.getSession).toHaveBeenCalled();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm).toBeInstanceOf(SessionViewModel);
|
||||
expect(vm?.userId).toBe('user-123');
|
||||
expect(vm?.email).toBe('test@example.com');
|
||||
expect(vm?.displayName).toBe('Test User');
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getSession returns null', async () => {
|
||||
mockApiClient.getSession.mockResolvedValue(null);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(mockApiClient.getSession).toHaveBeenCalled();
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getSession returns undefined', async () => {
|
||||
mockApiClient.getSession.mockResolvedValue(undefined);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(mockApiClient.getSession).toHaveBeenCalled();
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeNull();
|
||||
});
|
||||
|
||||
it('should return null when session has no user data', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: null,
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(mockApiClient.getSession).toHaveBeenCalled();
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('failure modes', () => {
|
||||
it('should handle server errors', async () => {
|
||||
const error = new Error('Get session failed');
|
||||
mockApiClient.getSession.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Get session failed');
|
||||
});
|
||||
|
||||
it('should handle network errors', async () => {
|
||||
const error = new Error('Network error');
|
||||
mockApiClient.getSession.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Network error');
|
||||
});
|
||||
|
||||
it('should handle authentication errors', async () => {
|
||||
const error = new Error('Invalid token');
|
||||
mockApiClient.getSession.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Invalid token');
|
||||
});
|
||||
|
||||
it('should handle timeout errors', async () => {
|
||||
const error = new Error('Request timeout');
|
||||
mockApiClient.getSession.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Request timeout');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decision branches', () => {
|
||||
it('should handle different user data structures', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
role: 'admin',
|
||||
permissions: ['read', 'write'],
|
||||
lastLogin: '2024-01-01T00:00:00.000Z',
|
||||
createdAt: '2023-01-01T00:00:00.000Z',
|
||||
updatedAt: '2024-01-01T00:00:00.000Z',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm).toBeInstanceOf(SessionViewModel);
|
||||
expect(vm?.userId).toBe('user-123');
|
||||
expect(vm?.email).toBe('test@example.com');
|
||||
expect(vm?.displayName).toBe('Test User');
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle user with minimal data', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: '',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm?.displayName).toBe('');
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle user with special characters in display name', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User <script>alert("xss")</script>',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm?.displayName).toBe('Test User <script>alert("xss")</script>');
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle different email formats', async () => {
|
||||
const emails = [
|
||||
'user@example.com',
|
||||
'user+tag@example.com',
|
||||
'user.name@example.com',
|
||||
'user@subdomain.example.com',
|
||||
];
|
||||
|
||||
for (const email of emails) {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email,
|
||||
displayName: 'Test User',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm?.email).toBe(email);
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle different token formats', async () => {
|
||||
const tokens = [
|
||||
'simple-token',
|
||||
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c',
|
||||
'token-with-special-chars-!@#$%^&*()',
|
||||
];
|
||||
|
||||
for (const token of tokens) {
|
||||
const mockResponse = {
|
||||
token,
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('aggregation logic', () => {
|
||||
it('should aggregate session data correctly', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
|
||||
// Verify all user data is aggregated into the view model
|
||||
expect(vm?.userId).toBe('user-123');
|
||||
expect(vm?.email).toBe('test@example.com');
|
||||
expect(vm?.displayName).toBe('Test User');
|
||||
expect(vm?.isAuthenticated).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle empty user object', async () => {
|
||||
const mockResponse = {
|
||||
token: 'jwt-token',
|
||||
user: {},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle missing token', async () => {
|
||||
const mockResponse = {
|
||||
token: null,
|
||||
user: {
|
||||
userId: 'user-123',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getSession.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getSession();
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
const vm = result.unwrap();
|
||||
expect(vm).toBeInstanceOf(SessionViewModel);
|
||||
expect(vm?.userId).toBe('user-123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
it('should handle unexpected error types', async () => {
|
||||
const error = { customError: 'Something went wrong' };
|
||||
mockApiClient.getSession.mockRejectedValue(error);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Something went wrong');
|
||||
});
|
||||
|
||||
it('should handle string errors', async () => {
|
||||
mockApiClient.getSession.mockRejectedValue('String error');
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('String error');
|
||||
});
|
||||
|
||||
it('should handle undefined errors', async () => {
|
||||
mockApiClient.getSession.mockRejectedValue(undefined);
|
||||
|
||||
const result = await service.getSession();
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.getError().type).toBe('serverError');
|
||||
expect(result.getError().message).toBe('Failed to get session');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user