import { describe, it, expect, vi, beforeEach } from 'vitest'; import { SessionService } from './SessionService'; import { AuthApiClient } from '../../api/auth/AuthApiClient'; import { SessionPresenter } from '../../presenters/SessionPresenter'; import { SessionViewModel } from '../../view-models'; import type { SessionDataDto } from '../../dtos'; describe('SessionService', () => { let mockApiClient: AuthApiClient; let mockPresenter: SessionPresenter; let service: SessionService; beforeEach(() => { mockApiClient = { getSession: vi.fn(), signup: vi.fn(), login: vi.fn(), logout: vi.fn(), getIracingAuthUrl: vi.fn(), } as unknown as AuthApiClient; mockPresenter = { presentSession: vi.fn(), } as unknown as SessionPresenter; service = new SessionService(mockApiClient, mockPresenter); }); describe('getSession', () => { it('should get session via API client and present it', async () => { // Arrange const dto: SessionDataDto = { userId: 'user-1', email: 'test@example.com', displayName: 'Test User', driverId: 'driver-1', isAuthenticated: true, }; const expectedViewModel = new SessionViewModel(dto); vi.mocked(mockApiClient.getSession).mockResolvedValue(dto); vi.mocked(mockPresenter.presentSession).mockReturnValue(expectedViewModel); // Act const result = await service.getSession(); // Assert expect(mockApiClient.getSession).toHaveBeenCalledTimes(1); expect(mockPresenter.presentSession).toHaveBeenCalledWith(dto); expect(mockPresenter.presentSession).toHaveBeenCalledTimes(1); expect(result).toBe(expectedViewModel); }); it('should return null when session is null', async () => { // Arrange vi.mocked(mockApiClient.getSession).mockResolvedValue(null); vi.mocked(mockPresenter.presentSession).mockReturnValue(null); // Act const result = await service.getSession(); // Assert expect(mockApiClient.getSession).toHaveBeenCalledTimes(1); expect(mockPresenter.presentSession).toHaveBeenCalledWith(null); expect(result).toBeNull(); }); it('should propagate API client errors', async () => { // Arrange const error = new Error('API Error: Failed to get session'); vi.mocked(mockApiClient.getSession).mockRejectedValue(error); // Act & Assert await expect(service.getSession()).rejects.toThrow( 'API Error: Failed to get session' ); expect(mockApiClient.getSession).toHaveBeenCalledTimes(1); expect(mockPresenter.presentSession).not.toHaveBeenCalled(); }); it('should handle different session data', async () => { // Arrange const dto: SessionDataDto = { userId: 'user-2', email: 'another@example.com', isAuthenticated: false, }; const expectedViewModel = new SessionViewModel(dto); vi.mocked(mockApiClient.getSession).mockResolvedValue(dto); vi.mocked(mockPresenter.presentSession).mockReturnValue(expectedViewModel); // Act const result = await service.getSession(); // Assert expect(mockApiClient.getSession).toHaveBeenCalledTimes(1); expect(mockPresenter.presentSession).toHaveBeenCalledWith(dto); expect(result).toBe(expectedViewModel); }); }); describe('Constructor Dependency Injection', () => { it('should require apiClient and presenter', () => { // This test verifies the constructor signature expect(() => { new SessionService(mockApiClient, mockPresenter); }).not.toThrow(); }); it('should use injected dependencies', async () => { // Arrange const customApiClient = { getSession: vi.fn().mockResolvedValue({ userId: 'user-1', email: 'test@example.com', isAuthenticated: true }), signup: vi.fn(), login: vi.fn(), logout: vi.fn(), getIracingAuthUrl: vi.fn(), } as unknown as AuthApiClient; const customPresenter = { presentSession: vi.fn().mockReturnValue(new SessionViewModel({ userId: 'user-1', email: 'test@example.com', isAuthenticated: true })), } as unknown as SessionPresenter; const customService = new SessionService(customApiClient, customPresenter); // Act await customService.getSession(); // Assert expect(customApiClient.getSession).toHaveBeenCalledTimes(1); expect(customPresenter.presentSession).toHaveBeenCalledTimes(1); }); }); });