feature flags
This commit is contained in:
@@ -15,7 +15,7 @@ export async function getHomeData() {
|
||||
redirect('/dashboard');
|
||||
}
|
||||
|
||||
const featureService = FeatureFlagService.fromEnv();
|
||||
const featureService = await FeatureFlagService.fromAPI();
|
||||
const isAlpha = featureService.isEnabled('alpha_features');
|
||||
const discovery = await landingService.getHomeDiscovery();
|
||||
|
||||
|
||||
@@ -1,190 +0,0 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { RaceService } from './RaceService';
|
||||
import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { RaceDetailViewModel } from '../../view-models/RaceDetailViewModel';
|
||||
import { RacesPageViewModel } from '../../view-models/RacesPageViewModel';
|
||||
import { RaceStatsViewModel } from '../../view-models/RaceStatsViewModel';
|
||||
import type { RaceDetailsViewModel } from '../../view-models/RaceDetailsViewModel';
|
||||
|
||||
describe('RaceService', () => {
|
||||
let mockApiClient: Mocked<RacesApiClient>;
|
||||
let service: RaceService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getDetail: vi.fn(),
|
||||
getPageData: vi.fn(),
|
||||
getTotal: vi.fn(),
|
||||
register: vi.fn(),
|
||||
withdraw: vi.fn(),
|
||||
cancel: vi.fn(),
|
||||
complete: vi.fn(),
|
||||
reopen: vi.fn(),
|
||||
} as unknown as Mocked<RacesApiClient>;
|
||||
|
||||
service = new RaceService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getRaceDetail', () => {
|
||||
it('should call apiClient.getDetail and return RaceDetailViewModel', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const mockDto = {
|
||||
race: { id: raceId, track: 'Test Track' },
|
||||
league: { id: 'league-1', name: 'Test League' },
|
||||
entryList: [],
|
||||
registration: { isRegistered: true, canRegister: false },
|
||||
userResult: null,
|
||||
};
|
||||
|
||||
mockApiClient.getDetail.mockResolvedValue(mockDto as any);
|
||||
|
||||
const result = await service.getRaceDetail(raceId, driverId);
|
||||
|
||||
expect(mockApiClient.getDetail).toHaveBeenCalledWith(raceId, driverId);
|
||||
expect(result).toBeInstanceOf(RaceDetailViewModel);
|
||||
expect(result.race?.id).toBe(raceId);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getDetail fails', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getDetail.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getRaceDetail(raceId, driverId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRaceDetails', () => {
|
||||
it('should call apiClient.getDetail and return a ViewModel-shaped object (no DTOs)', async () => {
|
||||
const raceId = 'race-123';
|
||||
const driverId = 'driver-456';
|
||||
|
||||
const mockDto = {
|
||||
race: {
|
||||
id: raceId,
|
||||
track: 'Test Track',
|
||||
car: 'Test Car',
|
||||
scheduledAt: '2023-12-31T20:00:00Z',
|
||||
status: 'completed',
|
||||
sessionType: 'race',
|
||||
},
|
||||
league: { id: 'league-1', name: 'Test League', description: 'Desc', settings: { maxDrivers: 32 } },
|
||||
entryList: [],
|
||||
registration: { isUserRegistered: true, canRegister: false },
|
||||
userResult: null,
|
||||
};
|
||||
|
||||
mockApiClient.getDetail.mockResolvedValue(mockDto as any);
|
||||
|
||||
const result: RaceDetailsViewModel = await service.getRaceDetails(raceId, driverId);
|
||||
|
||||
expect(mockApiClient.getDetail).toHaveBeenCalledWith(raceId, driverId);
|
||||
expect(result.race?.id).toBe(raceId);
|
||||
expect(result.league?.id).toBe('league-1');
|
||||
expect(result.registration.isUserRegistered).toBe(true);
|
||||
expect(result.canReopenRace).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRacesPageData', () => {
|
||||
it('should call apiClient.getPageData and return RacesPageViewModel with transformed data', async () => {
|
||||
const mockDto = {
|
||||
races: [
|
||||
{
|
||||
id: 'race-1',
|
||||
track: 'Monza',
|
||||
car: 'Ferrari',
|
||||
scheduledAt: '2023-10-01T10:00:00Z',
|
||||
status: 'upcoming',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'Test League',
|
||||
},
|
||||
{
|
||||
id: 'race-2',
|
||||
track: 'Silverstone',
|
||||
car: 'Mercedes',
|
||||
scheduledAt: '2023-09-15T10:00:00Z',
|
||||
status: 'completed',
|
||||
leagueId: 'league-1',
|
||||
leagueName: 'Test League',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getPageData.mockResolvedValue(mockDto as any);
|
||||
|
||||
const result = await service.getRacesPageData();
|
||||
|
||||
expect(mockApiClient.getPageData).toHaveBeenCalled();
|
||||
expect(result).toBeInstanceOf(RacesPageViewModel);
|
||||
expect(result.upcomingRaces).toHaveLength(1);
|
||||
expect(result.completedRaces).toHaveLength(1);
|
||||
expect(result.totalCount).toBe(2);
|
||||
expect(result.upcomingRaces[0].title).toBe('Monza - Ferrari');
|
||||
expect(result.completedRaces[0].title).toBe('Silverstone - Mercedes');
|
||||
});
|
||||
|
||||
it('should handle empty races array', async () => {
|
||||
const mockDto = { races: [] };
|
||||
|
||||
mockApiClient.getPageData.mockResolvedValue(mockDto as any);
|
||||
|
||||
const result = await service.getRacesPageData();
|
||||
|
||||
expect(result.upcomingRaces).toHaveLength(0);
|
||||
expect(result.completedRaces).toHaveLength(0);
|
||||
expect(result.totalCount).toBe(0);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getPageData fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getPageData.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getRacesPageData()).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRacesTotal', () => {
|
||||
it('should call apiClient.getTotal and return RaceStatsViewModel', async () => {
|
||||
const mockDto = { totalRaces: 42 };
|
||||
|
||||
mockApiClient.getTotal.mockResolvedValue(mockDto as any);
|
||||
|
||||
const result = await service.getRacesTotal();
|
||||
|
||||
expect(mockApiClient.getTotal).toHaveBeenCalled();
|
||||
expect(result).toBeInstanceOf(RaceStatsViewModel);
|
||||
expect(result.totalRaces).toBe(42);
|
||||
expect(result.formattedTotalRaces).toBe('42');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getTotal fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getTotal.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getRacesTotal()).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('reopenRace', () => {
|
||||
it('should call apiClient.reopen with raceId', async () => {
|
||||
const raceId = 'race-123';
|
||||
|
||||
await service.reopenRace(raceId);
|
||||
|
||||
expect(mockApiClient.reopen).toHaveBeenCalledWith(raceId);
|
||||
});
|
||||
|
||||
it('should propagate errors from apiClient.reopen', async () => {
|
||||
const raceId = 'race-123';
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.reopen.mockRejectedValue(error);
|
||||
|
||||
await expect(service.reopenRace(raceId)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user