core tests
This commit is contained in:
242
core/leagues/application/use-cases/JoinLeagueUseCase.test.ts
Normal file
242
core/leagues/application/use-cases/JoinLeagueUseCase.test.ts
Normal file
@@ -0,0 +1,242 @@
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { JoinLeagueUseCase } from './JoinLeagueUseCase';
|
||||
import type { LeagueRepository } from '../ports/LeagueRepository';
|
||||
import type { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
|
||||
import type { EventPublisher } from '../../../shared/ports/EventPublisher';
|
||||
import type { JoinLeagueCommand } from '../ports/JoinLeagueCommand';
|
||||
|
||||
const mockLeagueRepository = {
|
||||
findById: vi.fn(),
|
||||
addPendingRequests: vi.fn(),
|
||||
addLeagueMembers: vi.fn(),
|
||||
};
|
||||
|
||||
const mockDriverRepository = {
|
||||
findDriverById: vi.fn(),
|
||||
};
|
||||
|
||||
const mockEventPublisher = {
|
||||
publish: vi.fn(),
|
||||
};
|
||||
|
||||
describe('JoinLeagueUseCase', () => {
|
||||
let useCase: JoinLeagueUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
// Reset mocks
|
||||
vi.clearAllMocks();
|
||||
|
||||
useCase = new JoinLeagueUseCase(
|
||||
mockLeagueRepository as any,
|
||||
mockDriverRepository as any,
|
||||
mockEventPublisher as any
|
||||
);
|
||||
});
|
||||
|
||||
describe('Scenario 1: League missing', () => {
|
||||
it('should throw "League not found" when league does not exist', async () => {
|
||||
// Given
|
||||
const command: JoinLeagueCommand = {
|
||||
leagueId: 'league-123',
|
||||
driverId: 'driver-456',
|
||||
};
|
||||
|
||||
mockLeagueRepository.findById.mockImplementation(() => Promise.resolve(null));
|
||||
|
||||
// When & Then
|
||||
await expect(useCase.execute(command)).rejects.toThrow('League not found');
|
||||
expect(mockLeagueRepository.findById).toHaveBeenCalledWith('league-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 2: Driver missing', () => {
|
||||
it('should throw "Driver not found" when driver does not exist', async () => {
|
||||
// Given
|
||||
const command: JoinLeagueCommand = {
|
||||
leagueId: 'league-123',
|
||||
driverId: 'driver-456',
|
||||
};
|
||||
|
||||
const mockLeague = {
|
||||
id: 'league-123',
|
||||
name: 'Test League',
|
||||
description: null,
|
||||
visibility: 'public' as const,
|
||||
ownerId: 'owner-789',
|
||||
status: 'active' as const,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
maxDrivers: null,
|
||||
approvalRequired: true,
|
||||
lateJoinAllowed: true,
|
||||
raceFrequency: null,
|
||||
raceDay: null,
|
||||
raceTime: null,
|
||||
tracks: null,
|
||||
scoringSystem: null,
|
||||
bonusPointsEnabled: false,
|
||||
penaltiesEnabled: false,
|
||||
protestsEnabled: false,
|
||||
appealsEnabled: false,
|
||||
stewardTeam: null,
|
||||
gameType: null,
|
||||
skillLevel: null,
|
||||
category: null,
|
||||
tags: null,
|
||||
};
|
||||
|
||||
mockLeagueRepository.findById.mockImplementation(() => Promise.resolve(mockLeague));
|
||||
mockDriverRepository.findDriverById.mockImplementation(() => Promise.resolve(null));
|
||||
|
||||
// When & Then
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Driver not found');
|
||||
expect(mockLeagueRepository.findById).toHaveBeenCalledWith('league-123');
|
||||
expect(mockDriverRepository.findDriverById).toHaveBeenCalledWith('driver-456');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 3: approvalRequired path uses pending requests + time determinism', () => {
|
||||
it('should add pending request with deterministic time when approvalRequired is true', async () => {
|
||||
// Given
|
||||
const command: JoinLeagueCommand = {
|
||||
leagueId: 'league-123',
|
||||
driverId: 'driver-456',
|
||||
};
|
||||
|
||||
const mockLeague = {
|
||||
id: 'league-123',
|
||||
name: 'Test League',
|
||||
description: null,
|
||||
visibility: 'public' as const,
|
||||
ownerId: 'owner-789',
|
||||
status: 'active' as const,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
maxDrivers: null,
|
||||
approvalRequired: true,
|
||||
lateJoinAllowed: true,
|
||||
raceFrequency: null,
|
||||
raceDay: null,
|
||||
raceTime: null,
|
||||
tracks: null,
|
||||
scoringSystem: null,
|
||||
bonusPointsEnabled: false,
|
||||
penaltiesEnabled: false,
|
||||
protestsEnabled: false,
|
||||
appealsEnabled: false,
|
||||
stewardTeam: null,
|
||||
gameType: null,
|
||||
skillLevel: null,
|
||||
category: null,
|
||||
tags: null,
|
||||
};
|
||||
|
||||
const mockDriver = {
|
||||
id: 'driver-456',
|
||||
name: 'Test Driver',
|
||||
iracingId: 'iracing-123',
|
||||
avatarUrl: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
// Freeze time for deterministic testing
|
||||
const frozenTime = new Date('2024-01-01T00:00:00.000Z');
|
||||
vi.setSystemTime(frozenTime);
|
||||
|
||||
mockLeagueRepository.findById.mockResolvedValue(mockLeague);
|
||||
mockDriverRepository.findDriverById.mockResolvedValue(mockDriver);
|
||||
|
||||
// When
|
||||
await useCase.execute(command);
|
||||
|
||||
// Then
|
||||
expect(mockLeagueRepository.addPendingRequests).toHaveBeenCalledWith(
|
||||
'league-123',
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: expect.any(String),
|
||||
driverId: 'driver-456',
|
||||
name: 'Test Driver',
|
||||
requestDate: frozenTime,
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
// Verify no members were added
|
||||
expect(mockLeagueRepository.addLeagueMembers).not.toHaveBeenCalled();
|
||||
|
||||
// Reset system time
|
||||
vi.useRealTimers();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Scenario 4: no-approval path adds member', () => {
|
||||
it('should add member when approvalRequired is false', async () => {
|
||||
// Given
|
||||
const command: JoinLeagueCommand = {
|
||||
leagueId: 'league-123',
|
||||
driverId: 'driver-456',
|
||||
};
|
||||
|
||||
const mockLeague = {
|
||||
id: 'league-123',
|
||||
name: 'Test League',
|
||||
description: null,
|
||||
visibility: 'public' as const,
|
||||
ownerId: 'owner-789',
|
||||
status: 'active' as const,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
maxDrivers: null,
|
||||
approvalRequired: false,
|
||||
lateJoinAllowed: true,
|
||||
raceFrequency: null,
|
||||
raceDay: null,
|
||||
raceTime: null,
|
||||
tracks: null,
|
||||
scoringSystem: null,
|
||||
bonusPointsEnabled: false,
|
||||
penaltiesEnabled: false,
|
||||
protestsEnabled: false,
|
||||
appealsEnabled: false,
|
||||
stewardTeam: null,
|
||||
gameType: null,
|
||||
skillLevel: null,
|
||||
category: null,
|
||||
tags: null,
|
||||
};
|
||||
|
||||
const mockDriver = {
|
||||
id: 'driver-456',
|
||||
name: 'Test Driver',
|
||||
iracingId: 'iracing-123',
|
||||
avatarUrl: null,
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
};
|
||||
|
||||
mockLeagueRepository.findById.mockResolvedValue(mockLeague);
|
||||
mockDriverRepository.findDriverById.mockResolvedValue(mockDriver);
|
||||
|
||||
// When
|
||||
await useCase.execute(command);
|
||||
|
||||
// Then
|
||||
expect(mockLeagueRepository.addLeagueMembers).toHaveBeenCalledWith(
|
||||
'league-123',
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
driverId: 'driver-456',
|
||||
name: 'Test Driver',
|
||||
role: 'member',
|
||||
joinDate: expect.any(Date),
|
||||
}),
|
||||
])
|
||||
);
|
||||
|
||||
// Verify no pending requests were added
|
||||
expect(mockLeagueRepository.addPendingRequests).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user