website refactor
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { ListUsersUseCase, ListUsersResult } from './ListUsersUseCase';
|
||||
import { ListUsersUseCase } from './ListUsersUseCase';
|
||||
import { AdminUserRepository } from '../ports/AdminUserRepository';
|
||||
import { AdminUser } from '../../domain/entities/AdminUser';
|
||||
import { AuthorizationService } from '../../domain/services/AuthorizationService';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { DomainErrorProps, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
|
||||
export abstract class AdminDomainError extends Error implements DomainErrorProps<CommonDomainErrorKind> {
|
||||
export abstract class AdminDomainError extends Error implements DomainError<CommonDomainErrorKind> {
|
||||
readonly type = 'domain' as const;
|
||||
readonly context = 'admin-domain';
|
||||
abstract readonly kind: CommonDomainErrorKind;
|
||||
|
||||
@@ -5,8 +5,7 @@
|
||||
* Uses EligibilityEvaluator to provide explainable results.
|
||||
*/
|
||||
|
||||
import { EvaluationResultDto } from '../dtos/EvaluationResultDto';
|
||||
import { EligibilityFilterDto } from '../dtos/EligibilityFilterDto';
|
||||
import { EvaluationResultDto, EligibilityFilterDto } from '../../domain/types/Eligibility';
|
||||
import { EligibilityEvaluator, RatingData } from '../../domain/services/EligibilityEvaluator';
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Tests for GetUserRatingsSummaryQuery
|
||||
*/
|
||||
|
||||
import { describe, expect, it, beforeEach, vi } from 'vitest';
|
||||
import { describe, expect, it, beforeEach, vi, type Mock } from 'vitest';
|
||||
import { GetUserRatingsSummaryQuery, GetUserRatingsSummaryQueryHandler } from './GetUserRatingsSummaryQuery';
|
||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
|
||||
@@ -13,11 +13,16 @@ import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||
|
||||
import { UserId } from '../../domain/value-objects/UserId';
|
||||
|
||||
describe('GetUserRatingsSummaryQuery', () => {
|
||||
let mockUserRatingRepo: any;
|
||||
let mockExternalRatingRepo: any;
|
||||
let mockRatingEventRepo: any;
|
||||
let mockUserRatingRepo: { findByUserId: Mock };
|
||||
let mockExternalRatingRepo: { findByUserId: Mock };
|
||||
let mockRatingEventRepo: { getAllByUserId: Mock };
|
||||
let handler: GetUserRatingsSummaryQueryHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -32,9 +37,9 @@ describe('GetUserRatingsSummaryQuery', () => {
|
||||
};
|
||||
|
||||
handler = new GetUserRatingsSummaryQueryHandler(
|
||||
mockUserRatingRepo,
|
||||
mockExternalRatingRepo,
|
||||
mockRatingEventRepo
|
||||
mockUserRatingRepo as unknown as UserRatingRepository,
|
||||
mockExternalRatingRepo as unknown as ExternalGameRatingRepository,
|
||||
mockRatingEventRepo as unknown as RatingEventRepository
|
||||
);
|
||||
});
|
||||
|
||||
@@ -49,7 +54,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
||||
// Mock external ratings
|
||||
const gameKey = GameKey.create('iracing');
|
||||
const profile = ExternalGameRatingProfile.create({
|
||||
userId: { toString: () => userId } as any,
|
||||
userId: UserId.fromString(userId),
|
||||
gameKey,
|
||||
ratings: new Map([
|
||||
['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
|
||||
@@ -109,7 +114,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
||||
|
||||
// Multiple game profiles
|
||||
const iracingProfile = ExternalGameRatingProfile.create({
|
||||
userId: { toString: () => userId } as any,
|
||||
userId: UserId.fromString(userId),
|
||||
gameKey: GameKey.create('iracing'),
|
||||
ratings: new Map([
|
||||
['iRating', ExternalRating.create(GameKey.create('iracing'), 'iRating', 2200)],
|
||||
@@ -118,7 +123,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
||||
});
|
||||
|
||||
const assettoProfile = ExternalGameRatingProfile.create({
|
||||
userId: { toString: () => userId } as any,
|
||||
userId: UserId.fromString(userId),
|
||||
gameKey: GameKey.create('assetto'),
|
||||
ratings: new Map([
|
||||
['rating', ExternalRating.create(GameKey.create('assetto'), 'rating', 85)],
|
||||
|
||||
@@ -134,14 +134,16 @@ class MockUserRatingRepository {
|
||||
}
|
||||
}
|
||||
|
||||
import { CreateRatingEventDto } from '../dtos/CreateRatingEventDto';
|
||||
|
||||
// Mock AppendRatingEventsUseCase
|
||||
class MockAppendRatingEventsUseCase {
|
||||
constructor(
|
||||
private ratingEventRepository: any,
|
||||
private userRatingRepository: any
|
||||
private ratingEventRepository: MockRatingEventRepository,
|
||||
private userRatingRepository: MockUserRatingRepository
|
||||
) {}
|
||||
|
||||
async execute(input: any): Promise<any> {
|
||||
async execute(input: { userId: string; events: CreateRatingEventDto[] }): Promise<{ events: string[]; snapshotUpdated: boolean }> {
|
||||
const events: RatingEvent[] = [];
|
||||
|
||||
// Create events from input
|
||||
@@ -195,7 +197,6 @@ describe('Admin Vote Session Use Cases', () => {
|
||||
// Use dates relative to current time so close() works
|
||||
const now = new Date(Date.now() - 86400000); // Yesterday
|
||||
const tomorrow = new Date(Date.now() + 86400000); // Tomorrow
|
||||
const dayAfter = new Date(Date.now() + 86400000 * 2); // Day after tomorrow
|
||||
|
||||
beforeEach(() => {
|
||||
mockSessionRepo = new MockAdminVoteSessionRepository();
|
||||
|
||||
@@ -3,9 +3,6 @@ import { describe, expect, it, vi, beforeEach } from 'vitest';
|
||||
import { AppendRatingEventsUseCase, AppendRatingEventsInput } from './AppendRatingEventsUseCase';
|
||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
|
||||
describe('AppendRatingEventsUseCase', () => {
|
||||
let mockEventRepo: Partial<RatingEventRepository>;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { RatingEventRepository } from '../../domain/repositories/RatingEventRepo
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEvent, type RatingEventProps } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||
@@ -97,7 +97,7 @@ export class AppendRatingEventsUseCase {
|
||||
}
|
||||
|
||||
private createEventFromDto(dto: CreateRatingEventDto) {
|
||||
const props: any = {
|
||||
const props: RatingEventProps = {
|
||||
id: RatingEventId.generate(),
|
||||
userId: dto.userId,
|
||||
dimension: RatingDimensionKey.create(dto.dimension),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { AdminTrustRatingCalculator } from '../../domain/services/AdminTrustRatingCalculator';
|
||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||
import { CloseAdminVoteSessionInput, CloseAdminVoteSessionOutput } from '../dtos/AdminVoteSessionDto';
|
||||
import { AdminVoteSession, AdminVoteOutcome } from '../../domain/entities/AdminVoteSession';
|
||||
|
||||
/**
|
||||
* Use Case: CloseAdminVoteSessionUseCase
|
||||
@@ -26,7 +26,6 @@ export class CloseAdminVoteSessionUseCase {
|
||||
private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
|
||||
private readonly ratingEventRepository: RatingEventRepository,
|
||||
private readonly userRatingRepository: UserRatingRepository,
|
||||
private readonly appendRatingEventsUseCase: any, // Will be typed properly in integration
|
||||
) {}
|
||||
|
||||
async execute(input: CloseAdminVoteSessionInput): Promise<CloseAdminVoteSessionOutput> {
|
||||
@@ -117,7 +116,7 @@ export class CloseAdminVoteSessionUseCase {
|
||||
* Events are created for the admin being voted on
|
||||
* Per plans: no events are created for tie outcomes
|
||||
*/
|
||||
private async createRatingEvents(session: any, outcome: any): Promise<number> {
|
||||
private async createRatingEvents(session: AdminVoteSession, outcome: AdminVoteOutcome): Promise<number> {
|
||||
let eventsCreated = 0;
|
||||
|
||||
// Don't create events for tie outcomes
|
||||
|
||||
@@ -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 { User } from '../../domain/entities/User';
|
||||
import { UserRepository } from '../../domain/repositories/UserRepository';
|
||||
|
||||
export type GetCurrentSessionInput = {
|
||||
userId: string;
|
||||
|
||||
@@ -4,11 +4,12 @@
|
||||
* Authenticates a user with email and password.
|
||||
*/
|
||||
|
||||
import { PasswordHash } from '@/identity/domain/value-objects/PasswordHash';
|
||||
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
|
||||
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 { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
import { UserRepository } from '../../domain/repositories/UserRepository';
|
||||
|
||||
export type LoginWithEmailInput = {
|
||||
email: string;
|
||||
|
||||
@@ -6,8 +6,6 @@ import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||
|
||||
// In-memory implementations for integration testing
|
||||
class InMemoryRaceResultsProvider implements RaceResultsProvider {
|
||||
|
||||
@@ -6,8 +6,7 @@ import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
|
||||
// Mock implementations
|
||||
class MockRaceResultsProvider implements RaceResultsProvider {
|
||||
@@ -17,11 +16,11 @@ class MockRaceResultsProvider implements RaceResultsProvider {
|
||||
this.results = results;
|
||||
}
|
||||
|
||||
async getRaceResults(raceId: string): Promise<RaceResultsData | null> {
|
||||
async getRaceResults(_raceId: string): Promise<RaceResultsData | null> {
|
||||
return this.results;
|
||||
}
|
||||
|
||||
async hasRaceResults(raceId: string): Promise<boolean> {
|
||||
async hasRaceResults(_raceId: string): Promise<boolean> {
|
||||
return this.results !== null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ import { RaceResultsProvider } from '../ports/RaceResultsProvider';
|
||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||
import { DrivingRatingCalculator } from '../../domain/services/DrivingRatingCalculator';
|
||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
||||
import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
||||
import { RecordRaceRatingEventsInput, RecordRaceRatingEventsOutput } from '../dtos/RecordRaceRatingEventsDto';
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
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 { StoredUser } from '../../domain/repositories/UserRepository';
|
||||
import type { StoredUser, UserRepository } from '../../domain/repositories/UserRepository';
|
||||
import type { AuthenticatedUser } from '../ports/IdentityProviderPort';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
|
||||
|
||||
@@ -1,27 +1,30 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { UpsertExternalGameRatingUseCase } from './UpsertExternalGameRatingUseCase';
|
||||
import { UpsertExternalGameRatingInput } from '../dtos/UpsertExternalGameRatingDto';
|
||||
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
|
||||
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
||||
|
||||
// Mock repository for integration test
|
||||
class MockExternalGameRatingRepository {
|
||||
private profiles = new Map<string, any>();
|
||||
private profiles = new Map<string, ExternalGameRatingProfile>();
|
||||
|
||||
private getKey(userId: string, gameKey: string): string {
|
||||
return `${userId}|${gameKey}`;
|
||||
}
|
||||
|
||||
async findByUserIdAndGameKey(userId: string, gameKey: string): Promise<any | null> {
|
||||
async findByUserIdAndGameKey(userId: string, gameKey: string): Promise<ExternalGameRatingProfile | null> {
|
||||
return this.profiles.get(this.getKey(userId, gameKey)) || null;
|
||||
}
|
||||
|
||||
async findByUserId(userId: string): Promise<any[]> {
|
||||
return Array.from(this.profiles.values()).filter((p: any) => p.userId.toString() === userId);
|
||||
async findByUserId(userId: string): Promise<ExternalGameRatingProfile[]> {
|
||||
return Array.from(this.profiles.values()).filter((p: ExternalGameRatingProfile) => p.userId.toString() === userId);
|
||||
}
|
||||
|
||||
async findByGameKey(gameKey: string): Promise<any[]> {
|
||||
return Array.from(this.profiles.values()).filter((p: any) => p.gameKey.toString() === gameKey);
|
||||
async findByGameKey(gameKey: string): Promise<ExternalGameRatingProfile[]> {
|
||||
return Array.from(this.profiles.values()).filter((p: ExternalGameRatingProfile) => p.gameKey.toString() === gameKey);
|
||||
}
|
||||
|
||||
async save(profile: any): Promise<any> {
|
||||
async save(profile: ExternalGameRatingProfile): Promise<ExternalGameRatingProfile> {
|
||||
const key = this.getKey(profile.userId.toString(), profile.gameKey.toString());
|
||||
this.profiles.set(key, profile);
|
||||
return profile;
|
||||
@@ -50,7 +53,7 @@ describe('UpsertExternalGameRatingUseCase - Integration', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
repository = new MockExternalGameRatingRepository();
|
||||
useCase = new UpsertExternalGameRatingUseCase(repository as any);
|
||||
useCase = new UpsertExternalGameRatingUseCase(repository as unknown as ExternalGameRatingRepository);
|
||||
});
|
||||
|
||||
describe('Full upsert flow', () => {
|
||||
|
||||
@@ -6,11 +6,19 @@ import { GameKey } from '../../domain/value-objects/GameKey';
|
||||
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
|
||||
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
|
||||
import { UpsertExternalGameRatingInput } from '../dtos/UpsertExternalGameRatingDto';
|
||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||
import { vi, describe, it, expect, beforeEach, type Mock } from 'vitest';
|
||||
|
||||
describe('UpsertExternalGameRatingUseCase', () => {
|
||||
let useCase: UpsertExternalGameRatingUseCase;
|
||||
let mockRepository: ExternalGameRatingRepository;
|
||||
let mockRepository: {
|
||||
findByUserIdAndGameKey: Mock;
|
||||
findByUserId: Mock;
|
||||
findByGameKey: Mock;
|
||||
save: Mock;
|
||||
saveMany: Mock;
|
||||
delete: Mock;
|
||||
exists: Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockRepository = {
|
||||
@@ -21,9 +29,17 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
saveMany: vi.fn(),
|
||||
delete: vi.fn(),
|
||||
exists: vi.fn(),
|
||||
} as any;
|
||||
} as unknown as {
|
||||
findByUserIdAndGameKey: Mock;
|
||||
findByUserId: Mock;
|
||||
findByGameKey: Mock;
|
||||
save: Mock;
|
||||
saveMany: Mock;
|
||||
delete: Mock;
|
||||
exists: Mock;
|
||||
};
|
||||
|
||||
useCase = new UpsertExternalGameRatingUseCase(mockRepository);
|
||||
useCase = new UpsertExternalGameRatingUseCase(mockRepository as unknown as ExternalGameRatingRepository);
|
||||
});
|
||||
|
||||
describe('execute', () => {
|
||||
@@ -42,8 +58,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
||||
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
@@ -61,8 +77,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
|
||||
it('should update existing profile', async () => {
|
||||
const existingProfile = createTestProfile('user-123', 'iracing');
|
||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(existingProfile);
|
||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
||||
mockRepository.findByUserIdAndGameKey.mockResolvedValue(existingProfile);
|
||||
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||
|
||||
const input: UpsertExternalGameRatingInput = {
|
||||
userId: 'user-123',
|
||||
@@ -211,7 +227,7 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockRepository.findByUserIdAndGameKey as any).mockRejectedValue(new Error('Database connection failed'));
|
||||
mockRepository.findByUserIdAndGameKey.mockRejectedValue(new Error('Database connection failed'));
|
||||
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
@@ -232,8 +248,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
||||
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
@@ -254,8 +270,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
||||
},
|
||||
};
|
||||
|
||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
||||
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||
|
||||
const result = await useCase.execute(input);
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { DomainError } from '@core/shared/errors/DomainError';
|
||||
import type { CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
|
||||
export abstract class IdentityDomainError extends Error implements DomainError<CommonDomainErrorKind> {
|
||||
readonly type = 'domain' as const;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { ExternalGameRatingRepository } from './ExternalGameRatingRepository';
|
||||
import { ExternalGameRatingProfile } from '../entities/ExternalGameRatingProfile';
|
||||
import { UserId } from '../value-objects/UserId';
|
||||
@@ -182,11 +183,6 @@ describe('ExternalGameRatingRepository', () => {
|
||||
await repository.save(profile);
|
||||
|
||||
// Update the profile
|
||||
const updatedProvenance = ExternalRatingProvenance.create({
|
||||
source: 'iracing',
|
||||
lastSyncedAt: new Date('2024-01-02'),
|
||||
verified: true,
|
||||
});
|
||||
profile.updateLastSyncedAt(new Date('2024-01-02'));
|
||||
profile.markVerified();
|
||||
|
||||
@@ -290,7 +286,7 @@ describe('ExternalGameRatingRepository', () => {
|
||||
lastSyncedAt: new Date('2024-01-01'),
|
||||
verified: false,
|
||||
});
|
||||
profile2.updateRatings(profile2.ratings, profile2Provenance);
|
||||
profile2.updateRatings(new Map(profile2.ratings), profile2Provenance);
|
||||
|
||||
await repository.saveMany([profile1, profile2]);
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@ import { RatingEvent } from '../entities/RatingEvent';
|
||||
import { RatingEventId } from '../value-objects/RatingEventId';
|
||||
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
|
||||
import { RatingDelta } from '../value-objects/RatingDelta';
|
||||
import { AdminVoteOutcome } from '../entities/AdminVoteSession';
|
||||
|
||||
describe('AdminTrustRatingCalculator', () => {
|
||||
describe('calculate', () => {
|
||||
@@ -316,13 +315,12 @@ describe('AdminTrustRatingCalculator', () => {
|
||||
|
||||
it('should default to zero for unknown action type', () => {
|
||||
const input: SystemSignalInput = {
|
||||
actionType: 'sla_response' as any,
|
||||
actionType: 'unknown_type' as unknown as SystemSignalInput['actionType'],
|
||||
details: {},
|
||||
};
|
||||
|
||||
// Override for test
|
||||
const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input);
|
||||
expect(delta.value).toBe(5); // Known type
|
||||
expect(delta.value).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { RatingEvent } from '../entities/RatingEvent';
|
||||
import { DrivingReasonCode } from '../value-objects/DrivingReasonCode';
|
||||
import { RatingDelta } from '../value-objects/RatingDelta';
|
||||
|
||||
/**
|
||||
* Input DTO for driving rating calculation from race facts
|
||||
@@ -71,7 +69,7 @@ export class DrivingRatingCalculator {
|
||||
const fieldStrength = facts.results.length > 0
|
||||
? (facts.results
|
||||
.filter(r => r.status === 'finished')
|
||||
.reduce((sum, r) => sum + (r.sof || this.estimateDriverRating(r.userId)), 0) /
|
||||
.reduce((sum, r) => sum + (r.sof || this.estimateDriverRating()), 0) /
|
||||
Math.max(1, facts.results.filter(r => r.status === 'finished').length))
|
||||
: 0;
|
||||
|
||||
@@ -297,7 +295,7 @@ export class DrivingRatingCalculator {
|
||||
* Estimate driver rating for SoF calculation
|
||||
* This is a placeholder - in real implementation, would query user rating snapshot
|
||||
*/
|
||||
private static estimateDriverRating(userId: string): number {
|
||||
private static estimateDriverRating(): number {
|
||||
// Default rating for new drivers
|
||||
return 50;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
import { EligibilityEvaluator, RatingData } from './EligibilityEvaluator';
|
||||
import { EligibilityFilterDto } from '../../application/dtos/EligibilityFilterDto';
|
||||
import { EligibilityFilterDto } from '../types/Eligibility';
|
||||
|
||||
describe('EligibilityEvaluator', () => {
|
||||
let evaluator: EligibilityEvaluator;
|
||||
|
||||
@@ -6,8 +6,13 @@
|
||||
* Provides explainable results with detailed reasons.
|
||||
*/
|
||||
|
||||
import { EvaluationResultDto, EvaluationReason } from '../../application/dtos/EvaluationResultDto';
|
||||
import { EligibilityFilterDto, ParsedEligibilityFilter, EligibilityCondition } from '../../application/dtos/EligibilityFilterDto';
|
||||
import {
|
||||
EvaluationResultDto,
|
||||
EvaluationReason,
|
||||
EligibilityFilterDto,
|
||||
ParsedEligibilityFilter,
|
||||
EligibilityCondition
|
||||
} from '../types/Eligibility';
|
||||
|
||||
export interface RatingData {
|
||||
platform: {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
||||
import { RatingUpdateService } from './RatingUpdateService';
|
||||
import type { UserRatingRepository } from '../repositories/UserRatingRepository';
|
||||
import type { RatingEventRepository } from '../repositories/RatingEventRepository';
|
||||
@@ -10,8 +10,8 @@ import { RatingDelta } from '../value-objects/RatingDelta';
|
||||
|
||||
describe('RatingUpdateService - Slice 7 Evolution', () => {
|
||||
let service: RatingUpdateService;
|
||||
let userRatingRepository: any;
|
||||
let ratingEventRepository: any;
|
||||
let userRatingRepository: { findByUserId: Mock; save: Mock };
|
||||
let ratingEventRepository: { save: Mock; getAllByUserId: Mock };
|
||||
|
||||
beforeEach(() => {
|
||||
userRatingRepository = {
|
||||
@@ -24,7 +24,10 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
|
||||
getAllByUserId: vi.fn(),
|
||||
};
|
||||
|
||||
service = new RatingUpdateService(userRatingRepository, ratingEventRepository);
|
||||
service = new RatingUpdateService(
|
||||
userRatingRepository as unknown as UserRatingRepository,
|
||||
ratingEventRepository as unknown as RatingEventRepository
|
||||
);
|
||||
});
|
||||
|
||||
describe('recordRaceRatingEvents - Ledger-based approach', () => {
|
||||
@@ -150,7 +153,7 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
|
||||
|
||||
// Verify DNF penalty event was created
|
||||
const savedEvents = ratingEventRepository.save.mock.calls.map(call => call[0]);
|
||||
const hasDnfPenalty = savedEvents.some((event: any) =>
|
||||
const hasDnfPenalty = savedEvents.some((event: RatingEvent) =>
|
||||
event.reason.code === 'DRIVING_DNF_PENALTY'
|
||||
);
|
||||
expect(hasDnfPenalty).toBe(true);
|
||||
|
||||
55
core/identity/domain/types/Eligibility.ts
Normal file
55
core/identity/domain/types/Eligibility.ts
Normal file
@@ -0,0 +1,55 @@
|
||||
/**
|
||||
* Domain Types: Eligibility
|
||||
*
|
||||
* DSL-based eligibility filter and evaluation results.
|
||||
*/
|
||||
|
||||
export interface EligibilityFilterDto {
|
||||
/**
|
||||
* DSL expression for eligibility rules
|
||||
*/
|
||||
dsl: string;
|
||||
|
||||
/**
|
||||
* Optional context for evaluation
|
||||
*/
|
||||
context?: {
|
||||
userId?: string;
|
||||
leagueId?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EligibilityCondition {
|
||||
target: 'platform' | 'external';
|
||||
dimension?: string;
|
||||
game?: string;
|
||||
operator: string;
|
||||
expected: number | [number, number];
|
||||
}
|
||||
|
||||
export interface ParsedEligibilityFilter {
|
||||
conditions: EligibilityCondition[];
|
||||
logicalOperator: 'AND' | 'OR';
|
||||
}
|
||||
|
||||
export interface EvaluationReason {
|
||||
target: string;
|
||||
operator: string;
|
||||
expected: number | [number, number];
|
||||
actual: number;
|
||||
failed: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export interface EvaluationResultDto {
|
||||
eligible: boolean;
|
||||
reasons: EvaluationReason[];
|
||||
summary: string;
|
||||
evaluatedAt: string;
|
||||
metadata?: {
|
||||
userId?: string;
|
||||
filter?: string;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AdminTrustReasonCode } from './AdminTrustReasonCode';
|
||||
import { AdminTrustReasonCode, type AdminTrustReasonCodeValue } from './AdminTrustReasonCode';
|
||||
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('AdminTrustReasonCode', () => {
|
||||
describe('create', () => {
|
||||
@@ -71,7 +72,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
||||
];
|
||||
voteCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isVoteOutcome()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -82,7 +83,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_REVERSAL_PENALTY',
|
||||
];
|
||||
nonVoteCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isVoteOutcome()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -97,7 +98,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||
];
|
||||
systemCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isSystemSignal()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -108,7 +109,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
||||
];
|
||||
nonSystemCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isSystemSignal()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -122,7 +123,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
||||
];
|
||||
positiveCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isPositive()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -134,7 +135,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||
];
|
||||
nonPositiveCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isPositive()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -148,7 +149,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||
];
|
||||
negativeCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isNegative()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -160,7 +161,7 @@ describe('AdminTrustReasonCode', () => {
|
||||
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
||||
];
|
||||
nonNegativeCodes.forEach(codeStr => {
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
||||
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||
expect(code.isNegative()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DrivingReasonCode } from './DrivingReasonCode';
|
||||
import { DrivingReasonCode, type DrivingReasonCodeValue } from './DrivingReasonCode';
|
||||
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
||||
import { describe, it, expect } from 'vitest';
|
||||
|
||||
describe('DrivingReasonCode', () => {
|
||||
describe('create', () => {
|
||||
@@ -77,7 +78,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_PACE_RELATIVE_GAIN',
|
||||
];
|
||||
performanceCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isPerformance()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -89,7 +90,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||
];
|
||||
nonPerformanceCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isPerformance()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -103,7 +104,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_PENALTY_INVOLVEMENT_PENALTY',
|
||||
];
|
||||
cleanDrivingCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isCleanDriving()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -115,7 +116,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||
];
|
||||
nonCleanDrivingCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isCleanDriving()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -131,7 +132,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||
];
|
||||
reliabilityCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isReliability()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -142,7 +143,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_INCIDENTS_PENALTY',
|
||||
];
|
||||
nonReliabilityCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isReliability()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -160,7 +161,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_AFK_PENALTY',
|
||||
];
|
||||
penaltyCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isPenalty()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -172,7 +173,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||
];
|
||||
nonPenaltyCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isPenalty()).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -187,7 +188,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_PACE_RELATIVE_GAIN',
|
||||
];
|
||||
bonusCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isBonus()).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -198,7 +199,7 @@ describe('DrivingReasonCode', () => {
|
||||
'DRIVING_DNS_PENALTY',
|
||||
];
|
||||
nonBonusCodes.forEach(codeStr => {
|
||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
||||
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||
expect(code.isBonus()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
*/
|
||||
|
||||
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 { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||
|
||||
export interface DeleteMediaInput {
|
||||
mediaId: string;
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { AvatarRepository } from '../../domain/repositories/AvatarRepository';
|
||||
|
||||
export interface GetAvatarInput {
|
||||
driverId: string;
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||
|
||||
export interface GetMediaInput {
|
||||
mediaId: string;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* Handles the business logic for requesting avatar generation from a face photo.
|
||||
*/
|
||||
|
||||
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';
|
||||
@@ -12,6 +12,7 @@ import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationR
|
||||
import type { RacingSuitColor } from '../../domain/types/AvatarGenerationRequest';
|
||||
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
|
||||
import type { FaceValidationPort } from '../ports/FaceValidationPort';
|
||||
import { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
|
||||
|
||||
export interface RequestAvatarGenerationInput {
|
||||
userId: string;
|
||||
|
||||
@@ -5,7 +5,9 @@
|
||||
*/
|
||||
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
|
||||
|
||||
export interface SelectAvatarInput {
|
||||
requestId: string;
|
||||
|
||||
@@ -4,12 +4,13 @@
|
||||
* Handles the business logic for updating a driver's avatar.
|
||||
*/
|
||||
|
||||
import { AvatarId } from '@/media/domain/value-objects/AvatarId';
|
||||
import { AvatarId } from '../../domain/value-objects/AvatarId';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Avatar } from '../../domain/entities/Avatar';
|
||||
import { AvatarRepository } from '../../domain/repositories/AvatarRepository';
|
||||
|
||||
export interface UpdateAvatarInput {
|
||||
driverId: string;
|
||||
|
||||
@@ -5,10 +5,12 @@
|
||||
*/
|
||||
|
||||
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';
|
||||
import { Media } from '../../domain/entities/Media';
|
||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||
|
||||
// Define Multer file type locally since @types/multer is not available
|
||||
export interface MulterFile {
|
||||
|
||||
@@ -4,12 +4,15 @@
|
||||
* Retrieves unread notifications for a recipient.
|
||||
*/
|
||||
|
||||
import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository';
|
||||
import { NotificationRepository } from '../../domain/repositories/NotificationRepository';
|
||||
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 { Notification } from '../../domain/entities/Notification';
|
||||
|
||||
export interface GetUnreadNotificationsInput {
|
||||
recipientId: string;
|
||||
}
|
||||
|
||||
export interface GetUnreadNotificationsResult {
|
||||
notifications: Notification[];
|
||||
|
||||
@@ -4,11 +4,15 @@
|
||||
* Marks a notification as read.
|
||||
*/
|
||||
|
||||
import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository';
|
||||
import { NotificationRepository } from '../../domain/repositories/NotificationRepository';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
export interface MarkNotificationReadCommand {
|
||||
notificationId: string;
|
||||
recipientId: string;
|
||||
}
|
||||
|
||||
export interface MarkNotificationReadResult {
|
||||
notificationId: string;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
* Manages user notification preferences.
|
||||
*/
|
||||
|
||||
import { NotificationPreferenceRepository } from '@/notifications/domain/repositories/NotificationPreferenceRepository';
|
||||
import { NotificationChannel, NotificationType } from '@/notifications/domain/types/NotificationTypes';
|
||||
import { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
|
||||
import { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
|
||||
import type { Logger } from '@core/shared/domain/Logger';
|
||||
import { Result } from '@core/shared/domain/Result';
|
||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* based on their preferences.
|
||||
*/
|
||||
|
||||
import { NotificationChannel, NotificationType } from '@/notifications/domain/types/NotificationTypes';
|
||||
import { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
|
||||
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,8 @@ import { v4 as uuid } from 'uuid';
|
||||
import type { NotificationData } from '../../domain/entities/Notification';
|
||||
import { Notification } from '../../domain/entities/Notification';
|
||||
import type { NotificationDeliveryResult, NotificationGatewayRegistry } from '../ports/NotificationGateway';
|
||||
import { NotificationRepository } from '../../domain/repositories/NotificationRepository';
|
||||
import { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
|
||||
|
||||
export interface SendNotificationCommand {
|
||||
recipientId: string;
|
||||
@@ -93,7 +95,7 @@ export class SendNotificationUseCase {
|
||||
|
||||
// Check quiet hours (skip external channels during quiet hours)
|
||||
const effectiveChannels = preferences.isInQuietHours()
|
||||
? channels.filter(ch => ch === 'in_app')
|
||||
? channels.filter((ch: NotificationChannel) => ch === 'in_app')
|
||||
: channels;
|
||||
|
||||
// Ensure at least in_app is used
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import type { DomainError } from '@core/shared/errors/DomainError';
|
||||
import type { CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||
|
||||
/**
|
||||
* Domain Error: NotificationDomainError
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import * as useCases from './index';
|
||||
|
||||
describe('payments use-cases barrel exports', () => {
|
||||
it('re-exports all expected use cases', () => {
|
||||
const exported = useCases as unknown as Record<string, unknown>;
|
||||
|
||||
const expectedExports = [
|
||||
'AwardPrizeUseCase',
|
||||
'CreatePaymentUseCase',
|
||||
'CreatePrizeUseCase',
|
||||
'DeletePrizeUseCase',
|
||||
'GetMembershipFeesUseCase',
|
||||
'GetPaymentsUseCase',
|
||||
'GetPrizesUseCase',
|
||||
'GetSponsorBillingUseCase',
|
||||
'GetWalletUseCase',
|
||||
'ProcessWalletTransactionUseCase',
|
||||
'UpdateMemberPaymentUseCase',
|
||||
'UpdatePaymentStatusUseCase',
|
||||
'UpsertMembershipFeeUseCase',
|
||||
];
|
||||
|
||||
for (const name of expectedExports) {
|
||||
expect(exported[name], `missing export: ${name}`).toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,7 +0,0 @@
|
||||
import * as mod from '@core/payments/domain/entities/index';
|
||||
|
||||
describe('payments/domain/entities/index.ts', () => {
|
||||
it('imports', () => {
|
||||
expect(mod).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@@ -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
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 => {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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.' };
|
||||
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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' }) }
|
||||
);
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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',
|
||||
}));
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 });
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user