website service tests
This commit is contained in:
5
apps/website/lib/apiClient.ts
Normal file
5
apps/website/lib/apiClient.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { ApiClient } from './api/index';
|
||||
|
||||
const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
|
||||
|
||||
export const apiClient = new ApiClient(API_BASE_URL);
|
||||
@@ -8,7 +8,9 @@ import { Blocker } from './Blocker';
|
||||
export class ThrottleBlocker extends Blocker {
|
||||
private lastExecutionTime = 0;
|
||||
|
||||
constructor(private readonly delayMs: number) {}
|
||||
constructor(private readonly delayMs: number) {
|
||||
super();
|
||||
}
|
||||
|
||||
canExecute(): boolean {
|
||||
return Date.now() - this.lastExecutionTime >= this.delayMs;
|
||||
|
||||
60
apps/website/lib/services/dashboard/DashboardService.test.ts
Normal file
60
apps/website/lib/services/dashboard/DashboardService.test.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { DashboardService } from './DashboardService';
|
||||
import { DashboardApiClient } from '../../api/dashboard/DashboardApiClient';
|
||||
import { DashboardOverviewViewModel } from '../../view-models/DashboardOverviewViewModel';
|
||||
|
||||
describe('DashboardService', () => {
|
||||
let mockApiClient: Mocked<DashboardApiClient>;
|
||||
let service: DashboardService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getDashboardOverview: vi.fn(),
|
||||
} as Mocked<DashboardApiClient>;
|
||||
|
||||
service = new DashboardService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getDashboardOverview', () => {
|
||||
it('should call apiClient.getDashboardOverview and return DashboardOverviewViewModel', async () => {
|
||||
const mockDto = {
|
||||
currentDriver: {
|
||||
id: 'driver-123',
|
||||
name: 'Test Driver',
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
country: 'US',
|
||||
totalRaces: 42,
|
||||
wins: 10,
|
||||
podiums: 20,
|
||||
rating: 1500,
|
||||
globalRank: 5,
|
||||
consistency: 85,
|
||||
},
|
||||
myUpcomingRaces: [],
|
||||
otherUpcomingRaces: [],
|
||||
upcomingRaces: [],
|
||||
activeLeaguesCount: 3,
|
||||
nextRace: null,
|
||||
recentResults: [],
|
||||
leagueStandingsSummaries: [],
|
||||
feedSummary: { feedItems: [] },
|
||||
friends: [],
|
||||
};
|
||||
|
||||
mockApiClient.getDashboardOverview.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getDashboardOverview();
|
||||
|
||||
expect(mockApiClient.getDashboardOverview).toHaveBeenCalled();
|
||||
expect(result).toBeInstanceOf(DashboardOverviewViewModel);
|
||||
expect(result.activeLeaguesCount).toBe(3);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getDashboardOverview fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getDashboardOverview.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getDashboardOverview()).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
113
apps/website/lib/services/landing/LandingService.test.ts
Normal file
113
apps/website/lib/services/landing/LandingService.test.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { LandingService } from './LandingService';
|
||||
import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { LeaguesApiClient } from '../../api/leagues/LeaguesApiClient';
|
||||
import { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
import { HomeDiscoveryViewModel } from '@/lib/view-models/HomeDiscoveryViewModel';
|
||||
|
||||
describe('LandingService', () => {
|
||||
let mockRacesApi: Mocked<RacesApiClient>;
|
||||
let mockLeaguesApi: Mocked<LeaguesApiClient>;
|
||||
let mockTeamsApi: Mocked<TeamsApiClient>;
|
||||
let service: LandingService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRacesApi = {
|
||||
getPageData: vi.fn(),
|
||||
} as unknown as Mocked<RacesApiClient>;
|
||||
|
||||
mockLeaguesApi = {
|
||||
getAllWithCapacity: vi.fn(),
|
||||
} as unknown as Mocked<LeaguesApiClient>;
|
||||
|
||||
mockTeamsApi = {
|
||||
getAll: vi.fn(),
|
||||
} as unknown as Mocked<TeamsApiClient>;
|
||||
|
||||
service = new LandingService(mockRacesApi, mockLeaguesApi, mockTeamsApi);
|
||||
});
|
||||
|
||||
describe('getHomeDiscovery', () => {
|
||||
it('should return home discovery data with top leagues, teams, and upcoming races', async () => {
|
||||
const racesDto = {
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
track: 'Monza',
|
||||
car: 'Ferrari',
|
||||
scheduledAt: '2023-10-01T10:00:00Z',
|
||||
status: 'upcoming',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'Test League',
|
||||
strengthOfField: 1500,
|
||||
isUpcoming: true,
|
||||
isLive: false,
|
||||
isPast: false,
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
track: 'Silverstone',
|
||||
car: 'Mercedes',
|
||||
scheduledAt: '2023-10-02T10:00:00Z',
|
||||
status: 'upcoming',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'Test League',
|
||||
strengthOfField: 1600,
|
||||
isUpcoming: true,
|
||||
isLive: false,
|
||||
isPast: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const leaguesDto = {
|
||||
leagues: [
|
||||
{ id: 'league-1', name: 'League One' },
|
||||
{ id: 'league-2', name: 'League Two' },
|
||||
{ id: 'league-3', name: 'League Three' },
|
||||
{ id: 'league-4', name: 'League Four' },
|
||||
{ id: 'league-5', name: 'League Five' },
|
||||
],
|
||||
};
|
||||
|
||||
const teamsDto = {
|
||||
teams: [
|
||||
{ id: 'team-1', name: 'Team One', tag: 'T1', description: 'Best team' },
|
||||
{ id: 'team-2', name: 'Team Two', tag: 'T2', description: 'Great team' },
|
||||
{ id: 'team-3', name: 'Team Three', tag: 'T3', description: 'Awesome team' },
|
||||
{ id: 'team-4', name: 'Team Four', tag: 'T4', description: 'Cool team' },
|
||||
{ id: 'team-5', name: 'Team Five', tag: 'T5', description: 'Pro team' },
|
||||
],
|
||||
};
|
||||
|
||||
mockRacesApi.getPageData.mockResolvedValue(racesDto as any);
|
||||
mockLeaguesApi.getAllWithCapacity.mockResolvedValue(leaguesDto as any);
|
||||
mockTeamsApi.getAll.mockResolvedValue(teamsDto as any);
|
||||
|
||||
const result = await service.getHomeDiscovery();
|
||||
|
||||
expect(result).toBeInstanceOf(HomeDiscoveryViewModel);
|
||||
expect(result.topLeagues).toHaveLength(4); // First 4 leagues
|
||||
expect(result.teams).toHaveLength(4); // First 4 teams
|
||||
expect(result.upcomingRaces).toHaveLength(2); // All upcoming races (first 4)
|
||||
});
|
||||
|
||||
it('should handle empty data', async () => {
|
||||
mockRacesApi.getPageData.mockResolvedValue({ races: [] } as any);
|
||||
mockLeaguesApi.getAllWithCapacity.mockResolvedValue({ leagues: [] } as any);
|
||||
mockTeamsApi.getAll.mockResolvedValue({ teams: [] } as any);
|
||||
|
||||
const result = await service.getHomeDiscovery();
|
||||
|
||||
expect(result.topLeagues).toHaveLength(0);
|
||||
expect(result.teams).toHaveLength(0);
|
||||
expect(result.upcomingRaces).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should throw error when any API call fails', async () => {
|
||||
mockRacesApi.getPageData.mockRejectedValue(new Error('Races API failed'));
|
||||
|
||||
await expect(service.getHomeDiscovery()).rejects.toThrow('Races API failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
132
apps/website/lib/services/leagues/LeagueSettingsService.test.ts
Normal file
132
apps/website/lib/services/leagues/LeagueSettingsService.test.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { LeagueSettingsService } from './LeagueSettingsService';
|
||||
import { LeaguesApiClient } from '../../api/leagues/LeaguesApiClient';
|
||||
import { DriversApiClient } from '../../api/drivers/DriversApiClient';
|
||||
import { LeagueSettingsViewModel } from '@/lib/view-models/LeagueSettingsViewModel';
|
||||
|
||||
describe('LeagueSettingsService', () => {
|
||||
let mockLeaguesApiClient: Mocked<LeaguesApiClient>;
|
||||
let mockDriversApiClient: Mocked<DriversApiClient>;
|
||||
let service: LeagueSettingsService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLeaguesApiClient = {
|
||||
getAllWithCapacity: vi.fn(),
|
||||
getLeagueConfig: vi.fn(),
|
||||
getScoringPresets: vi.fn(),
|
||||
getMemberships: vi.fn(),
|
||||
transferOwnership: vi.fn(),
|
||||
} as unknown as Mocked<LeaguesApiClient>;
|
||||
|
||||
mockDriversApiClient = {
|
||||
getLeaderboard: vi.fn(),
|
||||
getDriver: vi.fn(),
|
||||
} as unknown as Mocked<DriversApiClient>;
|
||||
|
||||
service = new LeagueSettingsService(mockLeaguesApiClient, mockDriversApiClient);
|
||||
});
|
||||
|
||||
describe('getLeagueSettings', () => {
|
||||
it('should return league settings with all data', async () => {
|
||||
const leagueId = 'league-123';
|
||||
|
||||
// Mock getAllWithCapacity
|
||||
mockLeaguesApiClient.getAllWithCapacity.mockResolvedValue({
|
||||
leagues: [
|
||||
{ id: leagueId, name: 'Test League', ownerId: 'owner-123', capacity: 20, currentMembers: 10 },
|
||||
],
|
||||
});
|
||||
|
||||
// Mock getLeagueConfig
|
||||
mockLeaguesApiClient.getLeagueConfig.mockResolvedValue({
|
||||
config: { someConfig: 'value' },
|
||||
});
|
||||
|
||||
// Mock getScoringPresets
|
||||
mockLeaguesApiClient.getScoringPresets.mockResolvedValue({
|
||||
presets: [{ id: 'preset-1', name: 'Preset 1' }],
|
||||
});
|
||||
|
||||
// Mock getLeaderboard
|
||||
mockDriversApiClient.getLeaderboard.mockResolvedValue({
|
||||
drivers: [
|
||||
{ id: 'owner-123', name: 'Owner Driver', rating: 1500, rank: 10 },
|
||||
{ id: 'member-1', name: 'Member 1', rating: 1400, rank: 20 },
|
||||
],
|
||||
});
|
||||
|
||||
// Mock getDriver for owner
|
||||
mockDriversApiClient.getDriver.mockResolvedValue({
|
||||
id: 'owner-123',
|
||||
name: 'Owner Driver',
|
||||
avatarUrl: 'https://example.com/avatar.jpg',
|
||||
country: 'US',
|
||||
});
|
||||
|
||||
// Mock getMemberships
|
||||
mockLeaguesApiClient.getMemberships.mockResolvedValue({
|
||||
members: [
|
||||
{ driverId: 'member-1', role: 'member' },
|
||||
],
|
||||
});
|
||||
|
||||
const result = await service.getLeagueSettings(leagueId);
|
||||
|
||||
expect(result).toBeInstanceOf(LeagueSettingsViewModel);
|
||||
expect(result.league.id).toBe(leagueId);
|
||||
expect(result.league.name).toBe('Test League');
|
||||
expect(result.league.ownerId).toBe('owner-123');
|
||||
expect(result.presets).toHaveLength(1);
|
||||
expect(result.owner).toBeDefined();
|
||||
expect(result.members).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should return null when league not found', async () => {
|
||||
const leagueId = 'non-existent-league';
|
||||
|
||||
mockLeaguesApiClient.getAllWithCapacity.mockResolvedValue({
|
||||
leagues: [],
|
||||
});
|
||||
|
||||
const result = await service.getLeagueSettings(leagueId);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle errors gracefully', async () => {
|
||||
const leagueId = 'league-123';
|
||||
|
||||
mockLeaguesApiClient.getAllWithCapacity.mockRejectedValue(new Error('API error'));
|
||||
|
||||
const result = await service.getLeagueSettings(leagueId);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('transferOwnership', () => {
|
||||
it('should call apiClient.transferOwnership and return success', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const currentOwnerId = 'owner-123';
|
||||
const newOwnerId = 'new-owner-456';
|
||||
|
||||
mockLeaguesApiClient.transferOwnership.mockResolvedValue({ success: true });
|
||||
|
||||
const result = await service.transferOwnership(leagueId, currentOwnerId, newOwnerId);
|
||||
|
||||
expect(mockLeaguesApiClient.transferOwnership).toHaveBeenCalledWith(leagueId, currentOwnerId, newOwnerId);
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.transferOwnership fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const currentOwnerId = 'owner-123';
|
||||
const newOwnerId = 'new-owner-456';
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockLeaguesApiClient.transferOwnership.mockRejectedValue(error);
|
||||
|
||||
await expect(service.transferOwnership(leagueId, currentOwnerId, newOwnerId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,147 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { LeagueStewardingService } from './LeagueStewardingService';
|
||||
import { RaceService } from '../races/RaceService';
|
||||
import { ProtestService } from '../protests/ProtestService';
|
||||
import { PenaltyService } from '../penalties/PenaltyService';
|
||||
import { DriverService } from '../drivers/DriverService';
|
||||
import { LeagueMembershipService } from './LeagueMembershipService';
|
||||
import { LeagueStewardingViewModel } from '../../view-models/LeagueStewardingViewModel';
|
||||
|
||||
describe('LeagueStewardingService', () => {
|
||||
let mockRaceService: Mocked<RaceService>;
|
||||
let mockProtestService: Mocked<ProtestService>;
|
||||
let mockPenaltyService: Mocked<PenaltyService>;
|
||||
let mockDriverService: Mocked<DriverService>;
|
||||
let mockLeagueMembershipService: Mocked<LeagueMembershipService>;
|
||||
let service: LeagueStewardingService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRaceService = {
|
||||
findByLeagueId: vi.fn(),
|
||||
} as Mocked<RaceService>;
|
||||
|
||||
mockProtestService = {
|
||||
findByRaceId: vi.fn(),
|
||||
} as Mocked<ProtestService>;
|
||||
|
||||
mockPenaltyService = {
|
||||
findByRaceId: vi.fn(),
|
||||
} as Mocked<PenaltyService>;
|
||||
|
||||
mockDriverService = {
|
||||
findByIds: vi.fn(),
|
||||
} as Mocked<DriverService>;
|
||||
|
||||
mockLeagueMembershipService = {} as Mocked<LeagueMembershipService>;
|
||||
|
||||
service = new LeagueStewardingService(
|
||||
mockRaceService,
|
||||
mockProtestService,
|
||||
mockPenaltyService,
|
||||
mockDriverService,
|
||||
mockLeagueMembershipService
|
||||
);
|
||||
});
|
||||
|
||||
describe('getLeagueStewardingData', () => {
|
||||
it('should return stewarding data with all races and their protests/penalties', async () => {
|
||||
const leagueId = 'league-123';
|
||||
|
||||
// Mock races
|
||||
mockRaceService.findByLeagueId.mockResolvedValue([
|
||||
{ id: 'race-1', track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z' },
|
||||
{ id: 'race-2', track: 'Silverstone', scheduledAt: '2023-09-15T10:00:00Z' },
|
||||
]);
|
||||
|
||||
// Mock protests for race-1
|
||||
mockProtestService.findByRaceId.mockImplementation(async (raceId) => {
|
||||
if (raceId === 'race-1') {
|
||||
return [
|
||||
{ id: 'protest-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', status: 'pending' },
|
||||
{ id: 'protest-2', protestingDriverId: 'driver-3', accusedDriverId: 'driver-4', status: 'upheld' },
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Mock penalties for race-1
|
||||
mockPenaltyService.findByRaceId.mockImplementation(async (raceId) => {
|
||||
if (raceId === 'race-1') {
|
||||
return [
|
||||
{ id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
|
||||
];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
|
||||
// Mock driver service
|
||||
mockDriverService.findByIds.mockResolvedValue([
|
||||
{ id: 'driver-1', name: 'Driver 1' },
|
||||
{ id: 'driver-2', name: 'Driver 2' },
|
||||
{ id: 'driver-3', name: 'Driver 3' },
|
||||
{ id: 'driver-4', name: 'Driver 4' },
|
||||
]);
|
||||
|
||||
const result = await service.getLeagueStewardingData(leagueId);
|
||||
|
||||
expect(result).toBeInstanceOf(LeagueStewardingViewModel);
|
||||
expect(result.racesWithData).toHaveLength(2);
|
||||
expect(result.totalPending).toBe(1);
|
||||
expect(result.totalResolved).toBe(1);
|
||||
expect(result.totalPenalties).toBe(1);
|
||||
expect(result.pendingRaces).toHaveLength(1);
|
||||
expect(result.historyRaces).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should handle empty races array', async () => {
|
||||
const leagueId = 'league-123';
|
||||
|
||||
mockRaceService.findByLeagueId.mockResolvedValue([]);
|
||||
mockProtestService.findByRaceId.mockResolvedValue([]);
|
||||
mockPenaltyService.findByRaceId.mockResolvedValue([]);
|
||||
mockDriverService.findByIds.mockResolvedValue([]);
|
||||
|
||||
const result = await service.getLeagueStewardingData(leagueId);
|
||||
|
||||
expect(result.racesWithData).toHaveLength(0);
|
||||
expect(result.totalPending).toBe(0);
|
||||
expect(result.totalResolved).toBe(0);
|
||||
expect(result.totalPenalties).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reviewProtest', () => {
|
||||
it('should call protestService.reviewProtest', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
stewardId: 'steward-456',
|
||||
decision: 'upheld',
|
||||
decisionNotes: 'Test notes',
|
||||
};
|
||||
|
||||
mockProtestService.reviewProtest = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
await service.reviewProtest(input);
|
||||
|
||||
expect(mockProtestService.reviewProtest).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyPenalty', () => {
|
||||
it('should call penaltyService.applyPenalty', async () => {
|
||||
const input = {
|
||||
driverId: 'driver-123',
|
||||
raceId: 'race-456',
|
||||
type: 'time',
|
||||
value: 10,
|
||||
reason: 'Test reason',
|
||||
};
|
||||
|
||||
mockPenaltyService.applyPenalty = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
await service.applyPenalty(input);
|
||||
|
||||
expect(mockPenaltyService.applyPenalty).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
});
|
||||
123
apps/website/lib/services/leagues/LeagueWalletService.test.ts
Normal file
123
apps/website/lib/services/leagues/LeagueWalletService.test.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { LeagueWalletService } from './LeagueWalletService';
|
||||
import { WalletsApiClient } from '../../api/wallets/WalletsApiClient';
|
||||
import { LeagueWalletViewModel } from '@/lib/view-models/LeagueWalletViewModel';
|
||||
|
||||
describe('LeagueWalletService', () => {
|
||||
let mockApiClient: Mocked<WalletsApiClient>;
|
||||
let service: LeagueWalletService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getLeagueWallet: vi.fn(),
|
||||
withdrawFromLeagueWallet: vi.fn(),
|
||||
} as unknown as Mocked<WalletsApiClient>;
|
||||
|
||||
service = new LeagueWalletService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getWalletForLeague', () => {
|
||||
it('should call apiClient.getLeagueWallet and return LeagueWalletViewModel', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const mockDto = {
|
||||
balance: 1000,
|
||||
currency: 'USD',
|
||||
totalRevenue: 5000,
|
||||
totalFees: 1000,
|
||||
totalWithdrawals: 2000,
|
||||
pendingPayouts: 500,
|
||||
canWithdraw: true,
|
||||
transactions: [
|
||||
{
|
||||
id: 'txn-1',
|
||||
type: 'sponsorship' as const,
|
||||
description: 'Sponsorship payment',
|
||||
amount: 100,
|
||||
fee: 10,
|
||||
netAmount: 90,
|
||||
date: '2023-10-01T10:00:00Z',
|
||||
status: 'completed' as const,
|
||||
reference: 'ref-1',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueWallet.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getWalletForLeague(leagueId);
|
||||
|
||||
expect(mockApiClient.getLeagueWallet).toHaveBeenCalledWith(leagueId);
|
||||
expect(result).toBeInstanceOf(LeagueWalletViewModel);
|
||||
expect(result.balance).toBe(1000);
|
||||
expect(result.currency).toBe('USD');
|
||||
expect(result.transactions).toHaveLength(1);
|
||||
expect(result.formattedBalance).toBe('$1000.00');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getLeagueWallet fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getLeagueWallet.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getWalletForLeague(leagueId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('withdraw', () => {
|
||||
it('should call apiClient.withdrawFromLeagueWallet with correct parameters', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const amount = 500;
|
||||
const currency = 'USD';
|
||||
const seasonId = 'season-456';
|
||||
const destinationAccount = 'account-789';
|
||||
|
||||
const mockResponse = { success: true, message: 'Withdrawal successful' };
|
||||
mockApiClient.withdrawFromLeagueWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.withdraw(leagueId, amount, currency, seasonId, destinationAccount);
|
||||
|
||||
expect(mockApiClient.withdrawFromLeagueWallet).toHaveBeenCalledWith(leagueId, {
|
||||
amount,
|
||||
currency,
|
||||
seasonId,
|
||||
destinationAccount,
|
||||
});
|
||||
expect(result).toEqual(mockResponse);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.withdrawFromLeagueWallet fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const amount = 500;
|
||||
const currency = 'USD';
|
||||
const seasonId = 'season-456';
|
||||
const destinationAccount = 'account-789';
|
||||
|
||||
const error = new Error('Withdrawal failed');
|
||||
mockApiClient.withdrawFromLeagueWallet.mockRejectedValue(error);
|
||||
|
||||
await expect(service.withdraw(leagueId, amount, currency, seasonId, destinationAccount)).rejects.toThrow('Withdrawal failed');
|
||||
});
|
||||
|
||||
it('should block multiple rapid calls due to throttle', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const amount = 500;
|
||||
const currency = 'USD';
|
||||
const seasonId = 'season-456';
|
||||
const destinationAccount = 'account-789';
|
||||
|
||||
const mockResponse = { success: true };
|
||||
mockApiClient.withdrawFromLeagueWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
// First call should succeed
|
||||
await service.withdraw(leagueId, amount, currency, seasonId, destinationAccount);
|
||||
|
||||
// Reset mock
|
||||
mockApiClient.withdrawFromLeagueWallet.mockClear();
|
||||
|
||||
// Immediate second call should be blocked by throttle and throw error
|
||||
await expect(service.withdraw(leagueId, amount, currency, seasonId, destinationAccount)).rejects.toThrow('Request blocked due to rate limiting');
|
||||
|
||||
expect(mockApiClient.withdrawFromLeagueWallet).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { LeagueWizardService } from './LeagueWizardService';
|
||||
import { LeagueWizardCommandModel } from '@/lib/command-models/leagues/LeagueWizardCommandModel';
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
|
||||
// Mock the apiClient
|
||||
vi.mock('@/lib/apiClient', () => ({
|
||||
apiClient: {
|
||||
leagues: {
|
||||
create: vi.fn(),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
describe('LeagueWizardService', () => {
|
||||
describe('createLeague', () => {
|
||||
it('should call apiClient.leagues.create with correct command', async () => {
|
||||
const form = {
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
toCreateLeagueCommand: vi.fn().mockReturnValue({
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
ownerId: 'owner-123',
|
||||
}),
|
||||
} as unknown as LeagueWizardCommandModel;
|
||||
|
||||
const ownerId = 'owner-123';
|
||||
const mockOutput = { leagueId: 'new-league-id', success: true };
|
||||
|
||||
(apiClient.leagues.create as any).mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await LeagueWizardService.createLeague(form, ownerId);
|
||||
|
||||
expect(form.toCreateLeagueCommand).toHaveBeenCalledWith(ownerId);
|
||||
expect(apiClient.leagues.create).toHaveBeenCalledWith({
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
ownerId: 'owner-123',
|
||||
});
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.leagues.create fails', async () => {
|
||||
const form = {
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
toCreateLeagueCommand: vi.fn().mockReturnValue({
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
ownerId: 'owner-123',
|
||||
}),
|
||||
} as unknown as LeagueWizardCommandModel;
|
||||
|
||||
const ownerId = 'owner-123';
|
||||
const error = new Error('API call failed');
|
||||
|
||||
(apiClient.leagues.create as Mocked<typeof apiClient.leagues.create>).mockRejectedValue(error);
|
||||
|
||||
await expect(LeagueWizardService.createLeague(form, ownerId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createLeagueFromConfig', () => {
|
||||
it('should call createLeague with same parameters', async () => {
|
||||
const form = {
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
toCreateLeagueCommand: vi.fn().mockReturnValue({
|
||||
name: 'Test League',
|
||||
description: 'A test league',
|
||||
ownerId: 'owner-123',
|
||||
}),
|
||||
} as unknown as LeagueWizardCommandModel;
|
||||
|
||||
const ownerId = 'owner-123';
|
||||
const mockOutput = { leagueId: 'new-league-id', success: true };
|
||||
|
||||
(apiClient.leagues.create as Mocked<typeof apiClient.leagues.create>).mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await LeagueWizardService.createLeagueFromConfig(form, ownerId);
|
||||
|
||||
expect(apiClient.leagues.create).toHaveBeenCalled();
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
});
|
||||
});
|
||||
90
apps/website/lib/services/penalties/PenaltyService.test.ts
Normal file
90
apps/website/lib/services/penalties/PenaltyService.test.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { PenaltyService } from './PenaltyService';
|
||||
import { PenaltiesApiClient } from '../../api/penalties/PenaltiesApiClient';
|
||||
|
||||
describe('PenaltyService', () => {
|
||||
let mockApiClient: Mocked<PenaltiesApiClient>;
|
||||
let service: PenaltyService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getRacePenalties: vi.fn(),
|
||||
applyPenalty: vi.fn(),
|
||||
} as Mocked<PenaltiesApiClient>;
|
||||
|
||||
service = new PenaltyService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('findByRaceId', () => {
|
||||
it('should call apiClient.getRacePenalties and return penalties array', async () => {
|
||||
const raceId = 'race-123';
|
||||
const mockDto = {
|
||||
penalties: [
|
||||
{ id: 'penalty-1', driverId: 'driver-1', type: 'time', value: 5, reason: 'Incident' },
|
||||
{ id: 'penalty-2', driverId: 'driver-2', type: 'grid', value: 3, reason: 'Qualifying incident' },
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getRacePenalties.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.findByRaceId(raceId);
|
||||
|
||||
expect(mockApiClient.getRacePenalties).toHaveBeenCalledWith(raceId);
|
||||
expect(result).toEqual(mockDto.penalties);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should handle empty penalties array', async () => {
|
||||
const raceId = 'race-123';
|
||||
const mockDto = { penalties: [] };
|
||||
|
||||
mockApiClient.getRacePenalties.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.findByRaceId(raceId);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
expect(result).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getRacePenalties fails', async () => {
|
||||
const raceId = 'race-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getRacePenalties.mockRejectedValue(error);
|
||||
|
||||
await expect(service.findByRaceId(raceId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyPenalty', () => {
|
||||
it('should call apiClient.applyPenalty with input', async () => {
|
||||
const input = {
|
||||
driverId: 'driver-123',
|
||||
raceId: 'race-456',
|
||||
type: 'time',
|
||||
value: 10,
|
||||
reason: 'Test reason',
|
||||
};
|
||||
|
||||
mockApiClient.applyPenalty.mockResolvedValue(undefined);
|
||||
|
||||
await service.applyPenalty(input);
|
||||
|
||||
expect(mockApiClient.applyPenalty).toHaveBeenCalledWith(input);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.applyPenalty fails', async () => {
|
||||
const input = {
|
||||
driverId: 'driver-123',
|
||||
raceId: 'race-456',
|
||||
type: 'time',
|
||||
value: 10,
|
||||
reason: 'Test reason',
|
||||
};
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.applyPenalty.mockRejectedValue(error);
|
||||
|
||||
await expect(service.applyPenalty(input)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
205
apps/website/lib/services/protests/ProtestService.test.ts
Normal file
205
apps/website/lib/services/protests/ProtestService.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { ProtestService } from './ProtestService';
|
||||
import { ProtestsApiClient } from '../../api/protests/ProtestsApiClient';
|
||||
import { ProtestViewModel } from '../../view-models/ProtestViewModel';
|
||||
import { RaceViewModel } from '../../view-models/RaceViewModel';
|
||||
import { ProtestDriverViewModel } from '../../view-models/ProtestDriverViewModel';
|
||||
|
||||
describe('ProtestService', () => {
|
||||
let mockApiClient: Mocked<ProtestsApiClient>;
|
||||
let service: ProtestService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getLeagueProtests: vi.fn(),
|
||||
getLeagueProtest: vi.fn(),
|
||||
applyPenalty: vi.fn(),
|
||||
requestDefense: vi.fn(),
|
||||
reviewProtest: vi.fn(),
|
||||
getRaceProtests: vi.fn(),
|
||||
} as Mocked<ProtestsApiClient>;
|
||||
|
||||
service = new ProtestService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getLeagueProtests', () => {
|
||||
it('should call apiClient.getLeagueProtests and return transformed data', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const mockDto = {
|
||||
protests: [
|
||||
{
|
||||
id: 'protest-1',
|
||||
protestingDriverId: 'driver-1',
|
||||
accusedDriverId: 'driver-2',
|
||||
incident: { lap: 5, description: 'Contact at turn 1' },
|
||||
filedAt: '2023-10-01T10:00:00Z',
|
||||
status: 'pending',
|
||||
},
|
||||
],
|
||||
racesById: { 'race-1': { id: 'race-1', track: 'Monza' } },
|
||||
driversById: {
|
||||
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtests.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getLeagueProtests(leagueId);
|
||||
|
||||
expect(mockApiClient.getLeagueProtests).toHaveBeenCalledWith(leagueId);
|
||||
expect(result.protests).toHaveLength(1);
|
||||
expect(result.protests[0]).toBeInstanceOf(ProtestViewModel);
|
||||
expect(result.racesById).toEqual(mockDto.racesById);
|
||||
expect(result.driversById).toEqual(mockDto.driversById);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getLeagueProtests fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getLeagueProtests.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getLeagueProtests(leagueId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getProtestById', () => {
|
||||
it('should call apiClient.getLeagueProtest and return transformed data', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-1';
|
||||
const mockDto = {
|
||||
protests: [
|
||||
{
|
||||
id: protestId,
|
||||
protestingDriverId: 'driver-1',
|
||||
accusedDriverId: 'driver-2',
|
||||
incident: { lap: 5, description: 'Contact at turn 1' },
|
||||
filedAt: '2023-10-01T10:00:00Z',
|
||||
status: 'pending',
|
||||
},
|
||||
],
|
||||
racesById: { 'race-1': { id: 'race-1', track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z' } },
|
||||
driversById: {
|
||||
'driver-1': { id: 'driver-1', name: 'Driver 1', avatarUrl: 'https://example.com/avatar1.jpg' },
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2', avatarUrl: 'https://example.com/avatar2.jpg' },
|
||||
},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtest.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getProtestById(leagueId, protestId);
|
||||
|
||||
expect(mockApiClient.getLeagueProtest).toHaveBeenCalledWith(leagueId, protestId);
|
||||
expect(result).not.toBeNull();
|
||||
expect(result?.protest).toBeInstanceOf(ProtestViewModel);
|
||||
expect(result?.race).toBeInstanceOf(RaceViewModel);
|
||||
expect(result?.protestingDriver).toBeInstanceOf(ProtestDriverViewModel);
|
||||
expect(result?.accusedDriver).toBeInstanceOf(ProtestDriverViewModel);
|
||||
});
|
||||
|
||||
it('should return null when protest not found', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-999';
|
||||
const mockDto = {
|
||||
protests: [],
|
||||
racesById: {},
|
||||
driversById: {},
|
||||
};
|
||||
|
||||
mockApiClient.getLeagueProtest.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getProtestById(leagueId, protestId);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getLeagueProtest fails', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const protestId = 'protest-1';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getLeagueProtest.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getProtestById(leagueId, protestId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('applyPenalty', () => {
|
||||
it('should call apiClient.applyPenalty', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
driverId: 'driver-456',
|
||||
type: 'time',
|
||||
value: 10,
|
||||
reason: 'Test reason',
|
||||
};
|
||||
|
||||
mockApiClient.applyPenalty.mockResolvedValue(undefined);
|
||||
|
||||
await service.applyPenalty(input);
|
||||
|
||||
expect(mockApiClient.applyPenalty).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('requestDefense', () => {
|
||||
it('should call apiClient.requestDefense', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
driverId: 'driver-456',
|
||||
message: 'Please provide defense',
|
||||
};
|
||||
|
||||
mockApiClient.requestDefense.mockResolvedValue(undefined);
|
||||
|
||||
await service.requestDefense(input);
|
||||
|
||||
expect(mockApiClient.requestDefense).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('reviewProtest', () => {
|
||||
it('should call apiClient.reviewProtest', async () => {
|
||||
const input = {
|
||||
protestId: 'protest-123',
|
||||
stewardId: 'steward-456',
|
||||
decision: 'upheld',
|
||||
decisionNotes: 'Test notes',
|
||||
};
|
||||
|
||||
mockApiClient.reviewProtest.mockResolvedValue(undefined);
|
||||
|
||||
await service.reviewProtest(input);
|
||||
|
||||
expect(mockApiClient.reviewProtest).toHaveBeenCalledWith(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('findByRaceId', () => {
|
||||
it('should call apiClient.getRaceProtests and return protests array', async () => {
|
||||
const raceId = 'race-123';
|
||||
const mockDto = {
|
||||
protests: [
|
||||
{ id: 'protest-1', protestingDriverId: 'driver-1', accusedDriverId: 'driver-2', status: 'pending' },
|
||||
{ id: 'protest-2', protestingDriverId: 'driver-3', accusedDriverId: 'driver-4', status: 'resolved' },
|
||||
],
|
||||
driverMap: {},
|
||||
};
|
||||
|
||||
mockApiClient.getRaceProtests.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.findByRaceId(raceId);
|
||||
|
||||
expect(mockApiClient.getRaceProtests).toHaveBeenCalledWith(raceId);
|
||||
expect(result).toEqual(mockDto.protests);
|
||||
expect(result).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getRaceProtests fails', async () => {
|
||||
const raceId = 'race-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getRaceProtests.mockRejectedValue(error);
|
||||
|
||||
await expect(service.findByRaceId(raceId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
140
apps/website/lib/services/races/RaceStewardingService.test.ts
Normal file
140
apps/website/lib/services/races/RaceStewardingService.test.ts
Normal file
@@ -0,0 +1,140 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { RaceStewardingService } from './RaceStewardingService';
|
||||
import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { ProtestsApiClient } from '../../api/protests/ProtestsApiClient';
|
||||
import { PenaltiesApiClient } from '../../api/penalties/PenaltiesApiClient';
|
||||
import { RaceStewardingViewModel } from '../../view-models/RaceStewardingViewModel';
|
||||
|
||||
describe('RaceStewardingService', () => {
|
||||
let mockRacesApiClient: Mocked<RacesApiClient>;
|
||||
let mockProtestsApiClient: Mocked<ProtestsApiClient>;
|
||||
let mockPenaltiesApiClient: Mocked<PenaltiesApiClient>;
|
||||
let service: RaceStewardingService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRacesApiClient = {
|
||||
getDetail: vi.fn(),
|
||||
} as Mocked<RacesApiClient>;
|
||||
|
||||
mockProtestsApiClient = {
|
||||
getRaceProtests: vi.fn(),
|
||||
} as Mocked<ProtestsApiClient>;
|
||||
|
||||
mockPenaltiesApiClient = {
|
||||
getRacePenalties: vi.fn(),
|
||||
} as Mocked<PenaltiesApiClient>;
|
||||
|
||||
service = new RaceStewardingService(mockRacesApiClient, mockProtestsApiClient, mockPenaltiesApiClient);
|
||||
});
|
||||
|
||||
describe('getRaceStewardingData', () => {
|
||||
it('should return race stewarding data with all protests and penalties', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const raceDetailDto = {
|
||||
race: {
|
||||
id: raceId,
|
||||
track: 'Monza',
|
||||
scheduledAt: '2023-10-01T10:00:00Z',
|
||||
status: 'completed',
|
||||
},
|
||||
league: {
|
||||
id: 'league-1',
|
||||
name: 'Test League',
|
||||
},
|
||||
};
|
||||
|
||||
const protestsDto = {
|
||||
protests: [
|
||||
{
|
||||
id: 'protest-1',
|
||||
protestingDriverId: 'driver-1',
|
||||
accusedDriverId: 'driver-2',
|
||||
incident: { lap: 5, description: 'Contact at turn 1' },
|
||||
filedAt: '2023-10-01T10:00:00Z',
|
||||
status: 'pending',
|
||||
},
|
||||
{
|
||||
id: 'protest-2',
|
||||
protestingDriverId: 'driver-3',
|
||||
accusedDriverId: 'driver-4',
|
||||
incident: { lap: 10, description: 'Off-track' },
|
||||
filedAt: '2023-10-01T10:05:00Z',
|
||||
status: 'upheld',
|
||||
},
|
||||
],
|
||||
driverMap: {
|
||||
'driver-1': { id: 'driver-1', name: 'Driver 1' },
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
||||
'driver-3': { id: 'driver-3', name: 'Driver 3' },
|
||||
'driver-4': { id: 'driver-4', name: 'Driver 4' },
|
||||
},
|
||||
};
|
||||
|
||||
const penaltiesDto = {
|
||||
penalties: [
|
||||
{ id: 'penalty-1', driverId: 'driver-2', type: 'time', value: 5, reason: 'Incident' },
|
||||
{ id: 'penalty-2', driverId: 'driver-4', type: 'grid', value: 3, reason: 'Qualifying incident' },
|
||||
],
|
||||
driverMap: {
|
||||
'driver-2': { id: 'driver-2', name: 'Driver 2' },
|
||||
'driver-4': { id: 'driver-4', name: 'Driver 4' },
|
||||
},
|
||||
};
|
||||
|
||||
mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
|
||||
mockProtestsApiClient.getRaceProtests.mockResolvedValue(protestsDto as any);
|
||||
mockPenaltiesApiClient.getRacePenalties.mockResolvedValue(penaltiesDto as any);
|
||||
|
||||
const result = await service.getRaceStewardingData(raceId, driverId);
|
||||
|
||||
expect(mockRacesApiClient.getDetail).toHaveBeenCalledWith(raceId, driverId);
|
||||
expect(mockProtestsApiClient.getRaceProtests).toHaveBeenCalledWith(raceId);
|
||||
expect(mockPenaltiesApiClient.getRacePenalties).toHaveBeenCalledWith(raceId);
|
||||
|
||||
expect(result).toBeInstanceOf(RaceStewardingViewModel);
|
||||
expect(result.race).toEqual(raceDetailDto.race);
|
||||
expect(result.league).toEqual(raceDetailDto.league);
|
||||
expect(result.protests).toHaveLength(2);
|
||||
expect(result.penalties).toHaveLength(2);
|
||||
expect(result.pendingProtests).toHaveLength(1);
|
||||
expect(result.resolvedProtests).toHaveLength(1);
|
||||
expect(result.pendingCount).toBe(1);
|
||||
expect(result.resolvedCount).toBe(1);
|
||||
expect(result.penaltiesCount).toBe(2);
|
||||
});
|
||||
|
||||
it('should handle empty protests and penalties', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const raceDetailDto = {
|
||||
race: { id: raceId, track: 'Monza', scheduledAt: '2023-10-01T10:00:00Z', status: 'completed' },
|
||||
league: { id: 'league-1', name: 'Test League' },
|
||||
};
|
||||
|
||||
mockRacesApiClient.getDetail.mockResolvedValue(raceDetailDto as any);
|
||||
mockProtestsApiClient.getRaceProtests.mockResolvedValue({ protests: [], driverMap: {} } as any);
|
||||
mockPenaltiesApiClient.getRacePenalties.mockResolvedValue({ penalties: [], driverMap: {} } as any);
|
||||
|
||||
const result = await service.getRaceStewardingData(raceId, driverId);
|
||||
|
||||
expect(result.protests).toHaveLength(0);
|
||||
expect(result.penalties).toHaveLength(0);
|
||||
expect(result.pendingCount).toBe(0);
|
||||
expect(result.resolvedCount).toBe(0);
|
||||
expect(result.penaltiesCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw error when any API call fails', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockRacesApiClient.getDetail.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getRaceStewardingData(raceId, driverId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -70,6 +70,7 @@
|
||||
"deploy:website:preview": "npx vercel deploy --cwd apps/website",
|
||||
"deploy:website:prod": "npx vercel deploy --prod",
|
||||
"dev": "echo 'Development server placeholder - to be configured'",
|
||||
"lint": "npx eslint apps/api/src --ext .ts,.tsx --max-warnings 0",
|
||||
"docker:dev": "docker-compose -f docker-compose.dev.yml up",
|
||||
"docker:dev:build": "docker-compose -f docker-compose.dev.yml up --build",
|
||||
"docker:dev:clean": "docker-compose -f docker-compose.dev.yml down -v",
|
||||
|
||||
@@ -20,6 +20,7 @@ export default defineConfig({
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, './apps/website'),
|
||||
'@core': path.resolve(__dirname, './core'),
|
||||
'@adapters': path.resolve(__dirname, './adapters'),
|
||||
'@testing': path.resolve(__dirname, './testing'),
|
||||
|
||||
Reference in New Issue
Block a user