website refactor
This commit is contained in:
398
apps/api/src/domain/league/LeagueController.detail.test.ts
Normal file
398
apps/api/src/domain/league/LeagueController.detail.test.ts
Normal file
@@ -0,0 +1,398 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueService } from './LeagueService';
|
||||
|
||||
describe('LeagueController - Detail Endpoints', () => {
|
||||
let controller: LeagueController;
|
||||
let mockService: ReturnType<typeof vi.mocked<LeagueService>>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockService = {
|
||||
getLeagueOwnerSummary: vi.fn(),
|
||||
getLeagueSeasons: vi.fn(),
|
||||
getLeagueStats: vi.fn(),
|
||||
getLeagueMemberships: vi.fn(),
|
||||
} as never;
|
||||
|
||||
controller = new LeagueController(mockService);
|
||||
});
|
||||
|
||||
describe('getLeague', () => {
|
||||
it('should return league details by ID', async () => {
|
||||
const mockResult = {
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
rating: 1500,
|
||||
rank: 10,
|
||||
};
|
||||
mockService.getLeagueOwnerSummary.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeague('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueOwnerSummary).toHaveBeenCalledWith({
|
||||
ownerId: 'unknown',
|
||||
leagueId: 'league-1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle league not found gracefully', async () => {
|
||||
mockService.getLeagueOwnerSummary.mockRejectedValue(new Error('League not found'));
|
||||
|
||||
await expect(controller.getLeague('non-existent-league')).rejects.toThrow('League not found');
|
||||
});
|
||||
|
||||
it('should return league with minimal information', async () => {
|
||||
const mockResult = {
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'Simple Driver',
|
||||
country: 'DE',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
rating: null,
|
||||
rank: null,
|
||||
};
|
||||
mockService.getLeagueOwnerSummary.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeague('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.driver.name).toBe('Simple Driver');
|
||||
expect(result.rating).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeagueSeasons', () => {
|
||||
it('should return seasons for a league', async () => {
|
||||
const mockResult = [
|
||||
{
|
||||
seasonId: 'season-1',
|
||||
name: 'Season 1',
|
||||
status: 'active',
|
||||
startDate: new Date('2024-01-01'),
|
||||
endDate: new Date('2024-06-30'),
|
||||
isPrimary: true,
|
||||
isParallelActive: false,
|
||||
totalRaces: 12,
|
||||
completedRaces: 6,
|
||||
nextRaceAt: new Date('2024-03-15'),
|
||||
},
|
||||
{
|
||||
seasonId: 'season-2',
|
||||
name: 'Season 2',
|
||||
status: 'upcoming',
|
||||
startDate: new Date('2024-07-01'),
|
||||
endDate: new Date('2024-12-31'),
|
||||
isPrimary: false,
|
||||
isParallelActive: false,
|
||||
totalRaces: 12,
|
||||
completedRaces: 0,
|
||||
},
|
||||
];
|
||||
mockService.getLeagueSeasons.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSeasons('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueSeasons).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
});
|
||||
|
||||
it('should return empty array when league has no seasons', async () => {
|
||||
const mockResult: never[] = [];
|
||||
mockService.getLeagueSeasons.mockResolvedValue(mockResult);
|
||||
|
||||
const result = await controller.getLeagueSeasons('league-1');
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle league with single season', async () => {
|
||||
const mockResult = [
|
||||
{
|
||||
seasonId: 'season-1',
|
||||
name: 'Season 1',
|
||||
status: 'active',
|
||||
startDate: new Date('2024-01-01'),
|
||||
endDate: new Date('2024-12-31'),
|
||||
isPrimary: true,
|
||||
isParallelActive: false,
|
||||
totalRaces: 24,
|
||||
completedRaces: 12,
|
||||
nextRaceAt: new Date('2024-06-15'),
|
||||
},
|
||||
];
|
||||
mockService.getLeagueSeasons.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSeasons('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]?.totalRaces).toBe(24);
|
||||
});
|
||||
|
||||
it('should handle seasons with different statuses', async () => {
|
||||
const mockResult = [
|
||||
{
|
||||
seasonId: 'season-1',
|
||||
name: 'Season 1',
|
||||
status: 'completed',
|
||||
startDate: new Date('2024-01-01'),
|
||||
endDate: new Date('2024-06-30'),
|
||||
isPrimary: true,
|
||||
isParallelActive: false,
|
||||
totalRaces: 12,
|
||||
completedRaces: 12,
|
||||
},
|
||||
{
|
||||
seasonId: 'season-2',
|
||||
name: 'Season 2',
|
||||
status: 'active',
|
||||
startDate: new Date('2024-07-01'),
|
||||
endDate: new Date('2024-12-31'),
|
||||
isPrimary: false,
|
||||
isParallelActive: false,
|
||||
totalRaces: 12,
|
||||
completedRaces: 6,
|
||||
nextRaceAt: new Date('2024-10-15'),
|
||||
},
|
||||
{
|
||||
seasonId: 'season-3',
|
||||
name: 'Season 3',
|
||||
status: 'upcoming',
|
||||
startDate: new Date('2025-01-01'),
|
||||
endDate: new Date('2025-06-30'),
|
||||
isPrimary: false,
|
||||
isParallelActive: false,
|
||||
totalRaces: 12,
|
||||
completedRaces: 0,
|
||||
},
|
||||
];
|
||||
mockService.getLeagueSeasons.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSeasons('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result).toHaveLength(3);
|
||||
expect(result[0]?.status).toBe('completed');
|
||||
expect(result[1]?.status).toBe('active');
|
||||
expect(result[2]?.status).toBe('upcoming');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeagueStats', () => {
|
||||
it('should return league statistics', async () => {
|
||||
const mockResult = {
|
||||
totalMembers: 25,
|
||||
totalRaces: 150,
|
||||
averageRating: 1450.5,
|
||||
};
|
||||
mockService.getLeagueStats.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStats('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueStats).toHaveBeenCalledWith('league-1');
|
||||
});
|
||||
|
||||
it('should return empty stats for new league', async () => {
|
||||
const mockResult = {
|
||||
totalMembers: 0,
|
||||
totalRaces: 0,
|
||||
averageRating: 0,
|
||||
};
|
||||
mockService.getLeagueStats.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStats('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.totalMembers).toBe(0);
|
||||
expect(result.totalRaces).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle league with extensive statistics', async () => {
|
||||
const mockResult = {
|
||||
totalMembers: 100,
|
||||
totalRaces: 500,
|
||||
averageRating: 1650.75,
|
||||
};
|
||||
mockService.getLeagueStats.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStats('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.totalRaces).toBe(500);
|
||||
expect(result.totalMembers).toBe(100);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getLeagueMemberships', () => {
|
||||
it('should return league memberships', async () => {
|
||||
const mockResult = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
role: 'owner',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-15T00:00:00Z',
|
||||
},
|
||||
role: 'admin',
|
||||
joinedAt: '2024-01-15T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-02-01T00:00:00Z',
|
||||
},
|
||||
role: 'member',
|
||||
joinedAt: '2024-02-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueMemberships.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueMemberships('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueMemberships).toHaveBeenCalledWith('league-1');
|
||||
});
|
||||
|
||||
it('should return empty memberships for league with no members', async () => {
|
||||
const mockResult = { members: [] };
|
||||
mockService.getLeagueMemberships.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueMemberships('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.members).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle league with only owner', async () => {
|
||||
const mockResult = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'Owner',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
role: 'owner',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueMemberships.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueMemberships('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.members).toHaveLength(1);
|
||||
expect(result.members[0]?.role).toBe('owner');
|
||||
});
|
||||
|
||||
it('should handle league with mixed roles', async () => {
|
||||
const mockResult = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'Owner',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
role: 'owner',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Admin 1',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-02T00:00:00Z',
|
||||
},
|
||||
role: 'admin',
|
||||
joinedAt: '2024-01-02T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Admin 2',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-03T00:00:00Z',
|
||||
},
|
||||
role: 'admin',
|
||||
joinedAt: '2024-01-03T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-4',
|
||||
driver: {
|
||||
id: 'driver-4',
|
||||
iracingId: '22222',
|
||||
name: 'Member 1',
|
||||
country: 'DE',
|
||||
joinedAt: '2024-01-04T00:00:00Z',
|
||||
},
|
||||
role: 'member',
|
||||
joinedAt: '2024-01-04T00:00:00Z',
|
||||
},
|
||||
{
|
||||
driverId: 'driver-5',
|
||||
driver: {
|
||||
id: 'driver-5',
|
||||
iracingId: '33333',
|
||||
name: 'Member 2',
|
||||
country: 'FR',
|
||||
joinedAt: '2024-01-05T00:00:00Z',
|
||||
},
|
||||
role: 'member',
|
||||
joinedAt: '2024-01-05T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueMemberships.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueMemberships('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.members).toHaveLength(5);
|
||||
expect(result.members.filter(m => m.role === 'owner')).toHaveLength(1);
|
||||
expect(result.members.filter(m => m.role === 'admin')).toHaveLength(2);
|
||||
expect(result.members.filter(m => m.role === 'member')).toHaveLength(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
205
apps/api/src/domain/league/LeagueController.discovery.test.ts
Normal file
205
apps/api/src/domain/league/LeagueController.discovery.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueService } from './LeagueService';
|
||||
|
||||
describe('LeagueController - Discovery Endpoints', () => {
|
||||
let controller: LeagueController;
|
||||
let mockService: ReturnType<typeof vi.mocked<LeagueService>>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockService = {
|
||||
getAllLeaguesWithCapacity: vi.fn(),
|
||||
getAllLeaguesWithCapacityAndScoring: vi.fn(),
|
||||
getTotalLeagues: vi.fn(),
|
||||
} as never;
|
||||
|
||||
controller = new LeagueController(mockService);
|
||||
});
|
||||
|
||||
describe('getAllLeaguesWithCapacity', () => {
|
||||
it('should return leagues with capacity information', async () => {
|
||||
const mockResult = {
|
||||
leagues: [
|
||||
{
|
||||
id: 'league-1',
|
||||
name: 'GT3 Masters',
|
||||
description: 'A GT3 racing league',
|
||||
ownerId: 'owner-1',
|
||||
maxDrivers: 32,
|
||||
currentDrivers: 25,
|
||||
isPublic: true,
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
};
|
||||
mockService.getAllLeaguesWithCapacity.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacity();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getAllLeaguesWithCapacity).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return empty array when no leagues exist', async () => {
|
||||
const mockResult = { leagues: [], totalCount: 0 };
|
||||
mockService.getAllLeaguesWithCapacity.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacity();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.leagues).toHaveLength(0);
|
||||
expect(result.totalCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle multiple leagues with different capacities', async () => {
|
||||
const mockResult = {
|
||||
leagues: [
|
||||
{
|
||||
id: 'league-1',
|
||||
name: 'Small League',
|
||||
description: 'Small league',
|
||||
ownerId: 'owner-1',
|
||||
maxDrivers: 10,
|
||||
currentDrivers: 8,
|
||||
isPublic: true,
|
||||
},
|
||||
{
|
||||
id: 'league-2',
|
||||
name: 'Large League',
|
||||
description: 'Large league',
|
||||
ownerId: 'owner-2',
|
||||
maxDrivers: 50,
|
||||
currentDrivers: 45,
|
||||
isPublic: true,
|
||||
},
|
||||
],
|
||||
totalCount: 2,
|
||||
};
|
||||
mockService.getAllLeaguesWithCapacity.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacity();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.leagues).toHaveLength(2);
|
||||
expect(result.leagues[0]?.maxDrivers).toBe(10);
|
||||
expect(result.leagues[1]?.maxDrivers).toBe(50);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllLeaguesWithCapacityAndScoring', () => {
|
||||
it('should return leagues with capacity and scoring information', async () => {
|
||||
const mockResult = {
|
||||
leagues: [
|
||||
{
|
||||
id: 'league-1',
|
||||
name: 'GT3 Masters',
|
||||
description: 'A GT3 racing league',
|
||||
ownerId: 'owner-1',
|
||||
maxDrivers: 32,
|
||||
currentDrivers: 25,
|
||||
isPublic: true,
|
||||
scoringConfig: {
|
||||
pointsSystem: 'standard',
|
||||
pointsPerRace: 25,
|
||||
bonusPoints: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
};
|
||||
mockService.getAllLeaguesWithCapacityAndScoring.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacityAndScoring();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getAllLeaguesWithCapacityAndScoring).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return empty array when no leagues exist', async () => {
|
||||
const mockResult = { leagues: [], totalCount: 0 };
|
||||
mockService.getAllLeaguesWithCapacityAndScoring.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacityAndScoring();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.leagues).toHaveLength(0);
|
||||
expect(result.totalCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle leagues with different scoring configurations', async () => {
|
||||
const mockResult = {
|
||||
leagues: [
|
||||
{
|
||||
id: 'league-1',
|
||||
name: 'Standard League',
|
||||
description: 'Standard scoring',
|
||||
ownerId: 'owner-1',
|
||||
maxDrivers: 32,
|
||||
currentDrivers: 20,
|
||||
isPublic: true,
|
||||
scoringConfig: {
|
||||
pointsSystem: 'standard',
|
||||
pointsPerRace: 25,
|
||||
bonusPoints: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'league-2',
|
||||
name: 'Custom League',
|
||||
description: 'Custom scoring',
|
||||
ownerId: 'owner-2',
|
||||
maxDrivers: 20,
|
||||
currentDrivers: 15,
|
||||
isPublic: true,
|
||||
scoringConfig: {
|
||||
pointsSystem: 'custom',
|
||||
pointsPerRace: 50,
|
||||
bonusPoints: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
totalCount: 2,
|
||||
};
|
||||
mockService.getAllLeaguesWithCapacityAndScoring.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getAllLeaguesWithCapacityAndScoring();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.leagues).toHaveLength(2);
|
||||
expect(result.leagues[0]?.scoringConfig.pointsSystem).toBe('standard');
|
||||
expect(result.leagues[1]?.scoringConfig.pointsSystem).toBe('custom');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTotalLeagues', () => {
|
||||
it('should return total leagues count', async () => {
|
||||
const mockResult = { totalLeagues: 42 };
|
||||
mockService.getTotalLeagues.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getTotalLeagues();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getTotalLeagues).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should return zero when no leagues exist', async () => {
|
||||
const mockResult = { totalLeagues: 0 };
|
||||
mockService.getTotalLeagues.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getTotalLeagues();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.totalLeagues).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle large league counts', async () => {
|
||||
const mockResult = { totalLeagues: 1000 };
|
||||
mockService.getTotalLeagues.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getTotalLeagues();
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.totalLeagues).toBe(1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
231
apps/api/src/domain/league/LeagueController.schedule.test.ts
Normal file
231
apps/api/src/domain/league/LeagueController.schedule.test.ts
Normal file
@@ -0,0 +1,231 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueService } from './LeagueService';
|
||||
|
||||
describe('LeagueController - Schedule Endpoints', () => {
|
||||
let controller: LeagueController;
|
||||
let mockService: ReturnType<typeof vi.mocked<LeagueService>>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockService = {
|
||||
getLeagueSchedule: vi.fn(),
|
||||
} as never;
|
||||
|
||||
controller = new LeagueController(mockService);
|
||||
});
|
||||
|
||||
describe('getLeagueSchedule', () => {
|
||||
it('should return league schedule', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Spa Endurance',
|
||||
date: '2024-03-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Monza Sprint',
|
||||
date: '2024-03-22T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
name: 'Nürburgring Endurance',
|
||||
date: '2024-03-29T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueSchedule).toHaveBeenCalledWith('league-1', {});
|
||||
});
|
||||
|
||||
it('should return empty schedule for league with no races', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: false,
|
||||
races: [],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.races).toHaveLength(0);
|
||||
expect(result.published).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle schedule with specific season ID', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-2',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-10',
|
||||
name: 'Silverstone Endurance',
|
||||
date: '2024-08-01T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', { seasonId: 'season-2' });
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueSchedule).toHaveBeenCalledWith('league-1', { seasonId: 'season-2' });
|
||||
});
|
||||
|
||||
it('should handle schedule with multiple races on same track', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Spa Endurance',
|
||||
date: '2024-03-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Spa Sprint',
|
||||
date: '2024-04-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.races).toHaveLength(2);
|
||||
expect(result.races[0]?.name).toContain('Spa');
|
||||
expect(result.races[1]?.name).toContain('Spa');
|
||||
});
|
||||
|
||||
it('should handle schedule with different race names', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Spa Endurance',
|
||||
date: '2024-01-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Monza Sprint',
|
||||
date: '2024-02-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
name: 'Nürburgring Endurance',
|
||||
date: '2024-03-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-4',
|
||||
name: 'Silverstone Sprint',
|
||||
date: '2024-04-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.races).toHaveLength(4);
|
||||
expect(result.races[0]?.name).toBe('Spa Endurance');
|
||||
expect(result.races[1]?.name).toBe('Monza Sprint');
|
||||
expect(result.races[2]?.name).toBe('Nürburgring Endurance');
|
||||
expect(result.races[3]?.name).toBe('Silverstone Sprint');
|
||||
});
|
||||
|
||||
it('should handle schedule with different dates', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: '2024-01-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Race 2',
|
||||
date: '2024-02-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
name: 'Race 3',
|
||||
date: '2024-03-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.races).toHaveLength(3);
|
||||
expect(result.races[0]?.date).toBe('2024-01-15T14:00:00Z');
|
||||
expect(result.races[1]?.date).toBe('2024-02-15T14:00:00Z');
|
||||
expect(result.races[2]?.date).toBe('2024-03-15T14:00:00Z');
|
||||
});
|
||||
|
||||
it('should handle schedule with league name variations', async () => {
|
||||
const mockResult = {
|
||||
seasonId: 'season-1',
|
||||
published: true,
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
name: 'Race 1',
|
||||
date: '2024-03-15T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
name: 'Race 2',
|
||||
date: '2024-03-22T14:00:00Z',
|
||||
leagueName: 'GT3 Masters',
|
||||
},
|
||||
{
|
||||
id: 'race-3',
|
||||
name: 'Race 3',
|
||||
date: '2024-03-29T14:00:00Z',
|
||||
leagueName: null,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueSchedule.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueSchedule('league-1', {});
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.races).toHaveLength(3);
|
||||
expect(result.races[0]?.leagueName).toBe('GT3 Masters');
|
||||
expect(result.races[1]?.leagueName).toBe('GT3 Masters');
|
||||
expect(result.races[2]?.leagueName).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
388
apps/api/src/domain/league/LeagueController.standings.test.ts
Normal file
388
apps/api/src/domain/league/LeagueController.standings.test.ts
Normal file
@@ -0,0 +1,388 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { LeagueController } from './LeagueController';
|
||||
import { LeagueService } from './LeagueService';
|
||||
|
||||
describe('LeagueController - Standings Endpoints', () => {
|
||||
let controller: LeagueController;
|
||||
let mockService: ReturnType<typeof vi.mocked<LeagueService>>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockService = {
|
||||
getLeagueStandings: vi.fn(),
|
||||
} as never;
|
||||
|
||||
controller = new LeagueController(mockService);
|
||||
});
|
||||
|
||||
describe('getLeagueStandings', () => {
|
||||
it('should return league standings', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 5,
|
||||
podiums: 8,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 2,
|
||||
points: 145,
|
||||
races: 12,
|
||||
wins: 4,
|
||||
podiums: 7,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 3,
|
||||
points: 140,
|
||||
races: 12,
|
||||
wins: 3,
|
||||
podiums: 6,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(mockService.getLeagueStandings).toHaveBeenCalledWith('league-1');
|
||||
});
|
||||
|
||||
it('should return empty standings for league with no races', async () => {
|
||||
const mockResult = { standings: [] };
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should handle standings with single driver', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 100,
|
||||
races: 10,
|
||||
wins: 10,
|
||||
podiums: 10,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(1);
|
||||
expect(result.standings[0]?.position).toBe(1);
|
||||
expect(result.standings[0]?.points).toBe(100);
|
||||
});
|
||||
|
||||
it('should handle standings with many drivers', async () => {
|
||||
const mockResult = {
|
||||
standings: Array.from({ length: 20 }, (_, i) => ({
|
||||
driverId: `driver-${i + 1}`,
|
||||
driver: {
|
||||
id: `driver-${i + 1}`,
|
||||
iracingId: `${10000 + i}`,
|
||||
name: `Driver ${i + 1}`,
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: i + 1,
|
||||
points: 100 - i,
|
||||
races: 12,
|
||||
wins: Math.max(0, 5 - i),
|
||||
podiums: Math.max(0, 10 - i),
|
||||
})),
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(20);
|
||||
expect(result.standings[0]?.position).toBe(1);
|
||||
expect(result.standings[19]?.position).toBe(20);
|
||||
});
|
||||
|
||||
it('should handle standings with tied points', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 5,
|
||||
podiums: 8,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 2,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 5,
|
||||
podiums: 7,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 3,
|
||||
points: 145,
|
||||
races: 12,
|
||||
wins: 4,
|
||||
podiums: 6,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(3);
|
||||
expect(result.standings[0]?.points).toBe(150);
|
||||
expect(result.standings[1]?.points).toBe(150);
|
||||
expect(result.standings[2]?.points).toBe(145);
|
||||
});
|
||||
|
||||
it('should handle standings with varying race counts', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 5,
|
||||
podiums: 8,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 2,
|
||||
points: 140,
|
||||
races: 10,
|
||||
wins: 4,
|
||||
podiums: 6,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 3,
|
||||
points: 130,
|
||||
races: 8,
|
||||
wins: 3,
|
||||
podiums: 5,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(3);
|
||||
expect(result.standings[0]?.races).toBe(12);
|
||||
expect(result.standings[1]?.races).toBe(10);
|
||||
expect(result.standings[2]?.races).toBe(8);
|
||||
});
|
||||
|
||||
it('should handle standings with varying win counts', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 10,
|
||||
podiums: 12,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 2,
|
||||
points: 140,
|
||||
races: 12,
|
||||
wins: 2,
|
||||
podiums: 8,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 3,
|
||||
points: 130,
|
||||
races: 12,
|
||||
wins: 0,
|
||||
podiums: 4,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(3);
|
||||
expect(result.standings[0]?.wins).toBe(10);
|
||||
expect(result.standings[1]?.wins).toBe(2);
|
||||
expect(result.standings[2]?.wins).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle standings with varying podium counts', async () => {
|
||||
const mockResult = {
|
||||
standings: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: {
|
||||
id: 'driver-1',
|
||||
iracingId: '12345',
|
||||
name: 'John Doe',
|
||||
country: 'US',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 1,
|
||||
points: 150,
|
||||
races: 12,
|
||||
wins: 5,
|
||||
podiums: 12,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-2',
|
||||
driver: {
|
||||
id: 'driver-2',
|
||||
iracingId: '67890',
|
||||
name: 'Jane Smith',
|
||||
country: 'UK',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 2,
|
||||
points: 140,
|
||||
races: 12,
|
||||
wins: 4,
|
||||
podiums: 8,
|
||||
},
|
||||
{
|
||||
driverId: 'driver-3',
|
||||
driver: {
|
||||
id: 'driver-3',
|
||||
iracingId: '11111',
|
||||
name: 'Bob Johnson',
|
||||
country: 'CA',
|
||||
joinedAt: '2024-01-01T00:00:00Z',
|
||||
},
|
||||
position: 3,
|
||||
points: 130,
|
||||
races: 12,
|
||||
wins: 3,
|
||||
podiums: 4,
|
||||
},
|
||||
],
|
||||
};
|
||||
mockService.getLeagueStandings.mockResolvedValue(mockResult as never);
|
||||
|
||||
const result = await controller.getLeagueStandings('league-1');
|
||||
|
||||
expect(result).toEqual(mockResult);
|
||||
expect(result.standings).toHaveLength(3);
|
||||
expect(result.standings[0]?.podiums).toBe(12);
|
||||
expect(result.standings[1]?.podiums).toBe(8);
|
||||
expect(result.standings[2]?.podiums).toBe(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
257
apps/api/src/domain/league/LeagueService.endpoints.test.ts
Normal file
257
apps/api/src/domain/league/LeagueService.endpoints.test.ts
Normal file
@@ -0,0 +1,257 @@
|
||||
import { requestContextMiddleware } from '@adapters/http/RequestContext';
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import { LeagueService } from './LeagueService';
|
||||
|
||||
async function withUserId<T>(userId: string, fn: () => Promise<T>): Promise<T> {
|
||||
const req = { user: { userId } };
|
||||
const res = {};
|
||||
|
||||
return await new Promise<T>((resolve, reject) => {
|
||||
requestContextMiddleware(req as never, res as never, () => {
|
||||
fn().then(resolve, reject);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('LeagueService - All Endpoints', () => {
|
||||
it('covers all league endpoint happy paths and error branches', async () => {
|
||||
const logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn() };
|
||||
|
||||
const ok = async () => Result.ok(undefined);
|
||||
const err = async () => Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } } as never);
|
||||
|
||||
// Discovery use cases
|
||||
const getAllLeaguesWithCapacityUseCase = { execute: vi.fn(async () => Result.ok({ leagues: [] })) };
|
||||
const getAllLeaguesWithCapacityAndScoringUseCase = { execute: vi.fn(ok) };
|
||||
const getTotalLeaguesUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Detail use cases
|
||||
const getLeagueOwnerSummaryUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueSeasonsUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueStatsUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueMembershipsUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Schedule use case
|
||||
const getLeagueScheduleUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Standings use case
|
||||
const getLeagueStandingsUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Other use cases (for completeness)
|
||||
const getLeagueFullConfigUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueScoringConfigUseCase = { execute: vi.fn(ok) };
|
||||
const listLeagueScoringPresetsUseCase = { execute: vi.fn(ok) };
|
||||
const joinLeagueUseCase = { execute: vi.fn(ok) };
|
||||
const transferLeagueOwnershipUseCase = { execute: vi.fn(ok) };
|
||||
const createLeagueWithSeasonAndScoringUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueJoinRequestsUseCase = { execute: vi.fn(ok) };
|
||||
const approveLeagueJoinRequestUseCase = { execute: vi.fn(ok) };
|
||||
const rejectLeagueJoinRequestUseCase = { execute: vi.fn(ok) };
|
||||
const removeLeagueMemberUseCase = { execute: vi.fn(ok) };
|
||||
const updateLeagueMemberRoleUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueProtestsUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueAdminPermissionsUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueWalletUseCase = { execute: vi.fn(ok) };
|
||||
const withdrawFromLeagueWalletUseCase = { execute: vi.fn(ok) };
|
||||
const getSeasonSponsorshipsUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
const getLeagueRosterMembersUseCase = { execute: vi.fn(ok) };
|
||||
const getLeagueRosterJoinRequestsUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Schedule mutation use cases
|
||||
const createLeagueSeasonScheduleRaceUseCase = { execute: vi.fn(ok) };
|
||||
const updateLeagueSeasonScheduleRaceUseCase = { execute: vi.fn(ok) };
|
||||
const deleteLeagueSeasonScheduleRaceUseCase = { execute: vi.fn(ok) };
|
||||
const publishLeagueSeasonScheduleUseCase = { execute: vi.fn(ok) };
|
||||
const unpublishLeagueSeasonScheduleUseCase = { execute: vi.fn(ok) };
|
||||
|
||||
// Presenters
|
||||
const allLeaguesWithCapacityPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ leagues: [] })) };
|
||||
const allLeaguesWithCapacityAndScoringPresenter = {
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ leagues: [], totalCount: 0 })),
|
||||
};
|
||||
const leagueStandingsPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ standings: [] })) };
|
||||
const leagueProtestsPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ protests: [] })) };
|
||||
const seasonSponsorshipsPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ sponsorships: [] })) };
|
||||
const leagueScoringPresetsPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ presets: [] })) };
|
||||
const approveLeagueJoinRequestPresenter = {
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ success: true }))
|
||||
};
|
||||
const createLeaguePresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ id: 'l1' })) };
|
||||
const getLeagueAdminPermissionsPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ canManage: true })) };
|
||||
const getLeagueMembershipsPresenter = {
|
||||
reset: vi.fn(),
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ memberships: { members: [] } })),
|
||||
};
|
||||
|
||||
const getLeagueRosterMembersPresenter = {
|
||||
reset: vi.fn(),
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ([])),
|
||||
};
|
||||
|
||||
const getLeagueRosterJoinRequestsPresenter = {
|
||||
reset: vi.fn(),
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ([])),
|
||||
};
|
||||
const getLeagueOwnerSummaryPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ driver: { id: 'd1', iracingId: '12345', name: 'Driver', country: 'US', joinedAt: '2024-01-01T00:00:00Z' }, rating: 1500, rank: 10 })) };
|
||||
const getLeagueSeasonsPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ([])) };
|
||||
const joinLeaguePresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ success: true })) };
|
||||
const leagueSchedulePresenter = { reset: vi.fn(), present: vi.fn(), getViewModel: vi.fn(() => ({ seasonId: 'season-1', published: false, races: [] })) };
|
||||
const leagueStatsPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ totalMembers: 0, totalRaces: 0, averageRating: 0 })) };
|
||||
const rejectLeagueJoinRequestPresenter = {
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ success: true }))
|
||||
};
|
||||
const removeLeagueMemberPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ success: true })) };
|
||||
const totalLeaguesPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ totalLeagues: 0 })) };
|
||||
const transferLeagueOwnershipPresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ success: true })) };
|
||||
const updateLeagueMemberRolePresenter = { present: vi.fn(), getViewModel: vi.fn(() => ({ success: true })) };
|
||||
const leagueConfigPresenter = {
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ form: {} }))
|
||||
};
|
||||
const leagueScoringConfigPresenter = {
|
||||
present: vi.fn(),
|
||||
getViewModel: vi.fn(() => ({ config: {} }))
|
||||
};
|
||||
const getLeagueWalletPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ balance: 0 })) };
|
||||
const withdrawFromLeagueWalletPresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ success: true })) };
|
||||
const leagueJoinRequestsPresenter = { reset: vi.fn(), present: vi.fn(), getViewModel: vi.fn(() => ({ joinRequests: [] })) };
|
||||
|
||||
const createLeagueSeasonScheduleRacePresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ raceId: 'race-1' })) };
|
||||
const updateLeagueSeasonScheduleRacePresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ success: true })) };
|
||||
const deleteLeagueSeasonScheduleRacePresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ success: true })) };
|
||||
const publishLeagueSeasonSchedulePresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ success: true, published: true })) };
|
||||
const unpublishLeagueSeasonSchedulePresenter = { present: vi.fn(), getResponseModel: vi.fn(() => ({ success: true, published: false })) };
|
||||
|
||||
const service = new (LeagueService as unknown as { new (...args: never[]): LeagueService })(
|
||||
getAllLeaguesWithCapacityUseCase as never,
|
||||
getAllLeaguesWithCapacityAndScoringUseCase as never,
|
||||
getLeagueStandingsUseCase as never,
|
||||
getLeagueStatsUseCase as never,
|
||||
getLeagueFullConfigUseCase as never,
|
||||
getLeagueScoringConfigUseCase as never,
|
||||
listLeagueScoringPresetsUseCase as never,
|
||||
joinLeagueUseCase as never,
|
||||
transferLeagueOwnershipUseCase as never,
|
||||
createLeagueWithSeasonAndScoringUseCase as never,
|
||||
getTotalLeaguesUseCase as never,
|
||||
getLeagueJoinRequestsUseCase as never,
|
||||
approveLeagueJoinRequestUseCase as never,
|
||||
rejectLeagueJoinRequestUseCase as never,
|
||||
removeLeagueMemberUseCase as never,
|
||||
updateLeagueMemberRoleUseCase as never,
|
||||
getLeagueOwnerSummaryUseCase as never,
|
||||
getLeagueProtestsUseCase as never,
|
||||
getLeagueSeasonsUseCase as never,
|
||||
getLeagueMembershipsUseCase as never,
|
||||
getLeagueScheduleUseCase as never,
|
||||
getLeagueAdminPermissionsUseCase as never,
|
||||
getLeagueWalletUseCase as never,
|
||||
withdrawFromLeagueWalletUseCase as never,
|
||||
getSeasonSponsorshipsUseCase as never,
|
||||
createLeagueSeasonScheduleRaceUseCase as never,
|
||||
updateLeagueSeasonScheduleRaceUseCase as never,
|
||||
deleteLeagueSeasonScheduleRaceUseCase as never,
|
||||
publishLeagueSeasonScheduleUseCase as never,
|
||||
unpublishLeagueSeasonScheduleUseCase as never,
|
||||
logger as never,
|
||||
allLeaguesWithCapacityPresenter as never,
|
||||
allLeaguesWithCapacityAndScoringPresenter as never,
|
||||
leagueStandingsPresenter as never,
|
||||
leagueProtestsPresenter as never,
|
||||
seasonSponsorshipsPresenter as never,
|
||||
leagueScoringPresetsPresenter as never,
|
||||
approveLeagueJoinRequestPresenter as never,
|
||||
createLeaguePresenter as never,
|
||||
getLeagueAdminPermissionsPresenter as never,
|
||||
getLeagueMembershipsPresenter as never,
|
||||
getLeagueOwnerSummaryPresenter as never,
|
||||
getLeagueSeasonsPresenter as never,
|
||||
joinLeaguePresenter as never,
|
||||
leagueSchedulePresenter as never,
|
||||
leagueStatsPresenter as never,
|
||||
rejectLeagueJoinRequestPresenter as never,
|
||||
removeLeagueMemberPresenter as never,
|
||||
totalLeaguesPresenter as never,
|
||||
transferLeagueOwnershipPresenter as never,
|
||||
updateLeagueMemberRolePresenter as never,
|
||||
leagueConfigPresenter as never,
|
||||
leagueScoringConfigPresenter as never,
|
||||
getLeagueWalletPresenter as never,
|
||||
withdrawFromLeagueWalletPresenter as never,
|
||||
leagueJoinRequestsPresenter as never,
|
||||
createLeagueSeasonScheduleRacePresenter as never,
|
||||
updateLeagueSeasonScheduleRacePresenter as never,
|
||||
deleteLeagueSeasonScheduleRacePresenter as never,
|
||||
publishLeagueSeasonSchedulePresenter as never,
|
||||
unpublishLeagueSeasonSchedulePresenter as never,
|
||||
|
||||
// Roster admin read delegation (added for strict TDD)
|
||||
getLeagueRosterMembersUseCase as never,
|
||||
getLeagueRosterJoinRequestsUseCase as never,
|
||||
getLeagueRosterMembersPresenter as never,
|
||||
getLeagueRosterJoinRequestsPresenter as never,
|
||||
);
|
||||
|
||||
// Discovery endpoints
|
||||
await expect(service.getAllLeaguesWithCapacity()).resolves.toEqual({ leagues: [] });
|
||||
await expect(service.getAllLeaguesWithCapacityAndScoring()).resolves.toEqual({ leagues: [], totalCount: 0 });
|
||||
await expect(service.getTotalLeagues()).resolves.toEqual({ totalLeagues: 0 });
|
||||
|
||||
// Detail endpoints
|
||||
await expect(service.getLeagueOwnerSummary({ leagueId: 'l1' } as never)).resolves.toEqual({ driver: { id: 'd1', iracingId: '12345', name: 'Driver', country: 'US', joinedAt: '2024-01-01T00:00:00Z' }, rating: 1500, rank: 10 });
|
||||
await expect(service.getLeagueSeasons({ leagueId: 'l1' } as never)).resolves.toEqual([]);
|
||||
await expect(service.getLeagueStats('l1')).resolves.toEqual({ totalMembers: 0, totalRaces: 0, averageRating: 0 });
|
||||
await expect(service.getLeagueMemberships('l1')).resolves.toEqual({ members: [] });
|
||||
|
||||
// Schedule endpoint
|
||||
await expect(service.getLeagueSchedule('l1')).resolves.toEqual({ seasonId: 'season-1', published: false, races: [] });
|
||||
expect(getLeagueScheduleUseCase.execute).toHaveBeenCalledWith({ leagueId: 'l1' });
|
||||
|
||||
getLeagueScheduleUseCase.execute.mockClear();
|
||||
await expect(service.getLeagueSchedule('l1', { seasonId: 'season-x' } as never)).resolves.toEqual({
|
||||
seasonId: 'season-1',
|
||||
published: false,
|
||||
races: [],
|
||||
});
|
||||
expect(getLeagueScheduleUseCase.execute).toHaveBeenCalledWith({ leagueId: 'l1', seasonId: 'season-x' });
|
||||
|
||||
// Standings endpoint
|
||||
await expect(service.getLeagueStandings('l1')).resolves.toEqual({ standings: [] });
|
||||
|
||||
// Error branches: use case returns error result
|
||||
getAllLeaguesWithCapacityUseCase.execute.mockResolvedValueOnce(Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } } as never));
|
||||
await expect(service.getAllLeaguesWithCapacity()).rejects.toThrow('REPOSITORY_ERROR');
|
||||
|
||||
getLeagueFullConfigUseCase.execute.mockResolvedValueOnce(
|
||||
Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } } as never)
|
||||
);
|
||||
await expect(service.getLeagueFullConfig({ leagueId: 'l1' } as never)).resolves.toBeNull();
|
||||
|
||||
getLeagueScoringConfigUseCase.execute.mockImplementationOnce(async () => {
|
||||
throw new Error('boom');
|
||||
});
|
||||
await expect(service.getLeagueScoringConfig('l1')).resolves.toBeNull();
|
||||
|
||||
// Cover non-Error throw branches for logger.error wrapping
|
||||
getLeagueFullConfigUseCase.execute.mockResolvedValueOnce(
|
||||
Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } } as never)
|
||||
);
|
||||
await expect(service.getLeagueFullConfig({ leagueId: 'l1' } as never)).resolves.toBeNull();
|
||||
|
||||
getLeagueScoringConfigUseCase.execute.mockImplementationOnce(async () => {
|
||||
throw 'boom';
|
||||
});
|
||||
await expect(service.getLeagueScoringConfig('l1')).resolves.toBeNull();
|
||||
|
||||
// keep lint happy (ensures err() used)
|
||||
await err();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user