view models
This commit is contained in:
@@ -1,292 +0,0 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { DriverRegistrationService } from './DriverRegistrationService';
|
||||
import type { DriversApiClient } from '../../api/drivers/DriversApiClient';
|
||||
import type { DriverRegistrationStatusPresenter } from '../../presenters/DriverRegistrationStatusPresenter';
|
||||
import type { DriverRegistrationStatusDto } from '../../dtos';
|
||||
import type { DriverRegistrationStatusViewModel } from '../../view-models';
|
||||
|
||||
describe('DriverRegistrationService', () => {
|
||||
let service: DriverRegistrationService;
|
||||
let mockApiClient: DriversApiClient;
|
||||
let mockStatusPresenter: DriverRegistrationStatusPresenter;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getRegistrationStatus: vi.fn(),
|
||||
} as unknown as DriversApiClient;
|
||||
|
||||
mockStatusPresenter = {
|
||||
present: vi.fn(),
|
||||
} as unknown as DriverRegistrationStatusPresenter;
|
||||
|
||||
service = new DriverRegistrationService(
|
||||
mockApiClient,
|
||||
mockStatusPresenter
|
||||
);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('should create instance with injected dependencies', () => {
|
||||
expect(service).toBeInstanceOf(DriverRegistrationService);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDriverRegistrationStatus', () => {
|
||||
it('should fetch registration status from API and transform via presenter', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
|
||||
const mockDto: DriverRegistrationStatusDto = {
|
||||
isRegistered: true,
|
||||
raceId: 'race-456',
|
||||
driverId: 'driver-123',
|
||||
};
|
||||
|
||||
const mockViewModel = {
|
||||
isRegistered: true,
|
||||
raceId: 'race-456',
|
||||
driverId: 'driver-123',
|
||||
statusMessage: 'Registered for this race',
|
||||
statusColor: 'green',
|
||||
statusBadgeVariant: 'success',
|
||||
registrationButtonText: 'Withdraw',
|
||||
canRegister: false,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockStatusPresenter.present).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getDriverRegistrationStatus(driverId, raceId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
expect(mockStatusPresenter.present).toHaveBeenCalledWith(mockDto);
|
||||
expect(result).toEqual(mockViewModel);
|
||||
});
|
||||
|
||||
it('should handle unregistered driver status', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-789';
|
||||
const raceId = 'race-101';
|
||||
|
||||
const mockDto: DriverRegistrationStatusDto = {
|
||||
isRegistered: false,
|
||||
raceId: 'race-101',
|
||||
driverId: 'driver-789',
|
||||
};
|
||||
|
||||
const mockViewModel = {
|
||||
isRegistered: false,
|
||||
raceId: 'race-101',
|
||||
driverId: 'driver-789',
|
||||
statusMessage: 'Not registered',
|
||||
statusColor: 'red',
|
||||
statusBadgeVariant: 'warning',
|
||||
registrationButtonText: 'Register',
|
||||
canRegister: true,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockStatusPresenter.present).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
const result = await service.getDriverRegistrationStatus(driverId, raceId);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
expect(mockStatusPresenter.present).toHaveBeenCalledWith(mockDto);
|
||||
expect(result).toEqual(mockViewModel);
|
||||
expect(result.canRegister).toBe(true);
|
||||
});
|
||||
|
||||
it('should propagate errors from API client', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
const error = new Error('Failed to fetch registration status');
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockRejectedValue(error);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getDriverRegistrationStatus(driverId, raceId))
|
||||
.rejects.toThrow('Failed to fetch registration status');
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
expect(mockStatusPresenter.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle network errors gracefully', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
const networkError = new Error('Network request failed');
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockRejectedValue(networkError);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getDriverRegistrationStatus(driverId, raceId))
|
||||
.rejects.toThrow('Network request failed');
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
});
|
||||
|
||||
it('should handle API errors with proper error propagation', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
const apiError = new Error('Race not found');
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockRejectedValue(apiError);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getDriverRegistrationStatus(driverId, raceId))
|
||||
.rejects.toThrow('Race not found');
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
expect(mockStatusPresenter.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle multiple consecutive calls correctly', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId1 = 'race-456';
|
||||
const raceId2 = 'race-789';
|
||||
|
||||
const mockDto1: DriverRegistrationStatusDto = {
|
||||
isRegistered: true,
|
||||
raceId: raceId1,
|
||||
driverId,
|
||||
};
|
||||
|
||||
const mockDto2: DriverRegistrationStatusDto = {
|
||||
isRegistered: false,
|
||||
raceId: raceId2,
|
||||
driverId,
|
||||
};
|
||||
|
||||
const mockViewModel1 = {
|
||||
isRegistered: true,
|
||||
raceId: raceId1,
|
||||
driverId,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
const mockViewModel2 = {
|
||||
isRegistered: false,
|
||||
raceId: raceId2,
|
||||
driverId,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus)
|
||||
.mockResolvedValueOnce(mockDto1)
|
||||
.mockResolvedValueOnce(mockDto2);
|
||||
|
||||
vi.mocked(mockStatusPresenter.present)
|
||||
.mockReturnValueOnce(mockViewModel1)
|
||||
.mockReturnValueOnce(mockViewModel2);
|
||||
|
||||
// Act
|
||||
const result1 = await service.getDriverRegistrationStatus(driverId, raceId1);
|
||||
const result2 = await service.getDriverRegistrationStatus(driverId, raceId2);
|
||||
|
||||
// Assert
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledTimes(2);
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenNthCalledWith(1, driverId, raceId1);
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenNthCalledWith(2, driverId, raceId2);
|
||||
expect(result1.isRegistered).toBe(true);
|
||||
expect(result2.isRegistered).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle different driver IDs for same race', async () => {
|
||||
// Arrange
|
||||
const driverId1 = 'driver-123';
|
||||
const driverId2 = 'driver-456';
|
||||
const raceId = 'race-789';
|
||||
|
||||
const mockDto1: DriverRegistrationStatusDto = {
|
||||
isRegistered: true,
|
||||
raceId,
|
||||
driverId: driverId1,
|
||||
};
|
||||
|
||||
const mockDto2: DriverRegistrationStatusDto = {
|
||||
isRegistered: false,
|
||||
raceId,
|
||||
driverId: driverId2,
|
||||
};
|
||||
|
||||
const mockViewModel1 = {
|
||||
isRegistered: true,
|
||||
raceId,
|
||||
driverId: driverId1,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
const mockViewModel2 = {
|
||||
isRegistered: false,
|
||||
raceId,
|
||||
driverId: driverId2,
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus)
|
||||
.mockResolvedValueOnce(mockDto1)
|
||||
.mockResolvedValueOnce(mockDto2);
|
||||
|
||||
vi.mocked(mockStatusPresenter.present)
|
||||
.mockReturnValueOnce(mockViewModel1)
|
||||
.mockReturnValueOnce(mockViewModel2);
|
||||
|
||||
// Act
|
||||
const result1 = await service.getDriverRegistrationStatus(driverId1, raceId);
|
||||
const result2 = await service.getDriverRegistrationStatus(driverId2, raceId);
|
||||
|
||||
// Assert
|
||||
expect(result1.driverId).toBe(driverId1);
|
||||
expect(result1.isRegistered).toBe(true);
|
||||
expect(result2.driverId).toBe(driverId2);
|
||||
expect(result2.isRegistered).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle unauthorized access errors', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
const authError = new Error('Unauthorized: Driver not found');
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockRejectedValue(authError);
|
||||
|
||||
// Act & Assert
|
||||
await expect(service.getDriverRegistrationStatus(driverId, raceId))
|
||||
.rejects.toThrow('Unauthorized: Driver not found');
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalledWith(driverId, raceId);
|
||||
expect(mockStatusPresenter.present).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call presenter only after successful API response', async () => {
|
||||
// Arrange
|
||||
const driverId = 'driver-123';
|
||||
const raceId = 'race-456';
|
||||
|
||||
const mockDto: DriverRegistrationStatusDto = {
|
||||
isRegistered: true,
|
||||
raceId: 'race-456',
|
||||
driverId: 'driver-123',
|
||||
};
|
||||
|
||||
const mockViewModel = {
|
||||
isRegistered: true,
|
||||
raceId: 'race-456',
|
||||
driverId: 'driver-123',
|
||||
} as DriverRegistrationStatusViewModel;
|
||||
|
||||
vi.mocked(mockApiClient.getRegistrationStatus).mockResolvedValue(mockDto);
|
||||
vi.mocked(mockStatusPresenter.present).mockReturnValue(mockViewModel);
|
||||
|
||||
// Act
|
||||
await service.getDriverRegistrationStatus(driverId, raceId);
|
||||
|
||||
// Assert - verify call order
|
||||
expect(mockApiClient.getRegistrationStatus).toHaveBeenCalled();
|
||||
expect(mockStatusPresenter.present).toHaveBeenCalledAfter(
|
||||
mockApiClient.getRegistrationStatus as any
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user