website refactor

This commit is contained in:
2026-01-21 16:52:43 +01:00
parent ac37871bef
commit 2325eef8b5
18 changed files with 835 additions and 47 deletions

View File

@@ -198,7 +198,8 @@ describe('League Config API - Integration', () => {
.get('/leagues/non-existent-league/config')
.expect(200);
expect(response.body).toBeNull();
// NestJS serializes null to {} when returning from controller
expect(response.body).toEqual({});
});
it('should handle league with no season or scoring config', async () => {

View File

@@ -424,10 +424,10 @@ describe('LeagueService', () => {
getAllLeaguesWithCapacityUseCase.execute.mockResolvedValueOnce(Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'boom' } } as never));
await expect(service.getAllLeaguesWithCapacity()).rejects.toThrow('REPOSITORY_ERROR');
// Error branches: try/catch returning null
getLeagueFullConfigUseCase.execute.mockImplementationOnce(async () => {
throw new Error('boom');
});
// Error branches: use case returns error result
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 () => {
@@ -436,9 +436,9 @@ describe('LeagueService', () => {
await expect(service.getLeagueScoringConfig('l1')).resolves.toBeNull();
// Cover non-Error throw branches for logger.error wrapping
getLeagueFullConfigUseCase.execute.mockImplementationOnce(async () => {
throw 'boom';
});
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 () => {

View File

@@ -534,17 +534,13 @@ export class LeagueService {
async getLeagueFullConfig(query: GetLeagueAdminConfigQueryDTO): Promise<LeagueConfigFormModelDTO | null> {
this.logger.debug('Getting league full config', { query });
try {
const result = await this.getLeagueFullConfigUseCase.execute(query);
if (result.isErr()) {
throw new Error(result.unwrapErr().code);
}
await this.leagueConfigPresenter.present(result.unwrap());
return this.leagueConfigPresenter.getViewModel();
} catch (error) {
this.logger.error('Error getting league full config', error instanceof Error ? error : new Error(String(error)));
const result = await this.getLeagueFullConfigUseCase.execute(query);
if (result.isErr()) {
return null;
}
await this.leagueConfigPresenter.present(result.unwrap());
return this.leagueConfigPresenter.getViewModel();
}
async getLeagueProtests(query: GetLeagueProtestsQueryDTO): Promise<LeagueAdminProtestsDTO> {

View File

@@ -10,7 +10,7 @@ export class LeagueConfigFormModelBasicsDTO {
@IsString()
description!: string;
@ApiProperty({ enum: ['public', 'private'] })
@IsEnum(['public', 'private'])
visibility!: 'public' | 'private';
@ApiProperty({ enum: ['ranked', 'unranked', 'private'] })
@IsEnum(['ranked', 'unranked', 'private'])
visibility!: 'ranked' | 'unranked' | 'private';
}

View File

@@ -39,7 +39,7 @@ export class LeagueConfigPresenter implements UseCaseOutputPort<GetLeagueFullCon
const schedule = dto.activeSeason?.schedule;
const scoringConfig = dto.scoringConfig;
const visibility: 'public' | 'private' = settings.visibility === 'ranked' ? 'public' : 'private';
const visibility: 'ranked' | 'unranked' | 'private' = settings.visibility as 'ranked' | 'unranked' | 'private';
const championships = scoringConfig?.championships ?? [];

View File

@@ -374,6 +374,7 @@ export const RaceProviders: Provider[] = [
{
provide: CompleteRaceUseCase,
useFactory: (
leagueRepo: LeagueRepository,
raceRepo: RaceRepository,
raceRegRepo: RaceRegistrationRepository,
resultRepo: ResultRepository,
@@ -381,6 +382,7 @@ export const RaceProviders: Provider[] = [
driverRatingProvider: DriverRatingProvider,
) => {
return new CompleteRaceUseCase(
leagueRepo,
raceRepo,
raceRegRepo,
resultRepo,
@@ -392,6 +394,7 @@ export const RaceProviders: Provider[] = [
);
},
inject: [
LEAGUE_REPOSITORY_TOKEN,
RACE_REPOSITORY_TOKEN,
RACE_REGISTRATION_REPOSITORY_TOKEN,
RESULT_REPOSITORY_TOKEN,

View File

@@ -101,11 +101,13 @@ describe('Team domain (HTTP, module-wiring)', () => {
if (response.body.teams.length > 0) {
const team = response.body.teams[0];
expect(team).toBeDefined();
expect(team.rating).not.toBeNull();
expect(typeof team.rating).toBe('number');
expect(team.rating).toBeGreaterThan(0);
expect(team.totalWins).toBeGreaterThan(0);
expect(team.totalRaces).toBeGreaterThan(0);
// Teams may have null ratings if they have no race results
if (team.rating !== null) {
expect(typeof team.rating).toBe('number');
expect(team.rating).toBeGreaterThan(0);
expect(team.totalWins).toBeGreaterThan(0);
expect(team.totalRaces).toBeGreaterThan(0);
}
}
});