Files
gridpilot.gg/core/leagues/application/use-cases/JoinLeagueUseCase.test.ts
Marc Mintel bf2c0fdb0c
Some checks failed
CI / lint-typecheck (pull_request) Failing after 1m29s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
code quality
2026-01-26 01:54:57 +01:00

240 lines
6.7 KiB
TypeScript

import { describe, it, expect, vi, beforeEach } from 'vitest';
import { JoinLeagueUseCase } from './JoinLeagueUseCase';
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();
});
});
});