website refactor

This commit is contained in:
2026-01-21 00:53:29 +01:00
parent 4516427a19
commit 5f3712e5ab
10 changed files with 85 additions and 201 deletions

View File

@@ -32,8 +32,6 @@ import { InMemoryDriverExtendedProfileProvider } from '@adapters/racing/ports/In
import { InMemoryDriverRatingProvider } from '@adapters/racing/ports/InMemoryDriverRatingProvider';
// Import new use cases
// Import new repositories
import { InMemoryDriverStatsRepository } from '@adapters/racing/persistence/inmemory/InMemoryDriverStatsRepository';
import { InMemoryMediaRepository } from '@adapters/racing/persistence/media/InMemoryMediaRepository';
// Import MediaResolverAdapter
import { MediaResolverAdapter } from '@adapters/media/MediaResolverAdapter';
// Import repository tokens
@@ -67,7 +65,6 @@ import {
IS_DRIVER_REGISTERED_FOR_RACE_USE_CASE_TOKEN,
LIVERY_REPOSITORY_TOKEN,
LOGGER_TOKEN,
MEDIA_REPOSITORY_TOKEN,
MEDIA_RESOLVER_TOKEN,
NOTIFICATION_PREFERENCE_REPOSITORY_TOKEN,
RACE_REGISTRATION_REPOSITORY_TOKEN,
@@ -133,48 +130,25 @@ export const DriverProviders: Provider[] = createLoggedProviders([
},
// Repositories (racing + social repos are provided by imported persistence modules)
{
provide: DRIVER_STATS_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => new InMemoryDriverStatsRepository(logger),
inject: [LOGGER_TOKEN],
},
{
provide: MEDIA_REPOSITORY_TOKEN,
useFactory: (logger: Logger) => {
const mediaRepo = new InMemoryMediaRepository(logger);
// Override getTeamLogo to provide fallback URLs
const originalGetTeamLogo = mediaRepo.getTeamLogo.bind(mediaRepo);
mediaRepo.getTeamLogo = async (teamId: string): Promise<string | null> => {
const logo = await originalGetTeamLogo(teamId);
if (logo) return logo;
// Fallback: generate deterministic team logo URL
// Use path-only URL
return `/media/teams/${teamId}/logo`;
};
return mediaRepo;
},
inject: [LOGGER_TOKEN],
},
{
provide: RANKING_SERVICE_TOKEN,
useFactory: (
standingRepo: StandingRepository,
driverRepo: DriverRepository,
driverStatsRepo: DriverStatsRepository,
logger: Logger
) => new RankingUseCase(standingRepo, driverRepo, logger),
inject: ['IStandingRepository', DRIVER_REPOSITORY_TOKEN, LOGGER_TOKEN],
) => new RankingUseCase(standingRepo, driverRepo, driverStatsRepo, logger),
inject: ['IStandingRepository', DRIVER_REPOSITORY_TOKEN, DRIVER_STATS_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: DRIVER_STATS_SERVICE_TOKEN,
useFactory: (
resultRepo: ResultRepository,
standingRepo: StandingRepository,
driverStatsRepo: DriverStatsRepository,
logger: Logger
) => new DriverStatsUseCase(resultRepo, standingRepo, logger),
inject: ['IResultRepository', 'IStandingRepository', LOGGER_TOKEN],
) => new DriverStatsUseCase(resultRepo, standingRepo, driverStatsRepo, logger),
inject: ['IResultRepository', 'IStandingRepository', DRIVER_STATS_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
{
provide: DRIVER_RATING_PROVIDER_TOKEN,

View File

@@ -88,6 +88,27 @@ describe('Team domain (HTTP, module-wiring)', () => {
.expect(200);
});
it('returns leaderboard with non-zero ratings and wins', async () => {
const response = await request(app.getHttpServer())
.get('/teams/leaderboard')
.expect(200);
expect(response.body).toBeDefined();
expect(response.body.teams).toBeDefined();
expect(Array.isArray(response.body.teams)).toBe(true);
// Verify that teams have non-zero ratings and wins
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);
}
});
it('rejects unauthenticated actor on create team (401)', async () => {
await request(app.getHttpServer())
.post('/teams')

View File

@@ -19,6 +19,7 @@ import {
TEAM_STATS_REPOSITORY_TOKEN,
UPDATE_TEAM_USE_CASE_TOKEN,
GET_TEAMS_LEADERBOARD_USE_CASE_TOKEN,
DRIVER_STATS_REPOSITORY_TOKEN,
} from './TeamTokens';
export {
@@ -165,11 +166,15 @@ export const TeamProviders: Provider[] = [
},
{
provide: GET_TEAMS_LEADERBOARD_USE_CASE_TOKEN,
useFactory: (teamRepo: TeamRepository, membershipRepo: TeamMembershipRepository, driverStatsRepo: DriverStatsRepository, logger: Logger) =>
new GetTeamsLeaderboardUseCase(teamRepo, membershipRepo, (driverId) => {
const stats = driverStatsRepo.getDriverStatsSync?.(driverId);
useFactory: async (teamRepo: TeamRepository, membershipRepo: TeamMembershipRepository, driverStatsRepo: DriverStatsRepository, logger: Logger) => {
// Pre-fetch all driver stats for efficient lookup
const allStats = await driverStatsRepo.getAllStats();
return new GetTeamsLeaderboardUseCase(teamRepo, membershipRepo, (driverId) => {
const stats = allStats.get(driverId);
return stats ? { rating: stats.rating, wins: stats.wins, totalRaces: stats.totalRaces } : null;
}, logger),
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, 'IDriverStatsRepository', LOGGER_TOKEN],
}, logger);
},
inject: [TEAM_REPOSITORY_TOKEN, TEAM_MEMBERSHIP_REPOSITORY_TOKEN, DRIVER_STATS_REPOSITORY_TOKEN, LOGGER_TOKEN],
},
];

View File

@@ -7,6 +7,7 @@ export const TEAM_STATS_REPOSITORY_TOKEN = 'ITeamStatsRepository';
export const MEDIA_REPOSITORY_TOKEN = 'IMediaRepository';
export const MEDIA_RESOLVER_TOKEN = 'MediaResolverPort';
export const RESULT_REPOSITORY_TOKEN = 'IResultRepository';
export const DRIVER_STATS_REPOSITORY_TOKEN = 'IDriverStatsRepository';
export const GET_ALL_TEAMS_USE_CASE_TOKEN = Symbol('GET_ALL_TEAMS_USE_CASE_TOKEN');
export const GET_TEAM_DETAILS_USE_CASE_TOKEN = Symbol('GET_TEAM_DETAILS_USE_CASE_TOKEN');