core tests
This commit is contained in:
320
core/dashboard/application/use-cases/GetDashboardUseCase.test.ts
Normal file
320
core/dashboard/application/use-cases/GetDashboardUseCase.test.ts
Normal file
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* Unit tests for GetDashboardUseCase
|
||||
*
|
||||
* Tests cover:
|
||||
* 1) Validation of driverId (empty and whitespace)
|
||||
* 2) Driver not found
|
||||
* 3) Filters invalid races (missing trackName, past dates)
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { GetDashboardUseCase } from './GetDashboardUseCase';
|
||||
import { ValidationError } from '../../../shared/errors/ValidationError';
|
||||
import { DriverNotFoundError } from '../../domain/errors/DriverNotFoundError';
|
||||
import { DashboardRepository } from '../ports/DashboardRepository';
|
||||
import { DashboardEventPublisher } from '../ports/DashboardEventPublisher';
|
||||
import { Logger } from '../../../shared/domain/Logger';
|
||||
import { DriverData, RaceData, LeagueStandingData, ActivityData } from '../ports/DashboardRepository';
|
||||
|
||||
describe('GetDashboardUseCase', () => {
|
||||
let mockDriverRepository: DashboardRepository;
|
||||
let mockRaceRepository: DashboardRepository;
|
||||
let mockLeagueRepository: DashboardRepository;
|
||||
let mockActivityRepository: DashboardRepository;
|
||||
let mockEventPublisher: DashboardEventPublisher;
|
||||
let mockLogger: Logger;
|
||||
|
||||
let useCase: GetDashboardUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
// Mock all ports with vi.fn()
|
||||
mockDriverRepository = {
|
||||
findDriverById: vi.fn(),
|
||||
getUpcomingRaces: vi.fn(),
|
||||
getLeagueStandings: vi.fn(),
|
||||
getRecentActivity: vi.fn(),
|
||||
getFriends: vi.fn(),
|
||||
};
|
||||
|
||||
mockRaceRepository = {
|
||||
findDriverById: vi.fn(),
|
||||
getUpcomingRaces: vi.fn(),
|
||||
getLeagueStandings: vi.fn(),
|
||||
getRecentActivity: vi.fn(),
|
||||
getFriends: vi.fn(),
|
||||
};
|
||||
|
||||
mockLeagueRepository = {
|
||||
findDriverById: vi.fn(),
|
||||
getUpcomingRaces: vi.fn(),
|
||||
getLeagueStandings: vi.fn(),
|
||||
getRecentActivity: vi.fn(),
|
||||
getFriends: vi.fn(),
|
||||
};
|
||||
|
||||
mockActivityRepository = {
|
||||
findDriverById: vi.fn(),
|
||||
getUpcomingRaces: vi.fn(),
|
||||
getLeagueStandings: vi.fn(),
|
||||
getRecentActivity: vi.fn(),
|
||||
getFriends: vi.fn(),
|
||||
};
|
||||
|
||||
mockEventPublisher = {
|
||||
publishDashboardAccessed: vi.fn(),
|
||||
publishDashboardError: vi.fn(),
|
||||
};
|
||||
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new GetDashboardUseCase({
|
||||
driverRepository: mockDriverRepository,
|
||||
raceRepository: mockRaceRepository,
|
||||
leagueRepository: mockLeagueRepository,
|
||||
activityRepository: mockActivityRepository,
|
||||
eventPublisher: mockEventPublisher,
|
||||
logger: mockLogger,
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 1: Validation of driverId', () => {
|
||||
it('should throw ValidationError when driverId is empty string', async () => {
|
||||
// Given
|
||||
const query = { driverId: '' };
|
||||
|
||||
// When & Then
|
||||
await expect(useCase.execute(query)).rejects.toThrow(ValidationError);
|
||||
await expect(useCase.execute(query)).rejects.toThrow('Driver ID cannot be empty');
|
||||
|
||||
// Verify no repositories were called
|
||||
expect(mockDriverRepository.findDriverById).not.toHaveBeenCalled();
|
||||
expect(mockRaceRepository.getUpcomingRaces).not.toHaveBeenCalled();
|
||||
expect(mockLeagueRepository.getLeagueStandings).not.toHaveBeenCalled();
|
||||
expect(mockActivityRepository.getRecentActivity).not.toHaveBeenCalled();
|
||||
expect(mockEventPublisher.publishDashboardAccessed).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should throw ValidationError when driverId is whitespace only', async () => {
|
||||
// Given
|
||||
const query = { driverId: ' ' };
|
||||
|
||||
// When & Then
|
||||
await expect(useCase.execute(query)).rejects.toThrow(ValidationError);
|
||||
await expect(useCase.execute(query)).rejects.toThrow('Driver ID cannot be empty');
|
||||
|
||||
// Verify no repositories were called
|
||||
expect(mockDriverRepository.findDriverById).not.toHaveBeenCalled();
|
||||
expect(mockRaceRepository.getUpcomingRaces).not.toHaveBeenCalled();
|
||||
expect(mockLeagueRepository.getLeagueStandings).not.toHaveBeenCalled();
|
||||
expect(mockActivityRepository.getRecentActivity).not.toHaveBeenCalled();
|
||||
expect(mockEventPublisher.publishDashboardAccessed).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 2: Driver not found', () => {
|
||||
it('should throw DriverNotFoundError when driverRepository.findDriverById returns null', async () => {
|
||||
// Given
|
||||
const query = { driverId: 'driver-123' };
|
||||
(mockDriverRepository.findDriverById as any).mockResolvedValue(null);
|
||||
|
||||
// When & Then
|
||||
await expect(useCase.execute(query)).rejects.toThrow(DriverNotFoundError);
|
||||
await expect(useCase.execute(query)).rejects.toThrow('Driver with ID "driver-123" not found');
|
||||
|
||||
// Verify driver repository was called
|
||||
expect(mockDriverRepository.findDriverById).toHaveBeenCalledWith('driver-123');
|
||||
|
||||
// Verify other repositories were not called (since driver not found)
|
||||
expect(mockRaceRepository.getUpcomingRaces).not.toHaveBeenCalled();
|
||||
expect(mockLeagueRepository.getLeagueStandings).not.toHaveBeenCalled();
|
||||
expect(mockActivityRepository.getRecentActivity).not.toHaveBeenCalled();
|
||||
expect(mockEventPublisher.publishDashboardAccessed).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 3: Filters invalid races', () => {
|
||||
it('should exclude races missing trackName', async () => {
|
||||
// Given
|
||||
const query = { driverId: 'driver-123' };
|
||||
|
||||
// Mock driver exists
|
||||
(mockDriverRepository.findDriverById as any).mockResolvedValue({
|
||||
id: 'driver-123',
|
||||
name: 'Test Driver',
|
||||
rating: 1500,
|
||||
rank: 10,
|
||||
starts: 50,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
leagues: 3,
|
||||
} as DriverData);
|
||||
|
||||
// Mock races with missing trackName
|
||||
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([
|
||||
{
|
||||
id: 'race-1',
|
||||
trackName: '', // Missing trackName
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-25T10:00:00.000Z'),
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
trackName: 'Track A',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-26T10:00:00.000Z'),
|
||||
},
|
||||
] as RaceData[]);
|
||||
|
||||
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]);
|
||||
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]);
|
||||
|
||||
// When
|
||||
const result = await useCase.execute(query);
|
||||
|
||||
// Then
|
||||
expect(result.upcomingRaces).toHaveLength(1);
|
||||
expect(result.upcomingRaces[0].trackName).toBe('Track A');
|
||||
});
|
||||
|
||||
it('should exclude races with past scheduledDate', async () => {
|
||||
// Given
|
||||
const query = { driverId: 'driver-123' };
|
||||
|
||||
// Mock driver exists
|
||||
(mockDriverRepository.findDriverById as any).mockResolvedValue({
|
||||
id: 'driver-123',
|
||||
name: 'Test Driver',
|
||||
rating: 1500,
|
||||
rank: 10,
|
||||
starts: 50,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
leagues: 3,
|
||||
} as DriverData);
|
||||
|
||||
// Mock races with past dates
|
||||
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([
|
||||
{
|
||||
id: 'race-1',
|
||||
trackName: 'Track A',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-23T10:00:00.000Z'), // Past
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
trackName: 'Track B',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-25T10:00:00.000Z'), // Future
|
||||
},
|
||||
] as RaceData[]);
|
||||
|
||||
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]);
|
||||
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]);
|
||||
|
||||
// When
|
||||
const result = await useCase.execute(query);
|
||||
|
||||
// Then
|
||||
expect(result.upcomingRaces).toHaveLength(1);
|
||||
expect(result.upcomingRaces[0].trackName).toBe('Track B');
|
||||
});
|
||||
|
||||
it('should exclude races with missing trackName and past dates', async () => {
|
||||
// Given
|
||||
const query = { driverId: 'driver-123' };
|
||||
|
||||
// Mock driver exists
|
||||
(mockDriverRepository.findDriverById as any).mockResolvedValue({
|
||||
id: 'driver-123',
|
||||
name: 'Test Driver',
|
||||
rating: 1500,
|
||||
rank: 10,
|
||||
starts: 50,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
leagues: 3,
|
||||
} as DriverData);
|
||||
|
||||
// Mock races with various invalid states
|
||||
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([
|
||||
{
|
||||
id: 'race-1',
|
||||
trackName: '', // Missing trackName
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-25T10:00:00.000Z'), // Future
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
trackName: 'Track A',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-23T10:00:00.000Z'), // Past
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
trackName: 'Track B',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-26T10:00:00.000Z'), // Future
|
||||
},
|
||||
] as RaceData[]);
|
||||
|
||||
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]);
|
||||
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]);
|
||||
|
||||
// When
|
||||
const result = await useCase.execute(query);
|
||||
|
||||
// Then
|
||||
expect(result.upcomingRaces).toHaveLength(1);
|
||||
expect(result.upcomingRaces[0].trackName).toBe('Track B');
|
||||
});
|
||||
|
||||
it('should include only valid races with trackName and future dates', async () => {
|
||||
// Given
|
||||
const query = { driverId: 'driver-123' };
|
||||
|
||||
// Mock driver exists
|
||||
(mockDriverRepository.findDriverById as any).mockResolvedValue({
|
||||
id: 'driver-123',
|
||||
name: 'Test Driver',
|
||||
rating: 1500,
|
||||
rank: 10,
|
||||
starts: 50,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
leagues: 3,
|
||||
} as DriverData);
|
||||
|
||||
// Mock races with valid data
|
||||
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([
|
||||
{
|
||||
id: 'race-1',
|
||||
trackName: 'Track A',
|
||||
carType: 'GT3',
|
||||
scheduledDate: new Date('2026-01-25T10:00:00.000Z'),
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
trackName: 'Track B',
|
||||
carType: 'GT4',
|
||||
scheduledDate: new Date('2026-01-26T10:00:00.000Z'),
|
||||
},
|
||||
] as RaceData[]);
|
||||
|
||||
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]);
|
||||
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]);
|
||||
|
||||
// When
|
||||
const result = await useCase.execute(query);
|
||||
|
||||
// Then
|
||||
expect(result.upcomingRaces).toHaveLength(2);
|
||||
expect(result.upcomingRaces[0].trackName).toBe('Track A');
|
||||
expect(result.upcomingRaces[1].trackName).toBe('Track B');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user