website refactor

This commit is contained in:
2026-01-16 18:21:06 +01:00
parent 2f53727702
commit 095885544b
146 changed files with 970 additions and 524 deletions

View File

@@ -1,5 +1,4 @@
import type { ApplicationError } from '@core/shared/errors/ApplicationError';
import type { CommonApplicationErrorKind } from '@core/shared/errors/ApplicationError';
import type { ApplicationError, CommonApplicationErrorKind } from '@core/shared/errors/ApplicationError';
export abstract class RacingApplicationError
extends Error

View File

@@ -2,15 +2,16 @@
* Tests for GetTeamRatingLedgerQuery
*/
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { GetTeamRatingLedgerQuery, GetTeamRatingLedgerQueryHandler } from './GetTeamRatingLedgerQuery';
import { TeamRatingEvent } from '../../domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
import type { TeamRatingEventRepository } from '../../domain/repositories/TeamRatingEventRepository';
describe('GetTeamRatingLedgerQuery', () => {
let mockRatingEventRepo: any;
let mockRatingEventRepo: { findEventsPaginated: Mock };
let handler: GetTeamRatingLedgerQueryHandler;
beforeEach(() => {
@@ -18,7 +19,7 @@ describe('GetTeamRatingLedgerQuery', () => {
findEventsPaginated: vi.fn(),
};
handler = new GetTeamRatingLedgerQueryHandler(mockRatingEventRepo);
handler = new GetTeamRatingLedgerQueryHandler(mockRatingEventRepo as unknown as TeamRatingEventRepository);
});
describe('execute', () => {

View File

@@ -2,7 +2,7 @@
* Tests for GetTeamRatingsSummaryQuery
*/
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { GetTeamRatingsSummaryQuery, GetTeamRatingsSummaryQueryHandler } from './GetTeamRatingsSummaryQuery';
import { TeamRatingSnapshot } from '../../domain/services/TeamRatingSnapshotCalculator';
import { TeamRatingValue } from '../../domain/value-objects/TeamRatingValue';
@@ -10,10 +10,12 @@ import { TeamRatingEvent } from '../../domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
import type { TeamRatingRepository } from '../../domain/repositories/TeamRatingRepository';
import type { TeamRatingEventRepository } from '../../domain/repositories/TeamRatingEventRepository';
describe('GetTeamRatingsSummaryQuery', () => {
let mockTeamRatingRepo: any;
let mockRatingEventRepo: any;
let mockTeamRatingRepo: { findByTeamId: Mock };
let mockRatingEventRepo: { getAllByTeamId: Mock };
let handler: GetTeamRatingsSummaryQueryHandler;
beforeEach(() => {
@@ -25,8 +27,8 @@ describe('GetTeamRatingsSummaryQuery', () => {
};
handler = new GetTeamRatingsSummaryQueryHandler(
mockTeamRatingRepo,
mockRatingEventRepo
mockTeamRatingRepo as unknown as TeamRatingRepository,
mockRatingEventRepo as unknown as TeamRatingEventRepository
);
});

View File

@@ -6,9 +6,15 @@
*/
import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonSponsorship } from '../../domain/entities/season/SeasonSponsorship';
import { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { WalletRepository } from '@core/payments/domain/repositories/WalletRepository';
import { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
export interface AcceptSponsorshipRequestInput {
requestId: string;

View File

@@ -5,6 +5,8 @@ import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
import { TeamRating } from '../../domain/entities/TeamRating';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
// Mock repositories
class MockTeamRatingEventRepository implements TeamRatingEventRepository {
@@ -27,7 +29,7 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
return this.events.filter(e => e.teamId === teamId);
}
async findEventsPaginated(teamId: string): Promise<any> {
async findEventsPaginated(teamId: string): Promise<PaginatedResult<TeamRatingEvent>> {
const events = await this.getAllByTeamId(teamId);
return {
items: events,
@@ -44,13 +46,13 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
}
class MockTeamRatingRepository implements TeamRatingRepository {
private snapshots: Map<string, any> = new Map();
private snapshots: Map<string, TeamRating> = new Map();
async findByTeamId(teamId: string): Promise<any | null> {
async findByTeamId(teamId: string): Promise<TeamRating | null> {
return this.snapshots.get(teamId) || null;
}
async save(snapshot: any): Promise<any> {
async save(snapshot: TeamRating): Promise<TeamRating> {
this.snapshots.set(snapshot.teamId, snapshot);
return snapshot;
}

View File

@@ -5,11 +5,14 @@
* (driver, team, race, or season/league).
*/
import { Result } from '@/shared/domain/Result';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import { Money, isCurrency } from '../../domain/value-objects/Money';
import { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import { SponsorRepository } from '../../domain/repositories/SponsorRepository';
export interface ApplyForSponsorshipInput {
sponsorId: string;

View File

@@ -1,5 +1,10 @@
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { ApplyPenaltyUseCase } from './ApplyPenaltyUseCase';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
describe('ApplyPenaltyUseCase', () => {
let mockPenaltyRepo: {
@@ -43,11 +48,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when race does not exist', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue(null);
@@ -65,11 +72,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when steward does not have authority', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -95,11 +104,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when protest does not exist', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -127,11 +138,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when protest is not upheld', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -159,11 +172,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when protest is not for this race', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -191,11 +206,13 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should create penalty and return result on success', async () => {
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
const useCase = new ApplyPenaltyUseCase(
mockPenaltyRepo as unknown as PenaltyRepository,
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
mockLogger as unknown as Logger
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -223,7 +240,7 @@ describe('ApplyPenaltyUseCase', () => {
expect(presented.penaltyId).toBeDefined();
expect(mockPenaltyRepo.create).toHaveBeenCalledTimes(1);
const createdPenalty = (mockPenaltyRepo.create as Mock).mock.calls[0]?.[0] as any;
const createdPenalty = (mockPenaltyRepo.create as Mock).mock.calls[0]?.[0] as Penalty;
type ToStringable = { toString(): string };
const asString = (value: unknown): string => {

View File

@@ -5,11 +5,15 @@
* The penalty can be standalone or linked to an upheld protest.
*/
import { Result } from '@/shared/domain/Result';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { randomUUID } from 'crypto';
import { Penalty } from '../../domain/entities/penalty/Penalty';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export interface ApplyPenaltyInput {
raceId: string;
@@ -62,7 +66,7 @@ export class ApplyPenaltyUseCase {
// Validate steward has authority (owner or admin of the league)
const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
const stewardMembership = memberships.find(
m => m.driverId.toString() === command.stewardId && m.status.toString() === 'active'
(m) => m.driverId.toString() === command.stewardId && m.status.toString() === 'active'
);
if (!stewardMembership || (stewardMembership.role.toString() !== 'owner' && stewardMembership.role.toString() !== 'admin')) {
@@ -110,7 +114,7 @@ export class ApplyPenaltyUseCase {
`ApplyPenaltyUseCase: Successfully applied penalty ${penalty.id} for driver ${command.driverId} in race ${command.raceId}.`,
);
const result: ApplyPenaltyResult = { penaltyId: penalty.id };
const result: ApplyPenaltyResult = { penaltyId: penalty.id.toString() };
return Result.ok(result);
}

View File

@@ -3,11 +3,7 @@ import type { LeagueRepository } from '../../domain/repositories/LeagueRepositor
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { randomUUID } from 'crypto';
import { JoinedAt } from '../../domain/value-objects/JoinedAt';
import { LeagueId } from '../../domain/entities/LeagueId';
import { DriverId } from '../../domain/entities/DriverId';
import { MembershipRole } from '../../domain/entities/MembershipRole';
import { MembershipStatus } from '../../domain/entities/MembershipStatus';
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
export interface ApproveLeagueJoinRequestInput {
leagueId: string;
@@ -55,14 +51,16 @@ export class ApproveLeagueJoinRequestUseCase {
}
await this.leagueMembershipRepository.removeJoinRequest(input.joinRequestId);
await this.leagueMembershipRepository.saveMembership({
id: randomUUID(),
leagueId: LeagueId.create(input.leagueId),
driverId: DriverId.create(request.driverId.toString()),
role: MembershipRole.create('member'),
status: MembershipStatus.create('active'),
joinedAt: JoinedAt.create(new Date()),
});
await this.leagueMembershipRepository.saveMembership(
LeagueMembership.create({
id: randomUUID(),
leagueId: input.leagueId,
driverId: request.driverId.toString(),
role: 'member',
status: 'active',
joinedAt: new Date(),
})
);
const result: ApproveLeagueJoinRequestResult = { success: true, message: 'Join request approved.' };

View File

@@ -2,6 +2,8 @@ import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { Race } from '../../domain/entities/Race';
import { SessionType } from '../../domain/value-objects/SessionType';
import { CancelRaceUseCase } from './CancelRaceUseCase';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/domain/Logger';
describe('CancelRaceUseCase', () => {
let useCase: CancelRaceUseCase;
@@ -27,8 +29,8 @@ describe('CancelRaceUseCase', () => {
info: vi.fn(),
error: vi.fn(),
};
useCase = new CancelRaceUseCase(raceRepository as any,
logger as any);
useCase = new CancelRaceUseCase(raceRepository as unknown as RaceRepository,
logger as unknown as Logger);
});
it('should cancel race successfully', async () => {

View File

@@ -1,6 +1,8 @@
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Race } from '../../domain/entities/Race';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
export type CancelRaceInput = {
raceId: string;

View File

@@ -3,6 +3,11 @@ import { RaceEvent } from '../../domain/entities/RaceEvent';
import { Session } from '../../domain/entities/Session';
import { SessionType } from '../../domain/value-objects/SessionType';
import { CloseRaceEventStewardingUseCase } from './CloseRaceEventStewardingUseCase';
import { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { DomainEventPublisher } from '@core/shared/domain/DomainEvent';
import type { Logger } from '@core/shared/domain/Logger';
describe('CloseRaceEventStewardingUseCase', () => {
let useCase: CloseRaceEventStewardingUseCase;
@@ -42,11 +47,11 @@ describe('CloseRaceEventStewardingUseCase', () => {
logger = {
error: vi.fn(),
};
useCase = new CloseRaceEventStewardingUseCase(logger as any,
raceEventRepository as any,
raceRegistrationRepository as any,
penaltyRepository as any,
domainEventPublisher as any);
useCase = new CloseRaceEventStewardingUseCase(logger as unknown as Logger,
raceEventRepository as unknown as RaceEventRepository,
raceRegistrationRepository as unknown as RaceRegistrationRepository,
penaltyRepository as unknown as PenaltyRepository,
domainEventPublisher as unknown as DomainEventPublisher);
});
it('should close stewarding for expired events successfully', async () => {

View File

@@ -1,9 +1,12 @@
import { DomainEventPublisher } from '@/shared/domain/DomainEvent';
import { DomainEventPublisher } from '@core/shared/domain/DomainEvent';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceEvent } from '../../domain/entities/RaceEvent';
import { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
import { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
export type CloseRaceEventStewardingInput = {
raceId: string;

View File

@@ -2,7 +2,7 @@ import type { DriverRepository } from '../../domain/repositories/DriverRepositor
import { Driver } from '../../domain/entities/Driver';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application/Logger';
import type { Logger } from '@core/shared/domain/Logger';
export interface CompleteDriverOnboardingInput {
userId: string;

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { CompleteRaceUseCase, type CompleteRaceInput, type CompleteRaceResult } from './CompleteRaceUseCase';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { CompleteRaceUseCase, type CompleteRaceInput } from './CompleteRaceUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
@@ -39,10 +39,10 @@ describe('CompleteRaceUseCase', () => {
save: vi.fn(),
};
getDriverRating = vi.fn();
useCase = new CompleteRaceUseCase(raceRepository as any,
raceRegistrationRepository as any,
resultRepository as any,
standingRepository as any,
useCase = new CompleteRaceUseCase(raceRepository as unknown as RaceRepository,
raceRegistrationRepository as unknown as RaceRegistrationRepository,
resultRepository as unknown as ResultRepository,
standingRepository as unknown as StandingRepository,
getDriverRating);
});

View File

@@ -4,7 +4,16 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
import { Race } from '../../domain/entities/Race';
import type { Season } from '../../domain/entities/season/Season';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
export interface CreateLeagueSeasonScheduleRaceInput {
leagueId: string;
seasonId: string;
track: string;
car: string;
scheduledAt: Date;
}
export type CreateLeagueSeasonScheduleRaceResult = {
raceId: string;
@@ -88,6 +97,9 @@ export class CreateLeagueSeasonScheduleRaceUseCase {
}
private isWithinSeasonWindow(season: Season, scheduledAt: Date): boolean {
if (!season.startDate || !season.endDate) {
return true;
}
return scheduledAt >= season.startDate && scheduledAt <= season.endDate;
}
}

View File

@@ -1,4 +1,4 @@
import { League } from '@/racing/domain/entities/League';
import { League } from '../../domain/entities/League';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -13,6 +13,9 @@ import {
MIN_RANKED_LEAGUE_DRIVERS,
} from '../../domain/value-objects/LeagueVisibility';
import { PointsTable } from '../../domain/value-objects/PointsTable';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
export type CreateLeagueWithSeasonAndScoringCommand = {
name: string;

View File

@@ -1,15 +1,12 @@
import { describe, it, expect, vi, Mock, beforeEach } from 'vitest';
import { describe, it, expect, vi, type Mock, beforeEach } from 'vitest';
import { Season } from '@core/racing/domain/entities/season/Season';
import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository';
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
import {
CreateSeasonForLeagueUseCase,
type CreateSeasonForLeagueInput,
type CreateSeasonForLeagueResult,
type LeagueConfigFormModel,
} from '@core/racing/application/use-cases/CreateSeasonForLeagueUseCase';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result } from '@core/shared/domain/Result';
function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>): LeagueConfigFormModel {
return {
@@ -68,13 +65,18 @@ function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>)
};
}
type CreateSeasonErrorCode = ApplicationErrorCode<'LEAGUE_NOT_FOUND' | 'VALIDATION_ERROR' | 'REPOSITORY_ERROR'> & {
details?: { message: string };
};
describe('CreateSeasonForLeagueUseCase', () => {
const mockLeagueFindById = vi.fn();
const mockLeagueRepo: any = {
const mockLeagueRepo: {
findById: Mock;
findAll: Mock;
findByOwnerId: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
searchByName: Mock;
} = {
findById: mockLeagueFindById,
findAll: vi.fn(),
findByOwnerId: vi.fn(),
@@ -87,7 +89,15 @@ describe('CreateSeasonForLeagueUseCase', () => {
const mockSeasonFindById = vi.fn();
const mockSeasonAdd = vi.fn();
const mockSeasonRepo: any = {
const mockSeasonRepo: {
findById: Mock;
findByLeagueId: Mock;
create: Mock;
add: Mock;
update: Mock;
listByLeague: Mock;
listActiveByLeague: Mock;
} = {
findById: mockSeasonFindById,
findByLeagueId: vi.fn(),
create: vi.fn(),
@@ -105,7 +115,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
mockSeasonAdd.mockResolvedValue(undefined);
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
const useCase = new CreateSeasonForLeagueUseCase(
mockLeagueRepo as unknown as LeagueRepository,
mockSeasonRepo as unknown as SeasonRepository
);
const config = createLeagueConfigFormModel({
basics: {
@@ -157,7 +170,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
mockSeasonFindById.mockResolvedValue(sourceSeason);
mockSeasonAdd.mockResolvedValue(undefined);
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
const useCase = new CreateSeasonForLeagueUseCase(
mockLeagueRepo as unknown as LeagueRepository,
mockSeasonRepo as unknown as SeasonRepository
);
const command: CreateSeasonForLeagueInput = {
leagueId: 'league-1',
@@ -177,7 +193,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
it('returns error when league not found', async () => {
mockLeagueFindById.mockResolvedValue(null);
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
const useCase = new CreateSeasonForLeagueUseCase(
mockLeagueRepo as unknown as LeagueRepository,
mockSeasonRepo as unknown as SeasonRepository
);
const command: CreateSeasonForLeagueInput = {
leagueId: 'missing-league',
@@ -197,7 +216,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
mockSeasonFindById.mockResolvedValue(undefined);
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
const useCase = new CreateSeasonForLeagueUseCase(
mockLeagueRepo as unknown as LeagueRepository,
mockSeasonRepo as unknown as SeasonRepository
);
const command: CreateSeasonForLeagueInput = {
leagueId: 'league-1',

View File

@@ -3,11 +3,12 @@
*
* Creates a new sponsor.
*/
import { ApplicationErrorCode } from '@/shared/errors/ApplicationErrorCode';
import { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import { v4 as uuidv4 } from 'uuid';
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
import { SponsorRepository } from '../../domain/repositories/SponsorRepository';
export interface CreateSponsorInput {
name: string;

View File

@@ -3,6 +3,9 @@ import {
CreateTeamUseCase,
type CreateTeamInput
} from './CreateTeamUseCase';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
describe('CreateTeamUseCase', () => {
let useCase: CreateTeamUseCase;
@@ -34,9 +37,9 @@ describe('CreateTeamUseCase', () => {
warn: vi.fn(),
error: vi.fn(),
};
useCase = new CreateTeamUseCase(teamRepository as any,
membershipRepository as any,
logger as any);
useCase = new CreateTeamUseCase(teamRepository as unknown as TeamRepository,
membershipRepository as unknown as TeamMembershipRepository,
logger as unknown as Logger);
});
it('should create team successfully', async () => {

View File

@@ -3,7 +3,7 @@
*
* Creates a new team.
*/
import { Result } from '@/shared/domain/Result';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
@@ -13,6 +13,9 @@ import type {
TeamMembershipStatus,
TeamRole,
} from '../../domain/types/TeamMembership';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
export interface CreateTeamInput {
name: string;
tag: string;

View File

@@ -1,8 +1,14 @@
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
export interface DeleteLeagueSeasonScheduleRaceInput {
leagueId: string;
seasonId: string;
raceId: string;
}
export type DeleteLeagueSeasonScheduleRaceResult = {
success: true;

View File

@@ -3,7 +3,8 @@ import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import { FileProtestUseCase, type FileProtestErrorCode, type FileProtestInput, type FileProtestResult } from './FileProtestUseCase';
import { FileProtestUseCase, type FileProtestErrorCode, type FileProtestInput } from './FileProtestUseCase';
import { Protest } from '../../domain/entities/Protest';
describe('FileProtestUseCase', () => {
let mockProtestRepo: {
@@ -29,9 +30,11 @@ describe('FileProtestUseCase', () => {
});
it('should return error when race does not exist', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
const useCase = new FileProtestUseCase(
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
);
mockRaceRepo.findById.mockResolvedValue(null);
@@ -49,9 +52,11 @@ describe('FileProtestUseCase', () => {
});
it('should return error when protesting against self', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
const useCase = new FileProtestUseCase(
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -69,9 +74,11 @@ describe('FileProtestUseCase', () => {
});
it('should return error when protesting driver is not an active member', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
const useCase = new FileProtestUseCase(
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
@@ -92,9 +99,11 @@ describe('FileProtestUseCase', () => {
});
it('should create protest and return protestId on success', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
const useCase = new FileProtestUseCase(
mockProtestRepo as unknown as ProtestRepository,
mockRaceRepo as unknown as RaceRepository,
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
@@ -114,7 +123,7 @@ describe('FileProtestUseCase', () => {
expect(result.isOk()).toBe(true);
const presented = result.unwrap();
expect(mockProtestRepo.create).toHaveBeenCalledTimes(1);
const created = (mockProtestRepo.create as unknown as Mock).mock.calls[0]?.[0] as any;
const created = (mockProtestRepo.create as unknown as Mock).mock.calls[0]?.[0] as Protest;
expect(created.raceId.toString()).toBe('race1');
expect(created.protestingDriverId.toString()).toBe('driver1');

View File

@@ -27,11 +27,11 @@ describe('GetAllLeaguesWithCapacityAndScoringUseCase', () => {
it('should return enriched leagues with capacity and scoring', async () => {
const useCase = new GetAllLeaguesWithCapacityAndScoringUseCase(
mockLeagueRepo as any,
mockMembershipRepo as any,
mockSeasonRepo as any,
mockScoringConfigRepo as any,
mockGameRepo as any,
mockLeagueRepo as unknown as LeagueRepository,
mockMembershipRepo as unknown as LeagueMembershipRepository,
mockSeasonRepo as unknown as SeasonRepository,
mockScoringConfigRepo as unknown as LeagueScoringConfigRepository,
mockGameRepo as unknown as GameRepository,
{ getPresetById: vi.fn().mockReturnValue({ id: 'preset1', name: 'Default' }) }
);

View File

@@ -1,5 +1,5 @@
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import {
@@ -7,9 +7,24 @@ import {
type GetAllRacesPageDataInput
} from './GetAllRacesPageDataUseCase';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
describe('GetAllRacesPageDataUseCase', () => {
const mockRaceFindAll = vi.fn();
const mockRaceRepo: any = {
const mockRaceRepo: {
findById: Mock;
findAll: Mock;
findByLeagueId: Mock;
findUpcomingByLeagueId: Mock;
findCompletedByLeagueId: Mock;
findByStatus: Mock;
findByDateRange: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
} = {
findById: vi.fn(),
findAll: mockRaceFindAll,
findByLeagueId: vi.fn(),
@@ -24,7 +39,16 @@ describe('GetAllRacesPageDataUseCase', () => {
};
const mockLeagueFindAll = vi.fn();
const mockLeagueRepo: any = {
const mockLeagueRepo: {
findById: Mock;
findAll: Mock;
findByOwnerId: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
searchByName: Mock;
} = {
findById: vi.fn(),
findAll: mockLeagueFindAll,
findByOwnerId: vi.fn(),
@@ -47,9 +71,11 @@ describe('GetAllRacesPageDataUseCase', () => {
});
it('should return races and filters data', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
const useCase = new GetAllRacesPageDataUseCase(
mockRaceRepo as unknown as RaceRepository,
mockLeagueRepo as unknown as LeagueRepository,
mockLogger
);
const race1 = Race.create({
id: 'race1',
@@ -132,9 +158,11 @@ describe('GetAllRacesPageDataUseCase', () => {
});
it('should return empty result when no races or leagues', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
const useCase = new GetAllRacesPageDataUseCase(
mockRaceRepo as unknown as RaceRepository,
mockLeagueRepo as unknown as LeagueRepository,
mockLogger
);
mockRaceFindAll.mockResolvedValue([]);
mockLeagueFindAll.mockResolvedValue([]);
@@ -157,9 +185,11 @@ describe('GetAllRacesPageDataUseCase', () => {
});
it('should return error when repository throws', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
const useCase = new GetAllRacesPageDataUseCase(
mockRaceRepo as unknown as RaceRepository,
mockLeagueRepo as unknown as LeagueRepository,
mockLogger
);
const error = new Error('Repository error');
mockRaceFindAll.mockRejectedValue(error);

View File

@@ -1,6 +1,10 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceStatusValue } from '../../domain/entities/Race';
import type { Race, RaceStatusValue } from '../../domain/entities/Race';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { Logger } from '@core/shared/domain/Logger';
import type { League } from '../../domain/entities/League';
export type GetAllRacesPageDataInput = {};
@@ -46,12 +50,12 @@ export class GetAllRacesPageDataUseCase {
]);
this.logger.info(`Found ${allRaces.length} races and ${allLeagues.length} leagues.`);
const leagueMap = new Map(allLeagues.map(league => [league.id.toString(), league.name.toString()]));
const leagueMap = new Map(allLeagues.map((league: League) => [league.id.toString(), league.name.toString()]));
const races: GetAllRacesPageRaceItem[] = allRaces
.slice()
.sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime())
.map(race => ({
.sort((a: Race, b: Race) => b.scheduledAt.getTime() - a.scheduledAt.getTime())
.map((race: Race) => ({
id: race.id,
track: race.track,
car: race.car,

View File

@@ -2,6 +2,9 @@ import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { League } from '../../domain/entities/League';
import type { Race } from '../../domain/entities/Race';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type GetAllRacesInput = {};

View File

@@ -1,7 +1,10 @@
import { Team } from '@/racing/domain/entities/Team';
import { Team } from '../../domain/entities/Team';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import { TeamStatsRepository } from '../../domain/repositories/TeamStatsRepository';
export interface GetAllTeamsInput {}
@@ -39,6 +42,7 @@ export class GetAllTeamsUseCase {
async execute(
_input: GetAllTeamsInput,
): Promise<Result<GetAllTeamsResult, ApplicationErrorCode<GetAllTeamsErrorCode, { message: string }>>> {
void _input;
this.logger.debug('GetAllTeamsUseCase: Fetching all teams');
try {
@@ -71,7 +75,7 @@ export class GetAllTeamsUseCase {
rating: stats?.rating ?? 0,
logoUrl: logoUrl ?? null,
description: team.description.toString(),
leagues: team.leagues.map(l => l.toString()),
leagues: team.leagues.map((l) => l.toString()),
isRecruiting: team.isRecruiting,
});
}

View File

@@ -4,11 +4,12 @@
* Retrieves all liveries for a specific driver.
*/
import { DriverLivery } from '@/racing/domain/entities/DriverLivery';
import { DriverLivery } from '../../domain/entities/DriverLivery';
import { UseCase } from '@core/shared/application/UseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { LiveryRepository } from '../../domain/repositories/LiveryRepository';
export interface GetDriverLiveriesInput {
driverId: string;

View File

@@ -1,8 +1,10 @@
import { TeamMembership } from '@/racing/domain/types/TeamMembership';
import { TeamMembership } from '../../domain/types/TeamMembership';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Team } from '../../domain/entities/Team';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
export interface GetDriverTeamInput {
driverId: string;

View File

@@ -5,7 +5,7 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
import type { Driver } from '../../domain/entities/Driver';
import type { Team } from '../../domain/entities/Team';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { DriverStatsUseCase } from './DriverStatsUseCase';
import type { DriverStats, DriverStatsUseCase } from './DriverStatsUseCase';
import type { RankingUseCase } from './RankingUseCase';
import { SkillLevelService, type SkillLevel } from '../../domain/services/SkillLevelService';
import { MediaReference } from '@core/domain/media/MediaReference';
@@ -71,10 +71,11 @@ export class GetDriversLeaderboardUseCase implements UseCase<GetDriversLeaderboa
this.driverStatsUseCase.getDriverStats(driver.id)
);
const statsResults = await Promise.all(statsPromises);
const statsMap = new Map<string, any>();
const statsMap = new Map<string, DriverStats>();
drivers.forEach((driver, idx) => {
if (statsResults[idx]) {
statsMap.set(driver.id, statsResults[idx]);
const stats = statsResults[idx];
if (stats) {
statsMap.set(driver.id, stats);
}
});

View File

@@ -8,6 +8,8 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
import { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type SponsorshipEntityType = SponsorableEntityType;

View File

@@ -2,7 +2,13 @@ import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { League } from '../../domain/entities/League';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export interface GetLeagueAdminPermissionsInput {
leagueId: string;
performerDriverId: string;
}
export type LeagueAdminPermissions = {
canManageSchedule: boolean;

View File

@@ -1,15 +1,23 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueAdminUseCase,
type GetLeagueAdminInput,
type GetLeagueAdminResult,
type GetLeagueAdminErrorCode,
} from './GetLeagueAdminUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('GetLeagueAdminUseCase', () => {
let mockLeagueRepo: any;
let mockLeagueRepo: {
findById: Mock;
findAll: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
findByOwnerId: Mock;
searchByName: Mock;
};
let mockFindById: Mock;
beforeEach(() => {
@@ -26,7 +34,7 @@ describe('GetLeagueAdminUseCase', () => {
};
});
const createUseCase = () => new GetLeagueAdminUseCase(mockLeagueRepo);
const createUseCase = () => new GetLeagueAdminUseCase(mockLeagueRepo as unknown as LeagueRepository);
const params: GetLeagueAdminInput = {
leagueId: 'league1',

View File

@@ -1,9 +1,8 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueDriverSeasonStatsUseCase,
type GetLeagueDriverSeasonStatsErrorCode,
type GetLeagueDriverSeasonStatsInput,
type GetLeagueDriverSeasonStatsResult,
} from './GetLeagueDriverSeasonStatsUseCase';
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
@@ -22,12 +21,70 @@ describe('GetLeagueDriverSeasonStatsUseCase', () => {
const mockDriverFindById = vi.fn();
let useCase: GetLeagueDriverSeasonStatsUseCase;
let standingRepository: any;
let resultRepository: any;
let penaltyRepository: any;
let raceRepository: any;
let driverRepository: any;
let driverRatingPort: any;
let standingRepository: {
findByLeagueId: Mock;
findByDriverIdAndLeagueId: Mock;
findAll: Mock;
save: Mock;
saveMany: Mock;
delete: Mock;
deleteByLeagueId: Mock;
exists: Mock;
recalculate: Mock;
};
let resultRepository: {
findById: Mock;
findAll: Mock;
findByRaceId: Mock;
findByDriverId: Mock;
findByDriverIdAndLeagueId: Mock;
create: Mock;
createMany: Mock;
update: Mock;
delete: Mock;
deleteByRaceId: Mock;
exists: Mock;
existsByRaceId: Mock;
};
let penaltyRepository: {
findById: Mock;
findByDriverId: Mock;
findByProtestId: Mock;
findPending: Mock;
findByRaceId: Mock;
findIssuedBy: Mock;
create: Mock;
update: Mock;
exists: Mock;
};
let raceRepository: {
findById: Mock;
findAll: Mock;
findByLeagueId: Mock;
findUpcomingByLeagueId: Mock;
findCompletedByLeagueId: Mock;
findByStatus: Mock;
findByDateRange: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
};
let driverRepository: {
findById: Mock;
findByIRacingId: Mock;
findAll: Mock;
create: Mock;
update: Mock;
delete: Mock;
exists: Mock;
existsByIRacingId: Mock;
};
let driverRatingPort: {
getDriverRating: Mock;
calculateRatingChange: Mock;
updateDriverRating: Mock;
};
beforeEach(() => {
mockStandingFindByLeagueId.mockReset();
@@ -102,12 +159,14 @@ describe('GetLeagueDriverSeasonStatsUseCase', () => {
updateDriverRating: vi.fn(),
};
useCase = new GetLeagueDriverSeasonStatsUseCase(standingRepository,
resultRepository,
penaltyRepository,
raceRepository,
driverRepository,
driverRatingPort);
useCase = new GetLeagueDriverSeasonStatsUseCase(
standingRepository as unknown as StandingRepository,
resultRepository as unknown as ResultRepository,
penaltyRepository as unknown as PenaltyRepository,
raceRepository as unknown as RaceRepository,
driverRepository as unknown as DriverRepository,
driverRatingPort as unknown as DriverRatingPort
);
});
it('should return league driver season stats for given league id', async () => {

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, type Mock } from 'vitest';
import {
GetLeagueFullConfigUseCase,
type GetLeagueFullConfigInput,
type GetLeagueFullConfigResult,
type GetLeagueFullConfigErrorCode,
} from './GetLeagueFullConfigUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
@@ -13,10 +12,10 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
describe('GetLeagueFullConfigUseCase', () => {
let useCase: GetLeagueFullConfigUseCase;
let leagueRepository: any;
let seasonRepository: any;
let leagueScoringConfigRepository: any;
let gameRepository: any;
let leagueRepository: { findById: Mock };
let seasonRepository: { findByLeagueId: Mock };
let leagueScoringConfigRepository: { findBySeasonId: Mock };
let gameRepository: { findById: Mock };
beforeEach(() => {
leagueRepository = {
@@ -32,10 +31,12 @@ describe('GetLeagueFullConfigUseCase', () => {
findById: vi.fn(),
};
useCase = new GetLeagueFullConfigUseCase(leagueRepository,
seasonRepository,
leagueScoringConfigRepository,
gameRepository);
useCase = new GetLeagueFullConfigUseCase(
leagueRepository as unknown as LeagueRepository,
seasonRepository as unknown as SeasonRepository,
leagueScoringConfigRepository as unknown as LeagueScoringConfigRepository,
gameRepository as unknown as GameRepository
);
});
it('should return league config when league exists', async () => {

View File

@@ -1,14 +1,16 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueMembershipsUseCase,
type GetLeagueMembershipsInput,
type GetLeagueMembershipsResult,
type GetLeagueMembershipsErrorCode,
} from './GetLeagueMembershipsUseCase';
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
import { Driver } from '../../domain/entities/Driver';
import { League } from '../../domain/entities/League';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
describe('GetLeagueMembershipsUseCase', () => {
let useCase: GetLeagueMembershipsUseCase;
@@ -32,9 +34,11 @@ describe('GetLeagueMembershipsUseCase', () => {
leagueRepository = {
findById: vi.fn(),
};
useCase = new GetLeagueMembershipsUseCase(leagueMembershipRepository as any,
driverRepository as any,
leagueRepository as any);
useCase = new GetLeagueMembershipsUseCase(
leagueMembershipRepository as unknown as LeagueMembershipRepository,
driverRepository as unknown as DriverRepository,
leagueRepository as unknown as LeagueRepository
);
});
it('should return league memberships with drivers', async () => {

View File

@@ -1,13 +1,16 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueOwnerSummaryUseCase,
type GetLeagueOwnerSummaryInput,
type GetLeagueOwnerSummaryResult,
type GetLeagueOwnerSummaryErrorCode,
} from './GetLeagueOwnerSummaryUseCase';
import { Driver } from '../../domain/entities/Driver';
import { League } from '../../domain/entities/League';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
describe('GetLeagueOwnerSummaryUseCase', () => {
let useCase: GetLeagueOwnerSummaryUseCase;
@@ -40,10 +43,10 @@ describe('GetLeagueOwnerSummaryUseCase', () => {
findByLeagueId: vi.fn(),
};
useCase = new GetLeagueOwnerSummaryUseCase(
leagueRepository as any,
driverRepository as any,
leagueMembershipRepository as any,
standingRepository as any
leagueRepository as unknown as LeagueRepository,
driverRepository as unknown as DriverRepository,
leagueMembershipRepository as unknown as LeagueMembershipRepository,
standingRepository as unknown as StandingRepository
);
});

View File

@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueScheduleUseCase,
type GetLeagueScheduleInput,
type GetLeagueScheduleResult,
type GetLeagueScheduleErrorCode,
} from './GetLeagueScheduleUseCase';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -10,6 +9,8 @@ import type { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/domain/Logger';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
describe('GetLeagueScheduleUseCase', () => {
let useCase: GetLeagueScheduleUseCase;
@@ -42,7 +43,7 @@ describe('GetLeagueScheduleUseCase', () => {
error: vi.fn(),
} as unknown as Logger;
useCase = new GetLeagueScheduleUseCase(leagueRepository as unknown as LeagueRepository,
seasonRepository as any,
seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
});
@@ -109,7 +110,7 @@ describe('GetLeagueScheduleUseCase', () => {
raceRepository.findByLeagueId.mockResolvedValue([janRace, febRace]);
// Season 1 covers January
const resultSeason1 = await useCase.execute({ leagueId, seasonId: 'season-jan' } as any);
const resultSeason1 = await useCase.execute({ leagueId, seasonId: 'season-jan' });
expect(resultSeason1.isOk()).toBe(true);
const resultValue1 = resultSeason1.unwrap();
expect(resultValue1.seasonId).toBe('season-jan');
@@ -117,7 +118,7 @@ describe('GetLeagueScheduleUseCase', () => {
expect(resultValue1.races.map(r => r.race.id)).toEqual(['race-jan']);
// Season 2 covers February
const resultSeason2 = await useCase.execute({ leagueId, seasonId: 'season-feb' } as any);
const resultSeason2 = await useCase.execute({ leagueId, seasonId: 'season-feb' });
expect(resultSeason2.isOk()).toBe(true);
const resultValue2 = resultSeason2.unwrap();
expect(resultValue2.seasonId).toBe('season-feb');

View File

@@ -1,10 +1,13 @@
import { SeasonScheduleGenerator } from '@/racing/domain/services/SeasonScheduleGenerator';
import { SeasonScheduleGenerator } from '../../domain/services/SeasonScheduleGenerator';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { League } from '../../domain/entities/League';
import type { Race } from '../../domain/entities/Race';
import type { Season } from '../../domain/entities/season/Season';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
export type GetLeagueScheduleErrorCode =
| 'LEAGUE_NOT_FOUND'
@@ -51,7 +54,7 @@ export class GetLeagueScheduleUseCase {
}
const seasons = await this.seasonRepository.findByLeagueId(params.leagueId);
const activeSeason = seasons.find(s => s.status.isActive()) ?? seasons[0];
const activeSeason = seasons.find((s: Season) => s.status.isActive()) ?? seasons[0];
if (!activeSeason) {
return Result.err({
code: 'SEASON_NOT_FOUND',

View File

@@ -1,5 +1,4 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { Result } from '@core/shared/domain/Result';
import { GetLeagueScoringConfigUseCase } from './GetLeagueScoringConfigUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
@@ -13,10 +12,26 @@ import type { LeagueScoringPreset } from '../../domain/types/LeagueScoringPreset
describe('GetLeagueScoringConfigUseCase', () => {
let useCase: GetLeagueScoringConfigUseCase;
let mockLeagueRepository: any;
let mockSeasonRepository: any;
let mockLeagueScoringConfigRepository: any;
let mockGameRepository: any;
let mockLeagueRepository: {
findById: any;
exists: any;
save: any;
findAll: any;
};
let mockSeasonRepository: {
findByLeagueId: any;
save: any;
findById: any;
};
let mockLeagueScoringConfigRepository: {
findBySeasonId: any;
save: any;
};
let mockGameRepository: {
findById: any;
save: any;
findAll: any;
};
let mockPresetProvider: { getPresetById: any };
beforeEach(() => {
@@ -49,11 +64,11 @@ describe('GetLeagueScoringConfigUseCase', () => {
};
useCase = new GetLeagueScoringConfigUseCase(
mockLeagueRepository,
mockSeasonRepository,
mockLeagueScoringConfigRepository,
mockGameRepository,
mockPresetProvider,
mockLeagueRepository as unknown as LeagueRepository,
mockSeasonRepository as unknown as SeasonRepository,
mockLeagueScoringConfigRepository as unknown as LeagueScoringConfigRepository,
mockGameRepository as unknown as GameRepository,
mockPresetProvider as any,
);
});

View File

@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetRaceDetailUseCase,
type GetRaceDetailInput,
type GetRaceDetailResult,
type GetRaceDetailErrorCode,
} from './GetRaceDetailUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';

View File

@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import {
GetRacePenaltiesUseCase,
type GetRacePenaltiesInput,
type GetRacePenaltiesResult,
type GetRacePenaltiesErrorCode,
} from './GetRacePenaltiesUseCase';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';

View File

@@ -9,6 +9,9 @@ import { Protest } from '../../domain/entities/Protest';
import { Driver } from '../../domain/entities/Driver';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
describe('GetRaceProtestsUseCase', () => {
let useCase: GetRaceProtestsUseCase;
let protestRepository: { findByRaceId: Mock };
@@ -17,8 +20,10 @@ describe('GetRaceProtestsUseCase', () => {
beforeEach(() => {
protestRepository = { findByRaceId: vi.fn() };
driverRepository = { findById: vi.fn() };
useCase = new GetRaceProtestsUseCase(protestRepository as any,
driverRepository as any);
useCase = new GetRaceProtestsUseCase(
protestRepository as unknown as ProtestRepository,
driverRepository as unknown as DriverRepository
);
});
it('should return protests with drivers', async () => {

View File

@@ -2,12 +2,13 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetRaceRegistrationsUseCase,
type GetRaceRegistrationsInput,
type GetRaceRegistrationsResult,
type GetRaceRegistrationsErrorCode,
} from './GetRaceRegistrationsUseCase';
import { RaceRegistration } from '@core/racing/domain/entities/RaceRegistration';
import { Race } from '@core/racing/domain/entities/Race';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
describe('GetRaceRegistrationsUseCase', () => {
let useCase: GetRaceRegistrationsUseCase;
@@ -17,8 +18,10 @@ describe('GetRaceRegistrationsUseCase', () => {
beforeEach(() => {
raceRepository = { findById: vi.fn() };
registrationRepository = { findByRaceId: vi.fn() };
useCase = new GetRaceRegistrationsUseCase(raceRepository as any,
registrationRepository as any);
useCase = new GetRaceRegistrationsUseCase(
raceRepository as unknown as RaceRepository,
registrationRepository as unknown as RaceRegistrationRepository
);
});
it('should return race and registrations on success', async () => {

View File

@@ -27,11 +27,13 @@ describe('GetRaceResultsDetailUseCase', () => {
driverRepository = { findAll: vi.fn() };
penaltyRepository = { findByRaceId: vi.fn() };
useCase = new GetRaceResultsDetailUseCase(raceRepository as any,
leagueRepository as any,
resultRepository as any,
driverRepository as any,
penaltyRepository as any);
useCase = new GetRaceResultsDetailUseCase(
raceRepository as unknown as RaceRepository,
leagueRepository as unknown as LeagueRepository,
resultRepository as unknown as ResultRepository,
driverRepository as unknown as DriverRepository,
penaltyRepository as unknown as PenaltyRepository
);
});
it('presents race results detail when race exists', async () => {

View File

@@ -1,13 +1,15 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetRaceWithSOFUseCase,
type GetRaceWithSOFInput,
type GetRaceWithSOFResult,
type GetRaceWithSOFErrorCode,
} from './GetRaceWithSOFUseCase';
import { Race } from '../../domain/entities/Race';
import { SessionType } from '../../domain/value-objects/SessionType';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
describe('GetRaceWithSOFUseCase', () => {
let useCase: GetRaceWithSOFUseCase;
@@ -34,10 +36,10 @@ describe('GetRaceWithSOFUseCase', () => {
};
getDriverRating = vi.fn();
useCase = new GetRaceWithSOFUseCase(
raceRepository as any,
registrationRepository as any,
resultRepository as any,
getDriverRating
raceRepository as unknown as RaceRepository,
registrationRepository as unknown as RaceRegistrationRepository,
resultRepository as unknown as ResultRepository,
getDriverRating as any
);
});

View File

@@ -2,7 +2,13 @@ import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Race } from '../../domain/entities/Race';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { League } from '../../domain/entities/League';
export interface GetRacesPageDataInput {
leagueId: string;
}
export type GetRacesPageRaceItem = {
race: Race;
@@ -35,16 +41,16 @@ export class GetRacesPageDataUseCase {
]);
const leagueMap = new Map(
allLeagues.map(league => [league.id.toString(), league.name.toString()]),
allLeagues.map((league: League) => [league.id.toString(), league.name.toString()]),
);
const filteredRaces = input.leagueId
? allRaces.filter(race => race.leagueId === input.leagueId)
? allRaces.filter((race: Race) => race.leagueId === input.leagueId)
: allRaces;
filteredRaces.sort((a, b) => a.scheduledAt.getTime() - b.scheduledAt.getTime());
filteredRaces.sort((a: Race, b: Race) => a.scheduledAt.getTime() - b.scheduledAt.getTime());
const races: GetRacesPageRaceItem[] = filteredRaces.map(race => ({
const races: GetRacesPageRaceItem[] = filteredRaces.map((race: Race) => ({
race,
leagueName: leagueMap.get(race.leagueId) ?? 'Unknown League',
}));

View File

@@ -1,12 +1,12 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetSeasonDetailsUseCase,
type GetSeasonDetailsInput,
type GetSeasonDetailsResult,
type GetSeasonDetailsErrorCode,
} from './GetSeasonDetailsUseCase';
import { Season } from '../../domain/entities/season/Season';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
describe('GetSeasonDetailsUseCase', () => {
let useCase: GetSeasonDetailsUseCase;
@@ -19,7 +19,7 @@ describe('GetSeasonDetailsUseCase', () => {
findById: vi.fn(),
};
useCase = new GetSeasonDetailsUseCase(seasonRepository as any);
useCase = new GetSeasonDetailsUseCase(seasonRepository as unknown as SeasonRepository);
});
it('returns full details for a season', async () => {

View File

@@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import {
GetSeasonSponsorshipsUseCase,
type GetSeasonSponsorshipsInput,
type GetSeasonSponsorshipsResult,
type GetSeasonSponsorshipsErrorCode,
} from './GetSeasonSponsorshipsUseCase';
import type { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
@@ -52,11 +51,13 @@ describe('GetSeasonSponsorshipsUseCase', () => {
findByLeagueId: vi.fn(),
};
useCase = new GetSeasonSponsorshipsUseCase(seasonSponsorshipRepository as any,
seasonRepository as any,
leagueRepository as any,
leagueMembershipRepository as any,
raceRepository as any);
useCase = new GetSeasonSponsorshipsUseCase(
seasonSponsorshipRepository as unknown as SeasonSponsorshipRepository,
seasonRepository as unknown as SeasonRepository,
leagueRepository as unknown as LeagueRepository,
leagueMembershipRepository as unknown as LeagueMembershipRepository,
raceRepository as unknown as RaceRepository
);
});
it('returns SEASON_NOT_FOUND when season does not exist', async () => {

View File

@@ -15,6 +15,7 @@ export class GetSponsorsUseCase {
constructor(private readonly sponsorRepository: SponsorRepository) {}
async execute(_input: GetSponsorsInput): Promise<Result<GetSponsorsResult, ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>>> {
void _input;
try {
const sponsors = await this.sponsorRepository.findAll();
return Result.ok({ sponsors });

View File

@@ -5,9 +5,9 @@ import {
type GetTeamJoinRequestsResult,
type GetTeamJoinRequestsErrorCode,
} from './GetTeamJoinRequestsUseCase';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import { Driver } from '../../domain/entities/Driver';
import { Team } from '../../domain/entities/Team';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -34,9 +34,11 @@ describe('GetTeamJoinRequestsUseCase', () => {
findById: vi.fn(),
};
useCase = new GetTeamJoinRequestsUseCase(membershipRepository as any,
driverRepository as any,
teamRepository as any);
useCase = new GetTeamJoinRequestsUseCase(
membershipRepository as unknown as TeamMembershipRepository,
driverRepository as unknown as DriverRepository,
teamRepository as unknown as TeamRepository
);
});
it('should return join requests with drivers when team exists', async () => {

View File

@@ -7,6 +7,10 @@ import {
type GetTeamMembersErrorCode,
type GetTeamMembersInput
} from './GetTeamMembersUseCase';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { Logger } from '@core/shared/domain/Logger';
describe('GetTeamMembersUseCase', () => {
let useCase: GetTeamMembersUseCase;
@@ -41,10 +45,12 @@ describe('GetTeamMembersUseCase', () => {
warn: vi.fn(),
error: vi.fn(),
};
useCase = new GetTeamMembersUseCase(membershipRepository as any,
driverRepository as any,
teamRepository as any,
logger as any);
useCase = new GetTeamMembersUseCase(
membershipRepository as unknown as TeamMembershipRepository,
driverRepository as unknown as DriverRepository,
teamRepository as unknown as TeamRepository,
logger as unknown as Logger
);
});
it('should return team members with driver entities', async () => {

View File

@@ -3,6 +3,10 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
import type { Driver } from '../../domain/entities/Driver';
import type { Team } from '../../domain/entities/Team';
import type { TeamMembership } from '../../domain/types/TeamMembership';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type GetTeamMembersInput = {
teamId: string;

View File

@@ -1,15 +1,21 @@
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import {
GetTeamMembershipUseCase,
type GetTeamMembershipErrorCode,
type GetTeamMembershipInput
} from './GetTeamMembershipUseCase';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
describe('GetTeamMembershipUseCase', () => {
const mockGetMembership = vi.fn();
const mockMembershipRepo: any = {
const mockMembershipRepo: {
getMembership: Mock;
getActiveMembershipForDriver: Mock;
getTeamMembers: Mock;
saveMembership: Mock;
removeMembership: Mock;
getJoinRequests: Mock;
countByTeamId: Mock;
saveJoinRequest: Mock;
removeJoinRequest: Mock;
} = {
getMembership: mockGetMembership,
getActiveMembershipForDriver: vi.fn(),
getTeamMembers: vi.fn(),
@@ -32,7 +38,7 @@ describe('GetTeamMembershipUseCase', () => {
beforeEach(() => {
vi.clearAllMocks();
useCase = new GetTeamMembershipUseCase(mockMembershipRepo, mockLogger);
useCase = new GetTeamMembershipUseCase(mockMembershipRepo as unknown as TeamMembershipRepository, mockLogger);
});
it('should return membership data when membership exists', async () => {

View File

@@ -1,5 +1,8 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type GetTeamMembershipInput = {
teamId: string;
driverId: string;

View File

@@ -7,10 +7,15 @@ import {
type GetTeamsLeaderboardInput
} from './GetTeamsLeaderboardUseCase';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
describe('GetTeamsLeaderboardUseCase', () => {
let useCase: GetTeamsLeaderboardUseCase;
let teamRepository: {
findAll: Mock;
findById: Mock;
};
let teamMembershipRepository: {
getTeamMembers: Mock;
@@ -26,6 +31,7 @@ describe('GetTeamsLeaderboardUseCase', () => {
beforeEach(() => {
teamRepository = {
findAll: vi.fn(),
findById: vi.fn(),
};
teamMembershipRepository = {
getTeamMembers: vi.fn(),
@@ -37,10 +43,11 @@ describe('GetTeamsLeaderboardUseCase', () => {
warn: vi.fn(),
error: vi.fn(),
};
useCase = new GetTeamsLeaderboardUseCase(teamRepository as any,
teamMembershipRepository as any,
useCase = new GetTeamsLeaderboardUseCase(
teamRepository as unknown as TeamRepository,
teamMembershipRepository as unknown as TeamMembershipRepository,
getDriverStats as any,
logger as any
logger as unknown as Logger
);
});

View File

@@ -1,8 +1,10 @@
import { Team } from '@/racing/domain/entities/Team';
import { Team } from '../../domain/entities/Team';
import { SkillLevelService, type SkillLevel } from '@core/racing/domain/services/SkillLevelService';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
interface DriverStatsAdapter {
rating: number | null;
@@ -55,7 +57,7 @@ export class GetTeamsLeaderboardUseCase {
const items: TeamLeaderboardItem[] = [];
await Promise.all(
allTeams.map(async (team) => {
allTeams.map(async (team: Team) => {
const memberships = await this.teamMembershipRepository.getTeamMembers(team.id);
const memberCount = memberships.length;

View File

@@ -16,6 +16,7 @@ export class GetTotalDriversUseCase {
async execute(
_input: GetTotalDriversInput,
): Promise<Result<GetTotalDriversResult, ApplicationErrorCode<GetTotalDriversErrorCode, { message: string }>>> {
void _input;
try {
const drivers = await this.driverRepository.findAll();
const totalDrivers = drivers.length;

View File

@@ -16,6 +16,7 @@ export class GetTotalLeaguesUseCase {
async execute(
_input: GetTotalLeaguesInput,
): Promise<Result<GetTotalLeaguesResult, ApplicationErrorCode<GetTotalLeaguesErrorCode, { message: string }>>> {
void _input;
try {
const leagues = await this.leagueRepository.findAll();
const totalLeagues = leagues.length;

View File

@@ -16,6 +16,7 @@ export class GetTotalRacesUseCase {
async execute(
_input: GetTotalRacesInput,
): Promise<Result<GetTotalRacesResult, ApplicationErrorCode<GetTotalRacesErrorCode, { message: string }>>> {
void _input;
try {
const races = await this.raceRepository.findAll();
const totalRaces = races.length;

View File

@@ -1,6 +1,12 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result as RaceResult } from '../../domain/entities/result/Result';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { StandingRepository } from '../../domain/repositories/StandingRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type ImportRaceResultDTO = {
id: string;

View File

@@ -1,6 +1,12 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Result as RaceResult } from '../../domain/entities/result/Result';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
import { StandingRepository } from '../../domain/repositories/StandingRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type ImportRaceResultRow = {
id: string;

View File

@@ -1,7 +1,8 @@
import { Result } from '@/shared/domain/Result';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export type JoinLeagueErrorCode = 'ALREADY_MEMBER' | 'REPOSITORY_ERROR';

View File

@@ -1,8 +1,10 @@
import { TeamMembership } from '@/racing/domain/types/TeamMembership';
import { TeamMembership } from '../../domain/types/TeamMembership';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Team } from '../../domain/entities/Team';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
export type JoinTeamErrorCode =
| 'ALREADY_IN_TEAM'

View File

@@ -5,20 +5,26 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
import { Race } from '../../domain/entities/Race';
import { Season } from '../../domain/entities/season/Season';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import {
CreateLeagueSeasonScheduleRaceUseCase,
type CreateLeagueSeasonScheduleRaceErrorCode
} from './CreateLeagueSeasonScheduleRaceUseCase';
import {
DeleteLeagueSeasonScheduleRaceUseCase,
type DeleteLeagueSeasonScheduleRaceErrorCode
} from './DeleteLeagueSeasonScheduleRaceUseCase';
import {
PublishLeagueSeasonScheduleUseCase,
type PublishLeagueSeasonScheduleErrorCode
} from './PublishLeagueSeasonScheduleUseCase';
import {
UnpublishLeagueSeasonScheduleUseCase,
type UnpublishLeagueSeasonScheduleErrorCode
} from './UnpublishLeagueSeasonScheduleUseCase';
import {
UpdateLeagueSeasonScheduleRaceUseCase,
type UpdateLeagueSeasonScheduleRaceErrorCode
} from './UpdateLeagueSeasonScheduleRaceUseCase';
@@ -60,8 +66,8 @@ describe('CreateLeagueSeasonScheduleRaceUseCase', () => {
seasonRepository.findById.mockResolvedValue(season);
raceRepository.create.mockImplementation(async (race: Race) => race);
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger,
{ generateRaceId: () => 'race-123' },
);
@@ -89,8 +95,8 @@ describe('CreateLeagueSeasonScheduleRaceUseCase', () => {
const season = createSeasonWithinWindow({ leagueId: 'other-league' });
seasonRepository.findById.mockResolvedValue(season);
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger,
{ generateRaceId: () => 'race-123' },
);
@@ -116,8 +122,8 @@ describe('CreateLeagueSeasonScheduleRaceUseCase', () => {
const season = createSeasonWithinWindow();
seasonRepository.findById.mockResolvedValue(season);
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new CreateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger,
{ generateRaceId: () => 'race-123' },
);
@@ -165,8 +171,8 @@ describe('UpdateLeagueSeasonScheduleRaceUseCase', () => {
raceRepository.findById.mockResolvedValue(existing);
raceRepository.update.mockImplementation(async (race: Race) => race);
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const newScheduledAt = new Date('2025-01-20T20:00:00Z');
@@ -189,8 +195,8 @@ describe('UpdateLeagueSeasonScheduleRaceUseCase', () => {
const season = createSeasonWithinWindow({ leagueId: 'other-league' });
seasonRepository.findById.mockResolvedValue(season);
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -223,8 +229,8 @@ describe('UpdateLeagueSeasonScheduleRaceUseCase', () => {
});
raceRepository.findById.mockResolvedValue(existing);
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -248,8 +254,8 @@ describe('UpdateLeagueSeasonScheduleRaceUseCase', () => {
seasonRepository.findById.mockResolvedValue(season);
raceRepository.findById.mockResolvedValue(null);
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new UpdateLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -294,8 +300,8 @@ describe('DeleteLeagueSeasonScheduleRaceUseCase', () => {
raceRepository.findById.mockResolvedValue(existing);
raceRepository.delete.mockResolvedValue(undefined);
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -313,8 +319,8 @@ describe('DeleteLeagueSeasonScheduleRaceUseCase', () => {
const season = createSeasonWithinWindow({ leagueId: 'other-league' });
seasonRepository.findById.mockResolvedValue(season);
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -338,8 +344,8 @@ describe('DeleteLeagueSeasonScheduleRaceUseCase', () => {
seasonRepository.findById.mockResolvedValue(season);
raceRepository.findById.mockResolvedValue(null);
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as any,
raceRepository as any,
const useCase = new DeleteLeagueSeasonScheduleRaceUseCase(seasonRepository as unknown as SeasonRepository,
raceRepository as unknown as RaceRepository,
logger);
const result = await useCase.execute({
@@ -372,7 +378,7 @@ describe('PublishLeagueSeasonScheduleUseCase', () => {
seasonRepository.findById.mockResolvedValue(season);
seasonRepository.update.mockResolvedValue(undefined);
const useCase = new PublishLeagueSeasonScheduleUseCase(seasonRepository as any,
const useCase = new PublishLeagueSeasonScheduleUseCase(seasonRepository as unknown as SeasonRepository,
logger);
const result = await useCase.execute({ leagueId: 'league-1', seasonId: 'season-1' });
@@ -388,7 +394,7 @@ describe('PublishLeagueSeasonScheduleUseCase', () => {
const season = createSeasonWithinWindow({ leagueId: 'other-league' });
seasonRepository.findById.mockResolvedValue(season);
const useCase = new PublishLeagueSeasonScheduleUseCase(seasonRepository as any,
const useCase = new PublishLeagueSeasonScheduleUseCase(seasonRepository as unknown as SeasonRepository,
logger);
const result = await useCase.execute({ leagueId: 'league-1', seasonId: 'season-1' });
@@ -417,7 +423,7 @@ describe('UnpublishLeagueSeasonScheduleUseCase', () => {
seasonRepository.findById.mockResolvedValue(season);
seasonRepository.update.mockResolvedValue(undefined);
const useCase = new UnpublishLeagueSeasonScheduleUseCase(seasonRepository as any,
const useCase = new UnpublishLeagueSeasonScheduleUseCase(seasonRepository as unknown as SeasonRepository,
logger);
const result = await useCase.execute({ leagueId: 'league-1', seasonId: 'season-1' });
@@ -433,7 +439,7 @@ describe('UnpublishLeagueSeasonScheduleUseCase', () => {
const season = createSeasonWithinWindow({ leagueId: 'other-league' });
seasonRepository.findById.mockResolvedValue(season);
const useCase = new UnpublishLeagueSeasonScheduleUseCase(seasonRepository as any,
const useCase = new UnpublishLeagueSeasonScheduleUseCase(seasonRepository as unknown as SeasonRepository,
logger);
const result = await useCase.execute({ leagueId: 'league-1', seasonId: 'season-1' });

View File

@@ -1,8 +1,10 @@
import { TeamMembership } from '@/racing/domain/types/TeamMembership';
import { TeamMembership } from '../../domain/types/TeamMembership';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Team } from '../../domain/entities/Team';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
export type LeaveTeamErrorCode =
| 'TEAM_NOT_FOUND'

View File

@@ -1,8 +1,12 @@
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
export interface PublishLeagueSeasonScheduleInput {
leagueId: string;
seasonId: string;
}
export type PublishLeagueSeasonScheduleResult = {
success: true;

View File

@@ -5,11 +5,14 @@
* Designed for fast, common penalty scenarios like track limits, warnings, etc.
*/
import { Result } from '@/shared/domain/Result';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { randomUUID } from 'crypto';
import { Penalty } from '../../domain/entities/penalty/Penalty';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export type QuickPenaltyErrorCode = 'RACE_NOT_FOUND' | 'UNAUTHORIZED' | 'UNKNOWN_INFRACTION' | 'REPOSITORY_ERROR';
@@ -55,7 +58,7 @@ export class QuickPenaltyUseCase {
// Validate admin has authority
const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
const adminMembership = memberships.find(
m => m.driverId.toString() === input.adminId && m.status.toString() === 'active'
(m: any) => m.driverId.toString() === input.adminId && m.status.toString() === 'active'
);
if (!adminMembership || (adminMembership.role.toString() !== 'owner' && adminMembership.role.toString() !== 'admin')) {
@@ -99,7 +102,7 @@ export class QuickPenaltyUseCase {
await this.penaltyRepository.create(penalty);
const result: QuickPenaltyResult = {
penaltyId: penalty.id,
penaltyId: penalty.id.toString(),
raceId: input.raceId,
driverId: input.driverId,
type,

View File

@@ -1,5 +1,5 @@
import { ChampionshipStanding } from '@/racing/domain/entities/championship/ChampionshipStanding';
import { ChampionshipStanding } from '../../domain/entities/championship/ChampionshipStanding';
import { ChampionshipAggregator } from '@core/racing/domain/services/ChampionshipAggregator';
import { EventScoringService } from '@core/racing/domain/services/EventScoringService';
import type { ChampionshipConfig } from '@core/racing/domain/types/ChampionshipConfig';
@@ -8,6 +8,13 @@ import type { SessionType } from '@core/racing/domain/types/SessionType';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import { ChampionshipStandingRepository } from '../../domain/repositories/ChampionshipStandingRepository';
export type RecalculateChampionshipStandingsInput = {
leagueId: string;
@@ -48,7 +55,7 @@ export class RecalculateChampionshipStandingsUseCase {
input: RecalculateChampionshipStandingsInput,
): Promise<
Result<
void,
RecalculateChampionshipStandingsResult,
ApplicationErrorCode<RecalculateChampionshipStandingsErrorCode, { message: string }>
>
> {

View File

@@ -5,6 +5,7 @@ import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
import { TeamRating } from '../../domain/entities/TeamRating';
// Mock repositories
class MockTeamRatingEventRepository implements TeamRatingEventRepository {
@@ -48,13 +49,13 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
}
class MockTeamRatingRepository implements TeamRatingRepository {
private snapshots: Map<string, any> = new Map();
private snapshots: Map<string, TeamRating> = new Map();
async findByTeamId(teamId: string): Promise<any | null> {
async findByTeamId(teamId: string): Promise<TeamRating | null> {
return this.snapshots.get(teamId) || null;
}
async save(snapshot: any): Promise<any> {
async save(snapshot: TeamRating): Promise<TeamRating> {
this.snapshots.set(snapshot.teamId, snapshot);
return snapshot;
}

View File

@@ -6,14 +6,14 @@ import { AppendTeamRatingEventsUseCase } from './AppendTeamRatingEventsUseCase';
import { TeamDrivingRaceFactsDto } from '@core/racing/domain/services/TeamDrivingRatingEventFactory';
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
import { TeamRating } from '../../domain/entities/TeamRating';
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
// Mock repositories
class MockTeamRaceResultsProvider implements TeamRaceResultsProvider {
private results: TeamDrivingRaceFactsDto | null = null;
async getTeamRaceResults(raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
async getTeamRaceResults(_raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
return this.results;
}
@@ -59,13 +59,13 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
}
class MockTeamRatingRepository implements TeamRatingRepository {
private snapshots: Map<string, any> = new Map();
private snapshots: Map<string, TeamRating> = new Map();
async findByTeamId(teamId: string): Promise<any | null> {
async findByTeamId(teamId: string): Promise<TeamRating | null> {
return this.snapshots.get(teamId) || null;
}
async save(snapshot: any): Promise<any> {
async save(snapshot: TeamRating): Promise<TeamRating> {
this.snapshots.set(snapshot.teamId, snapshot);
return snapshot;
}

View File

@@ -1,7 +1,6 @@
import { describe, it, expect, vi, type Mock, beforeEach } from 'vitest';
import {
RejectLeagueJoinRequestUseCase,
type RejectLeagueJoinRequestResult,
} from './RejectLeagueJoinRequestUseCase';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -22,7 +21,7 @@ describe('RejectLeagueJoinRequestUseCase', () => {
it('reject removes request only', async () => {
const output: { present: Mock } = {
present: vi.fn(),
} as any;
};
const useCase = new RejectLeagueJoinRequestUseCase(leagueMembershipRepository as unknown as LeagueMembershipRepository);
@@ -32,7 +31,6 @@ describe('RejectLeagueJoinRequestUseCase', () => {
const result = await useCase.execute(
{ leagueId: 'league-1', joinRequestId: 'jr-1' },
output,
);
expect(result.isOk()).toBe(true);
@@ -42,7 +40,7 @@ describe('RejectLeagueJoinRequestUseCase', () => {
it('reject returns error when request missing', async () => {
const output: { present: Mock } = {
present: vi.fn(),
} as any;
};
const useCase = new RejectLeagueJoinRequestUseCase(leagueMembershipRepository as unknown as LeagueMembershipRepository);
@@ -50,7 +48,6 @@ describe('RejectLeagueJoinRequestUseCase', () => {
const result = await useCase.execute(
{ leagueId: 'league-1', joinRequestId: 'jr-404' },
output,
);
expect(result.isErr()).toBe(true);

View File

@@ -7,7 +7,13 @@
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
export interface RejectSponsorshipRequestInput {
requestId: string;
respondedBy: string;
reason?: string;
}
export type RejectSponsorshipRequestResult = {
requestId: string;

View File

@@ -2,12 +2,12 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
RejectTeamJoinRequestUseCase,
type RejectTeamJoinRequestInput,
type RejectTeamJoinRequestResult,
type RejectTeamJoinRequestErrorCode,
} from './RejectTeamJoinRequestUseCase';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/domain/Logger';
interface TeamRepositoryMock {
findById: Mock;

View File

@@ -1,7 +1,16 @@
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { TeamRepository } from '../../domain/repositories/TeamRepository';
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamJoinRequest } from '../../domain/types/TeamMembership';
export interface RejectTeamJoinRequestInput {
teamId: string;
managerId: string;
requestId: string;
reason?: string;
}
export type RejectTeamJoinRequestResult = {
teamId: string;
@@ -57,7 +66,7 @@ export class RejectTeamJoinRequestUseCase {
}
const joinRequests = await this.membershipRepository.getJoinRequests(teamId);
const joinRequest = joinRequests.find(r => r.id === requestId);
const joinRequest = joinRequests.find((r: TeamJoinRequest) => r.id === requestId);
if (!joinRequest) {
this.logger.warn('Join request not found when rejecting', { teamId, managerId, requestId });
return Result.err({

View File

@@ -1,6 +1,8 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Race } from '../../domain/entities/Race';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/domain/Logger';
export type ReopenRaceInput = {
raceId: string;

View File

@@ -6,6 +6,12 @@
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
export type ReviewProtestErrorCode = 'PROTEST_NOT_FOUND' | 'RACE_NOT_FOUND' | 'NOT_LEAGUE_ADMIN' | 'REPOSITORY_ERROR';
@@ -53,7 +59,7 @@ export class ReviewProtestUseCase {
// Validate steward has authority (owner or admin of the league)
const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
const stewardMembership = memberships.find(
m => m.driverId.toString() === input.stewardId && m.status.toString() === 'active'
(m: LeagueMembership) => m.driverId.toString() === input.stewardId && m.status.toString() === 'active'
);
if (!stewardMembership || (stewardMembership.role.toString() !== 'owner' && stewardMembership.role.toString() !== 'admin')) {

View File

@@ -11,10 +11,6 @@ import {
ManageSeasonLifecycleUseCase,
type CreateSeasonForLeagueCommand,
type ManageSeasonLifecycleCommand,
type CreateSeasonForLeagueResult,
type ListSeasonsForLeagueResult,
type GetSeasonDetailsResult,
type ManageSeasonLifecycleResult,
type CreateSeasonForLeagueErrorCode,
type ListSeasonsForLeagueErrorCode,
type GetSeasonDetailsErrorCode,

View File

@@ -1,4 +1,4 @@
import { Position } from '@/racing/domain/entities/result/Position';
import { Position } from '../../domain/entities/result/Position';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -8,6 +8,10 @@ import type { RaceEvent } from '../../domain/entities/RaceEvent';
import { IncidentCount } from '../../domain/entities/result/IncidentCount';
import type { Result as RaceResult } from '../../domain/entities/result/Result';
import { isLeagueStewardOrHigherRole } from '../../domain/types/LeagueRoles';
import { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export type SendFinalResultsInput = {
leagueId: string;

View File

@@ -1,4 +1,4 @@
import { isLeagueStewardOrHigherRole } from '@/racing/domain/types/LeagueRoles';
import { isLeagueStewardOrHigherRole } from '../../domain/types/LeagueRoles';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
@@ -8,6 +8,11 @@ import type { RaceEvent } from '../../domain/entities/RaceEvent';
import { IncidentCount } from '../../domain/entities/result/IncidentCount';
import { Position } from '../../domain/entities/result/Position';
import type { Result as RaceResult } from '../../domain/entities/result/Result';
import { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import { ResultRepository } from '../../domain/repositories/ResultRepository';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { DriverRepository } from '../../domain/repositories/DriverRepository';
export type SendPerformanceSummaryInput = {
leagueId: string;
@@ -87,7 +92,7 @@ export class SendPerformanceSummaryUseCase {
}
const results = await this.resultRepository.findByRaceId(mainRaceSession.id);
const driverResult = results.find(r => r.driverId.toString() === input.driverId);
const driverResult = results.find((r: RaceResult) => r.driverId.toString() === input.driverId);
if (!driverResult) {
return Result.err({

View File

@@ -407,7 +407,6 @@ describe('TeamRankingUseCase', () => {
describe('error handling', () => {
it('should handle repository errors gracefully', async () => {
// Mock repository to throw error
const originalFindAll = mockTeamRepo.findAll.bind(mockTeamRepo);
mockTeamRepo.findAll = async () => {
throw new Error('Repository connection failed');
};

View File

@@ -7,7 +7,7 @@ import { TeamRatingFactoryUseCase } from './TeamRatingFactoryUseCase';
class MockTeamRaceResultsProvider implements TeamRaceResultsProvider {
private results: TeamDrivingRaceFactsDto | null = null;
async getTeamRaceResults(raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
async getTeamRaceResults(_raceId: string): Promise<TeamDrivingRaceFactsDto | null> {
return this.results;
}

View File

@@ -6,12 +6,13 @@
* Mirrors the user rating factory pattern.
*/
import { TeamDrivingRatingEventFactory } from '@/racing/domain/services/TeamDrivingRatingEventFactory';
import { TeamDrivingRatingEventFactory } from '../../domain/services/TeamDrivingRatingEventFactory';
import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
import type { Logger } from '@core/shared/domain/Logger';
import { TeamRaceResultsProvider } from '../ports/TeamRaceResultsProvider';
export interface TeamRatingFactoryInput {
raceId: string;

View File

@@ -4,7 +4,13 @@ import type {
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
export interface TransferLeagueOwnershipInput {
leagueId: string;
currentOwnerId: string;
newOwnerId: string;
}
export type TransferLeagueOwnershipResult = {
leagueId: string;

View File

@@ -1,8 +1,12 @@
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
export interface UnpublishLeagueSeasonScheduleInput {
leagueId: string;
seasonId: string;
}
export type UnpublishLeagueSeasonScheduleResult = {
success: true;

View File

@@ -1,7 +1,7 @@
import { Result } from '@core/shared/domain/Result';
import type { UseCase } from '@core/shared/application/UseCase';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application/Logger';
import type { Logger } from '@core/shared/domain/Logger';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { Driver } from '../../domain/entities/Driver';

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { describe, it, expect, vi } from 'vitest';
import {
UpdateLeagueMemberRoleUseCase,
type UpdateLeagueMemberRoleInput,
type UpdateLeagueMemberRoleResult,
type UpdateLeagueMemberRoleErrorCode,
} from './UpdateLeagueMemberRoleUseCase';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
@@ -22,9 +21,9 @@ describe('UpdateLeagueMemberRoleUseCase', () => {
const mockLeagueMembershipRepository = {
getLeagueMembers: vi.fn().mockResolvedValue([mockMembership]),
saveMembership: vi.fn().mockResolvedValue(undefined),
} as any;
};
const useCase = new UpdateLeagueMemberRoleUseCase(mockLeagueMembershipRepository);
const useCase = new UpdateLeagueMemberRoleUseCase(mockLeagueMembershipRepository as unknown as LeagueMembershipRepository);
const input: UpdateLeagueMemberRoleInput = {
leagueId: 'league-1',

View File

@@ -2,9 +2,11 @@ import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonScheduleGenerator } from '@/racing/domain/services/SeasonScheduleGenerator';
import { SeasonScheduleGenerator } from '../../domain/services/SeasonScheduleGenerator';
import { Race } from '../../domain/entities/Race';
import type { Season } from '../../domain/entities/season/Season';
import { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import { RaceRepository } from '../../domain/repositories/RaceRepository';
export type UpdateLeagueSeasonScheduleRaceInput = {
leagueId: string;

View File

@@ -26,11 +26,6 @@ describe('UpdateTeamUseCase', () => {
getMembership: vi.fn().mockResolvedValue(mockMembership),
} as unknown as TeamMembershipRepository;
const present = vi.fn<(data: UpdateTeamResult) => void>();
const output: { present: typeof present } = {
present,
};
const useCase = new UpdateTeamUseCase(mockTeamRepository, mockMembershipRepository);
const command: UpdateTeamInput = {
@@ -78,7 +73,6 @@ describe('UpdateTeamUseCase', () => {
const error = result.unwrapErr() as ApplicationErrorCode<UpdateTeamErrorCode, { message: string }>;
expect(error.code).toBe('PERMISSION_DENIED');
expect(error.details?.message).toBe('User does not have permission to update this team');
expect(present).not.toHaveBeenCalled();
});
it('returns error if team not found', async () => {
@@ -94,11 +88,6 @@ describe('UpdateTeamUseCase', () => {
getMembership: vi.fn().mockResolvedValue(mockMembership),
} as unknown as TeamMembershipRepository;
const present = vi.fn<(data: UpdateTeamResult) => void>();
const output: { present: typeof present } = {
present,
};
const useCase = new UpdateTeamUseCase(mockTeamRepository, mockMembershipRepository);
const command: UpdateTeamInput = {
@@ -113,7 +102,6 @@ describe('UpdateTeamUseCase', () => {
const error = result.unwrapErr() as ApplicationErrorCode<UpdateTeamErrorCode, { message: string }>;
expect(error.code).toBe('TEAM_NOT_FOUND');
expect(error.details?.message).toBe('Team not found');
expect(present).not.toHaveBeenCalled();
});
it('returns repository error on unexpected failure', async () => {
@@ -129,11 +117,6 @@ describe('UpdateTeamUseCase', () => {
getMembership: vi.fn().mockResolvedValue(mockMembership),
} as unknown as TeamMembershipRepository;
const present = vi.fn<(data: UpdateTeamResult) => void>();
const output: { present: typeof present } = {
present,
};
const useCase = new UpdateTeamUseCase(mockTeamRepository, mockMembershipRepository);
const command: UpdateTeamInput = {
@@ -148,6 +131,5 @@ describe('UpdateTeamUseCase', () => {
const error = result.unwrapErr() as ApplicationErrorCode<UpdateTeamErrorCode, { message: string }>;
expect(error.code).toBe('REPOSITORY_ERROR');
expect(error.details?.message).toBe('db error');
expect(present).not.toHaveBeenCalled();
});
});

View File

@@ -10,11 +10,20 @@ import {
type WithdrawFromLeagueWalletInput
} from './WithdrawFromLeagueWalletUseCase';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import { TransactionRepository } from '../../domain/repositories/TransactionRepository';
describe('WithdrawFromLeagueWalletUseCase', () => {
let leagueRepository: { findById: Mock };
let walletRepository: { findByLeagueId: Mock; update: Mock };
let transactionRepository: { create: Mock };
let logger: Logger & { error: Mock };
let logger: {
debug: Mock;
info: Mock;
warn: Mock;
error: Mock;
};
let useCase: WithdrawFromLeagueWalletUseCase;
beforeEach(() => {
@@ -27,12 +36,14 @@ describe('WithdrawFromLeagueWalletUseCase', () => {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn()
} as any;
};
useCase = new WithdrawFromLeagueWalletUseCase(leagueRepository as any,
walletRepository as any,
transactionRepository as any,
logger);
useCase = new WithdrawFromLeagueWalletUseCase(
leagueRepository as unknown as LeagueRepository,
walletRepository as unknown as LeagueWalletRepository,
transactionRepository as unknown as TransactionRepository,
logger as unknown as Logger
);
});
it('returns LEAGUE_NOT_FOUND when league is missing', async () => {

View File

@@ -1,10 +1,13 @@
import { LeagueWalletId } from '@/racing/domain/entities/league-wallet/LeagueWalletId';
import { LeagueWalletId } from '../../domain/entities/league-wallet/LeagueWalletId';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Transaction } from '../../domain/entities/league-wallet/Transaction';
import { TransactionId } from '../../domain/entities/league-wallet/TransactionId';
import { Money } from '../../domain/value-objects/Money';
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import { TransactionRepository } from '../../domain/repositories/TransactionRepository';
export type WithdrawFromLeagueWalletInput = {
leagueId: string;

View File

@@ -2,18 +2,22 @@ import { describe, it, expect, vi, beforeEach } from 'vitest';
import {
WithdrawFromRaceUseCase,
type WithdrawFromRaceInput,
type WithdrawFromRaceResult,
type WithdrawFromRaceErrorCode,
} from './WithdrawFromRaceUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { Logger } from '@core/shared/application/Logger';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
describe('WithdrawFromRaceUseCase', () => {
let raceRepository: { findById: ReturnType<typeof vi.fn> };
let registrationRepository: { isRegistered: ReturnType<typeof vi.fn>; withdraw: ReturnType<typeof vi.fn> };
let logger: Logger;
let logger: {
debug: ReturnType<typeof vi.fn>;
info: ReturnType<typeof vi.fn>;
warn: ReturnType<typeof vi.fn>;
error: ReturnType<typeof vi.fn>;
};
beforeEach(() => {
raceRepository = {
@@ -30,13 +34,15 @@ describe('WithdrawFromRaceUseCase', () => {
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
} as any;
};
});
const createUseCase = () =>
new WithdrawFromRaceUseCase(raceRepository as any,
registrationRepository as any,
logger);
new WithdrawFromRaceUseCase(
raceRepository as unknown as RaceRepository,
registrationRepository as unknown as RaceRegistrationRepository,
logger as unknown as Logger
);
it('withdraws from race successfully', async () => {
const race = {

View File

@@ -2,7 +2,7 @@ import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceRegistrationRepository } from '@core/racing/domain/repositories/RaceRegistrationRepository';
import type { RaceRepository } from '@core/racing/domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/application/Logger';
import type { Logger } from '@core/shared/domain/Logger';
export type WithdrawFromRaceInput = {
raceId: string;