website refactor
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
import { vi, describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { ListUsersUseCase, ListUsersResult } from './ListUsersUseCase';
|
import { ListUsersUseCase } from './ListUsersUseCase';
|
||||||
import { AdminUserRepository } from '../ports/AdminUserRepository';
|
import { AdminUserRepository } from '../ports/AdminUserRepository';
|
||||||
import { AdminUser } from '../../domain/entities/AdminUser';
|
import { AdminUser } from '../../domain/entities/AdminUser';
|
||||||
import { AuthorizationService } from '../../domain/services/AuthorizationService';
|
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 type = 'domain' as const;
|
||||||
readonly context = 'admin-domain';
|
readonly context = 'admin-domain';
|
||||||
abstract readonly kind: CommonDomainErrorKind;
|
abstract readonly kind: CommonDomainErrorKind;
|
||||||
|
|||||||
@@ -5,8 +5,7 @@
|
|||||||
* Uses EligibilityEvaluator to provide explainable results.
|
* Uses EligibilityEvaluator to provide explainable results.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EvaluationResultDto } from '../dtos/EvaluationResultDto';
|
import { EvaluationResultDto, EligibilityFilterDto } from '../../domain/types/Eligibility';
|
||||||
import { EligibilityFilterDto } from '../dtos/EligibilityFilterDto';
|
|
||||||
import { EligibilityEvaluator, RatingData } from '../../domain/services/EligibilityEvaluator';
|
import { EligibilityEvaluator, RatingData } from '../../domain/services/EligibilityEvaluator';
|
||||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||||
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Tests for GetUserRatingsSummaryQuery
|
* 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 { GetUserRatingsSummaryQuery, GetUserRatingsSummaryQueryHandler } from './GetUserRatingsSummaryQuery';
|
||||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||||
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
|
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
|
||||||
@@ -13,11 +13,16 @@ import { RatingEvent } from '../../domain/entities/RatingEvent';
|
|||||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
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', () => {
|
describe('GetUserRatingsSummaryQuery', () => {
|
||||||
let mockUserRatingRepo: any;
|
let mockUserRatingRepo: { findByUserId: Mock };
|
||||||
let mockExternalRatingRepo: any;
|
let mockExternalRatingRepo: { findByUserId: Mock };
|
||||||
let mockRatingEventRepo: any;
|
let mockRatingEventRepo: { getAllByUserId: Mock };
|
||||||
let handler: GetUserRatingsSummaryQueryHandler;
|
let handler: GetUserRatingsSummaryQueryHandler;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -32,9 +37,9 @@ describe('GetUserRatingsSummaryQuery', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handler = new GetUserRatingsSummaryQueryHandler(
|
handler = new GetUserRatingsSummaryQueryHandler(
|
||||||
mockUserRatingRepo,
|
mockUserRatingRepo as unknown as UserRatingRepository,
|
||||||
mockExternalRatingRepo,
|
mockExternalRatingRepo as unknown as ExternalGameRatingRepository,
|
||||||
mockRatingEventRepo
|
mockRatingEventRepo as unknown as RatingEventRepository
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,7 +54,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
|||||||
// Mock external ratings
|
// Mock external ratings
|
||||||
const gameKey = GameKey.create('iracing');
|
const gameKey = GameKey.create('iracing');
|
||||||
const profile = ExternalGameRatingProfile.create({
|
const profile = ExternalGameRatingProfile.create({
|
||||||
userId: { toString: () => userId } as any,
|
userId: UserId.fromString(userId),
|
||||||
gameKey,
|
gameKey,
|
||||||
ratings: new Map([
|
ratings: new Map([
|
||||||
['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
|
['iRating', ExternalRating.create(gameKey, 'iRating', 2200)],
|
||||||
@@ -109,7 +114,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
|||||||
|
|
||||||
// Multiple game profiles
|
// Multiple game profiles
|
||||||
const iracingProfile = ExternalGameRatingProfile.create({
|
const iracingProfile = ExternalGameRatingProfile.create({
|
||||||
userId: { toString: () => userId } as any,
|
userId: UserId.fromString(userId),
|
||||||
gameKey: GameKey.create('iracing'),
|
gameKey: GameKey.create('iracing'),
|
||||||
ratings: new Map([
|
ratings: new Map([
|
||||||
['iRating', ExternalRating.create(GameKey.create('iracing'), 'iRating', 2200)],
|
['iRating', ExternalRating.create(GameKey.create('iracing'), 'iRating', 2200)],
|
||||||
@@ -118,7 +123,7 @@ describe('GetUserRatingsSummaryQuery', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const assettoProfile = ExternalGameRatingProfile.create({
|
const assettoProfile = ExternalGameRatingProfile.create({
|
||||||
userId: { toString: () => userId } as any,
|
userId: UserId.fromString(userId),
|
||||||
gameKey: GameKey.create('assetto'),
|
gameKey: GameKey.create('assetto'),
|
||||||
ratings: new Map([
|
ratings: new Map([
|
||||||
['rating', ExternalRating.create(GameKey.create('assetto'), 'rating', 85)],
|
['rating', ExternalRating.create(GameKey.create('assetto'), 'rating', 85)],
|
||||||
|
|||||||
@@ -134,14 +134,16 @@ class MockUserRatingRepository {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import { CreateRatingEventDto } from '../dtos/CreateRatingEventDto';
|
||||||
|
|
||||||
// Mock AppendRatingEventsUseCase
|
// Mock AppendRatingEventsUseCase
|
||||||
class MockAppendRatingEventsUseCase {
|
class MockAppendRatingEventsUseCase {
|
||||||
constructor(
|
constructor(
|
||||||
private ratingEventRepository: any,
|
private ratingEventRepository: MockRatingEventRepository,
|
||||||
private userRatingRepository: any
|
private userRatingRepository: MockUserRatingRepository
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(input: any): Promise<any> {
|
async execute(input: { userId: string; events: CreateRatingEventDto[] }): Promise<{ events: string[]; snapshotUpdated: boolean }> {
|
||||||
const events: RatingEvent[] = [];
|
const events: RatingEvent[] = [];
|
||||||
|
|
||||||
// Create events from input
|
// Create events from input
|
||||||
@@ -195,7 +197,6 @@ describe('Admin Vote Session Use Cases', () => {
|
|||||||
// Use dates relative to current time so close() works
|
// Use dates relative to current time so close() works
|
||||||
const now = new Date(Date.now() - 86400000); // Yesterday
|
const now = new Date(Date.now() - 86400000); // Yesterday
|
||||||
const tomorrow = new Date(Date.now() + 86400000); // Tomorrow
|
const tomorrow = new Date(Date.now() + 86400000); // Tomorrow
|
||||||
const dayAfter = new Date(Date.now() + 86400000 * 2); // Day after tomorrow
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockSessionRepo = new MockAdminVoteSessionRepository();
|
mockSessionRepo = new MockAdminVoteSessionRepository();
|
||||||
|
|||||||
@@ -3,9 +3,6 @@ import { describe, expect, it, vi, beforeEach } from 'vitest';
|
|||||||
import { AppendRatingEventsUseCase, AppendRatingEventsInput } from './AppendRatingEventsUseCase';
|
import { AppendRatingEventsUseCase, AppendRatingEventsInput } from './AppendRatingEventsUseCase';
|
||||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
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', () => {
|
describe('AppendRatingEventsUseCase', () => {
|
||||||
let mockEventRepo: Partial<RatingEventRepository>;
|
let mockEventRepo: Partial<RatingEventRepository>;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { RatingEventRepository } from '../../domain/repositories/RatingEventRepo
|
|||||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
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 { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
||||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
||||||
@@ -97,7 +97,7 @@ export class AppendRatingEventsUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private createEventFromDto(dto: CreateRatingEventDto) {
|
private createEventFromDto(dto: CreateRatingEventDto) {
|
||||||
const props: any = {
|
const props: RatingEventProps = {
|
||||||
id: RatingEventId.generate(),
|
id: RatingEventId.generate(),
|
||||||
userId: dto.userId,
|
userId: dto.userId,
|
||||||
dimension: RatingDimensionKey.create(dto.dimension),
|
dimension: RatingDimensionKey.create(dto.dimension),
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
|
||||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||||
import { AdminTrustRatingCalculator } from '../../domain/services/AdminTrustRatingCalculator';
|
|
||||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
||||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||||
import { CloseAdminVoteSessionInput, CloseAdminVoteSessionOutput } from '../dtos/AdminVoteSessionDto';
|
import { CloseAdminVoteSessionInput, CloseAdminVoteSessionOutput } from '../dtos/AdminVoteSessionDto';
|
||||||
|
import { AdminVoteSession, AdminVoteOutcome } from '../../domain/entities/AdminVoteSession';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Use Case: CloseAdminVoteSessionUseCase
|
* Use Case: CloseAdminVoteSessionUseCase
|
||||||
@@ -26,7 +26,6 @@ export class CloseAdminVoteSessionUseCase {
|
|||||||
private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
|
private readonly adminVoteSessionRepository: AdminVoteSessionRepository,
|
||||||
private readonly ratingEventRepository: RatingEventRepository,
|
private readonly ratingEventRepository: RatingEventRepository,
|
||||||
private readonly userRatingRepository: UserRatingRepository,
|
private readonly userRatingRepository: UserRatingRepository,
|
||||||
private readonly appendRatingEventsUseCase: any, // Will be typed properly in integration
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async execute(input: CloseAdminVoteSessionInput): Promise<CloseAdminVoteSessionOutput> {
|
async execute(input: CloseAdminVoteSessionInput): Promise<CloseAdminVoteSessionOutput> {
|
||||||
@@ -117,7 +116,7 @@ export class CloseAdminVoteSessionUseCase {
|
|||||||
* Events are created for the admin being voted on
|
* Events are created for the admin being voted on
|
||||||
* Per plans: no events are created for tie outcomes
|
* 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;
|
let eventsCreated = 0;
|
||||||
|
|
||||||
// Don't create events for tie outcomes
|
// 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 { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { User } from '../../domain/entities/User';
|
import { User } from '../../domain/entities/User';
|
||||||
|
import { UserRepository } from '../../domain/repositories/UserRepository';
|
||||||
|
|
||||||
export type GetCurrentSessionInput = {
|
export type GetCurrentSessionInput = {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
* Authenticates a user with email and password.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
|
import { UserRepository } from '../../domain/repositories/UserRepository';
|
||||||
|
|
||||||
export type LoginWithEmailInput = {
|
export type LoginWithEmailInput = {
|
||||||
email: string;
|
email: string;
|
||||||
|
|||||||
@@ -6,8 +6,6 @@ import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
|||||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
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
|
// In-memory implementations for integration testing
|
||||||
class InMemoryRaceResultsProvider implements RaceResultsProvider {
|
class InMemoryRaceResultsProvider implements RaceResultsProvider {
|
||||||
|
|||||||
@@ -6,8 +6,7 @@ import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
|||||||
import { UserRating } from '../../domain/value-objects/UserRating';
|
import { UserRating } from '../../domain/value-objects/UserRating';
|
||||||
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
import { RatingEvent } from '../../domain/entities/RatingEvent';
|
||||||
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
import { RatingEventId } from '../../domain/value-objects/RatingEventId';
|
||||||
import { RatingDimensionKey } from '../../domain/value-objects/RatingDimensionKey';
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { RatingDelta } from '../../domain/value-objects/RatingDelta';
|
|
||||||
|
|
||||||
// Mock implementations
|
// Mock implementations
|
||||||
class MockRaceResultsProvider implements RaceResultsProvider {
|
class MockRaceResultsProvider implements RaceResultsProvider {
|
||||||
@@ -17,11 +16,11 @@ class MockRaceResultsProvider implements RaceResultsProvider {
|
|||||||
this.results = results;
|
this.results = results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getRaceResults(raceId: string): Promise<RaceResultsData | null> {
|
async getRaceResults(_raceId: string): Promise<RaceResultsData | null> {
|
||||||
return this.results;
|
return this.results;
|
||||||
}
|
}
|
||||||
|
|
||||||
async hasRaceResults(raceId: string): Promise<boolean> {
|
async hasRaceResults(_raceId: string): Promise<boolean> {
|
||||||
return this.results !== null;
|
return this.results !== null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ import { RaceResultsProvider } from '../ports/RaceResultsProvider';
|
|||||||
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
import { RatingEventRepository } from '../../domain/repositories/RatingEventRepository';
|
||||||
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
import { UserRatingRepository } from '../../domain/repositories/UserRatingRepository';
|
||||||
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
|
||||||
import { DrivingRatingCalculator } from '../../domain/services/DrivingRatingCalculator';
|
|
||||||
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
|
|
||||||
import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
import { AppendRatingEventsUseCase } from './AppendRatingEventsUseCase';
|
||||||
import { RecordRaceRatingEventsInput, RecordRaceRatingEventsOutput } from '../dtos/RecordRaceRatingEventsDto';
|
import { RecordRaceRatingEventsInput, RecordRaceRatingEventsOutput } from '../dtos/RecordRaceRatingEventsDto';
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { Result } from '@core/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 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 { AuthenticatedUser } from '../ports/IdentityProviderPort';
|
||||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,30 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { UpsertExternalGameRatingUseCase } from './UpsertExternalGameRatingUseCase';
|
import { UpsertExternalGameRatingUseCase } from './UpsertExternalGameRatingUseCase';
|
||||||
import { UpsertExternalGameRatingInput } from '../dtos/UpsertExternalGameRatingDto';
|
import { UpsertExternalGameRatingInput } from '../dtos/UpsertExternalGameRatingDto';
|
||||||
|
import { ExternalGameRatingProfile } from '../../domain/entities/ExternalGameRatingProfile';
|
||||||
|
import { ExternalGameRatingRepository } from '../../domain/repositories/ExternalGameRatingRepository';
|
||||||
|
|
||||||
// Mock repository for integration test
|
// Mock repository for integration test
|
||||||
class MockExternalGameRatingRepository {
|
class MockExternalGameRatingRepository {
|
||||||
private profiles = new Map<string, any>();
|
private profiles = new Map<string, ExternalGameRatingProfile>();
|
||||||
|
|
||||||
private getKey(userId: string, gameKey: string): string {
|
private getKey(userId: string, gameKey: string): string {
|
||||||
return `${userId}|${gameKey}`;
|
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;
|
return this.profiles.get(this.getKey(userId, gameKey)) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByUserId(userId: string): Promise<any[]> {
|
async findByUserId(userId: string): Promise<ExternalGameRatingProfile[]> {
|
||||||
return Array.from(this.profiles.values()).filter((p: any) => p.userId.toString() === userId);
|
return Array.from(this.profiles.values()).filter((p: ExternalGameRatingProfile) => p.userId.toString() === userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async findByGameKey(gameKey: string): Promise<any[]> {
|
async findByGameKey(gameKey: string): Promise<ExternalGameRatingProfile[]> {
|
||||||
return Array.from(this.profiles.values()).filter((p: any) => p.gameKey.toString() === gameKey);
|
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());
|
const key = this.getKey(profile.userId.toString(), profile.gameKey.toString());
|
||||||
this.profiles.set(key, profile);
|
this.profiles.set(key, profile);
|
||||||
return profile;
|
return profile;
|
||||||
@@ -50,7 +53,7 @@ describe('UpsertExternalGameRatingUseCase - Integration', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
repository = new MockExternalGameRatingRepository();
|
repository = new MockExternalGameRatingRepository();
|
||||||
useCase = new UpsertExternalGameRatingUseCase(repository as any);
|
useCase = new UpsertExternalGameRatingUseCase(repository as unknown as ExternalGameRatingRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Full upsert flow', () => {
|
describe('Full upsert flow', () => {
|
||||||
|
|||||||
@@ -6,11 +6,19 @@ import { GameKey } from '../../domain/value-objects/GameKey';
|
|||||||
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
|
import { ExternalRating } from '../../domain/value-objects/ExternalRating';
|
||||||
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
|
import { ExternalRatingProvenance } from '../../domain/value-objects/ExternalRatingProvenance';
|
||||||
import { UpsertExternalGameRatingInput } from '../dtos/UpsertExternalGameRatingDto';
|
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', () => {
|
describe('UpsertExternalGameRatingUseCase', () => {
|
||||||
let useCase: UpsertExternalGameRatingUseCase;
|
let useCase: UpsertExternalGameRatingUseCase;
|
||||||
let mockRepository: ExternalGameRatingRepository;
|
let mockRepository: {
|
||||||
|
findByUserIdAndGameKey: Mock;
|
||||||
|
findByUserId: Mock;
|
||||||
|
findByGameKey: Mock;
|
||||||
|
save: Mock;
|
||||||
|
saveMany: Mock;
|
||||||
|
delete: Mock;
|
||||||
|
exists: Mock;
|
||||||
|
};
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
mockRepository = {
|
mockRepository = {
|
||||||
@@ -21,9 +29,17 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
|||||||
saveMany: vi.fn(),
|
saveMany: vi.fn(),
|
||||||
delete: vi.fn(),
|
delete: vi.fn(),
|
||||||
exists: 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', () => {
|
describe('execute', () => {
|
||||||
@@ -42,8 +58,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||||
|
|
||||||
const result = await useCase.execute(input);
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
@@ -61,8 +77,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
|||||||
|
|
||||||
it('should update existing profile', async () => {
|
it('should update existing profile', async () => {
|
||||||
const existingProfile = createTestProfile('user-123', 'iracing');
|
const existingProfile = createTestProfile('user-123', 'iracing');
|
||||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(existingProfile);
|
mockRepository.findByUserIdAndGameKey.mockResolvedValue(existingProfile);
|
||||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||||
|
|
||||||
const input: UpsertExternalGameRatingInput = {
|
const input: UpsertExternalGameRatingInput = {
|
||||||
userId: 'user-123',
|
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);
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
@@ -232,8 +248,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||||
|
|
||||||
const result = await useCase.execute(input);
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
@@ -254,8 +270,8 @@ describe('UpsertExternalGameRatingUseCase', () => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
(mockRepository.findByUserIdAndGameKey as any).mockResolvedValue(null);
|
mockRepository.findByUserIdAndGameKey.mockResolvedValue(null);
|
||||||
(mockRepository.save as any).mockImplementation(async (profile: any) => profile);
|
mockRepository.save.mockImplementation(async (profile: ExternalGameRatingProfile) => profile);
|
||||||
|
|
||||||
const result = await useCase.execute(input);
|
const result = await useCase.execute(input);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { DomainError } from '@core/shared/errors/DomainError';
|
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||||
import type { CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
|
||||||
|
|
||||||
export abstract class IdentityDomainError extends Error implements DomainError<CommonDomainErrorKind> {
|
export abstract class IdentityDomainError extends Error implements DomainError<CommonDomainErrorKind> {
|
||||||
readonly type = 'domain' as const;
|
readonly type = 'domain' as const;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
import { ExternalGameRatingRepository } from './ExternalGameRatingRepository';
|
import { ExternalGameRatingRepository } from './ExternalGameRatingRepository';
|
||||||
import { ExternalGameRatingProfile } from '../entities/ExternalGameRatingProfile';
|
import { ExternalGameRatingProfile } from '../entities/ExternalGameRatingProfile';
|
||||||
import { UserId } from '../value-objects/UserId';
|
import { UserId } from '../value-objects/UserId';
|
||||||
@@ -182,11 +183,6 @@ describe('ExternalGameRatingRepository', () => {
|
|||||||
await repository.save(profile);
|
await repository.save(profile);
|
||||||
|
|
||||||
// Update the 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.updateLastSyncedAt(new Date('2024-01-02'));
|
||||||
profile.markVerified();
|
profile.markVerified();
|
||||||
|
|
||||||
@@ -290,7 +286,7 @@ describe('ExternalGameRatingRepository', () => {
|
|||||||
lastSyncedAt: new Date('2024-01-01'),
|
lastSyncedAt: new Date('2024-01-01'),
|
||||||
verified: false,
|
verified: false,
|
||||||
});
|
});
|
||||||
profile2.updateRatings(profile2.ratings, profile2Provenance);
|
profile2.updateRatings(new Map(profile2.ratings), profile2Provenance);
|
||||||
|
|
||||||
await repository.saveMany([profile1, profile2]);
|
await repository.saveMany([profile1, profile2]);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { RatingEvent } from '../entities/RatingEvent';
|
|||||||
import { RatingEventId } from '../value-objects/RatingEventId';
|
import { RatingEventId } from '../value-objects/RatingEventId';
|
||||||
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
|
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
|
||||||
import { RatingDelta } from '../value-objects/RatingDelta';
|
import { RatingDelta } from '../value-objects/RatingDelta';
|
||||||
import { AdminVoteOutcome } from '../entities/AdminVoteSession';
|
|
||||||
|
|
||||||
describe('AdminTrustRatingCalculator', () => {
|
describe('AdminTrustRatingCalculator', () => {
|
||||||
describe('calculate', () => {
|
describe('calculate', () => {
|
||||||
@@ -316,13 +315,12 @@ describe('AdminTrustRatingCalculator', () => {
|
|||||||
|
|
||||||
it('should default to zero for unknown action type', () => {
|
it('should default to zero for unknown action type', () => {
|
||||||
const input: SystemSignalInput = {
|
const input: SystemSignalInput = {
|
||||||
actionType: 'sla_response' as any,
|
actionType: 'unknown_type' as unknown as SystemSignalInput['actionType'],
|
||||||
details: {},
|
details: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Override for test
|
|
||||||
const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input);
|
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 { 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
|
* Input DTO for driving rating calculation from race facts
|
||||||
@@ -71,7 +69,7 @@ export class DrivingRatingCalculator {
|
|||||||
const fieldStrength = facts.results.length > 0
|
const fieldStrength = facts.results.length > 0
|
||||||
? (facts.results
|
? (facts.results
|
||||||
.filter(r => r.status === 'finished')
|
.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))
|
Math.max(1, facts.results.filter(r => r.status === 'finished').length))
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
@@ -297,7 +295,7 @@ export class DrivingRatingCalculator {
|
|||||||
* Estimate driver rating for SoF calculation
|
* Estimate driver rating for SoF calculation
|
||||||
* This is a placeholder - in real implementation, would query user rating snapshot
|
* 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
|
// Default rating for new drivers
|
||||||
return 50;
|
return 50;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { EligibilityEvaluator, RatingData } from './EligibilityEvaluator';
|
import { EligibilityEvaluator, RatingData } from './EligibilityEvaluator';
|
||||||
import { EligibilityFilterDto } from '../../application/dtos/EligibilityFilterDto';
|
import { EligibilityFilterDto } from '../types/Eligibility';
|
||||||
|
|
||||||
describe('EligibilityEvaluator', () => {
|
describe('EligibilityEvaluator', () => {
|
||||||
let evaluator: EligibilityEvaluator;
|
let evaluator: EligibilityEvaluator;
|
||||||
|
|||||||
@@ -6,8 +6,13 @@
|
|||||||
* Provides explainable results with detailed reasons.
|
* Provides explainable results with detailed reasons.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { EvaluationResultDto, EvaluationReason } from '../../application/dtos/EvaluationResultDto';
|
import {
|
||||||
import { EligibilityFilterDto, ParsedEligibilityFilter, EligibilityCondition } from '../../application/dtos/EligibilityFilterDto';
|
EvaluationResultDto,
|
||||||
|
EvaluationReason,
|
||||||
|
EligibilityFilterDto,
|
||||||
|
ParsedEligibilityFilter,
|
||||||
|
EligibilityCondition
|
||||||
|
} from '../types/Eligibility';
|
||||||
|
|
||||||
export interface RatingData {
|
export interface RatingData {
|
||||||
platform: {
|
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 { RatingUpdateService } from './RatingUpdateService';
|
||||||
import type { UserRatingRepository } from '../repositories/UserRatingRepository';
|
import type { UserRatingRepository } from '../repositories/UserRatingRepository';
|
||||||
import type { RatingEventRepository } from '../repositories/RatingEventRepository';
|
import type { RatingEventRepository } from '../repositories/RatingEventRepository';
|
||||||
@@ -10,8 +10,8 @@ import { RatingDelta } from '../value-objects/RatingDelta';
|
|||||||
|
|
||||||
describe('RatingUpdateService - Slice 7 Evolution', () => {
|
describe('RatingUpdateService - Slice 7 Evolution', () => {
|
||||||
let service: RatingUpdateService;
|
let service: RatingUpdateService;
|
||||||
let userRatingRepository: any;
|
let userRatingRepository: { findByUserId: Mock; save: Mock };
|
||||||
let ratingEventRepository: any;
|
let ratingEventRepository: { save: Mock; getAllByUserId: Mock };
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
userRatingRepository = {
|
userRatingRepository = {
|
||||||
@@ -24,7 +24,10 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
|
|||||||
getAllByUserId: vi.fn(),
|
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', () => {
|
describe('recordRaceRatingEvents - Ledger-based approach', () => {
|
||||||
@@ -150,7 +153,7 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
|
|||||||
|
|
||||||
// Verify DNF penalty event was created
|
// Verify DNF penalty event was created
|
||||||
const savedEvents = ratingEventRepository.save.mock.calls.map(call => call[0]);
|
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'
|
event.reason.code === 'DRIVING_DNF_PENALTY'
|
||||||
);
|
);
|
||||||
expect(hasDnfPenalty).toBe(true);
|
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 { IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
describe('AdminTrustReasonCode', () => {
|
describe('AdminTrustReasonCode', () => {
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
@@ -71,7 +72,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
||||||
];
|
];
|
||||||
voteCodes.forEach(codeStr => {
|
voteCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isVoteOutcome()).toBe(true);
|
expect(code.isVoteOutcome()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -82,7 +83,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_REVERSAL_PENALTY',
|
'ADMIN_ACTION_REVERSAL_PENALTY',
|
||||||
];
|
];
|
||||||
nonVoteCodes.forEach(codeStr => {
|
nonVoteCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isVoteOutcome()).toBe(false);
|
expect(code.isVoteOutcome()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -97,7 +98,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||||
];
|
];
|
||||||
systemCodes.forEach(codeStr => {
|
systemCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isSystemSignal()).toBe(true);
|
expect(code.isSystemSignal()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -108,7 +109,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
'ADMIN_VOTE_OUTCOME_NEGATIVE',
|
||||||
];
|
];
|
||||||
nonSystemCodes.forEach(codeStr => {
|
nonSystemCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isSystemSignal()).toBe(false);
|
expect(code.isSystemSignal()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -122,7 +123,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
||||||
];
|
];
|
||||||
positiveCodes.forEach(codeStr => {
|
positiveCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isPositive()).toBe(true);
|
expect(code.isPositive()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -134,7 +135,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||||
];
|
];
|
||||||
nonPositiveCodes.forEach(codeStr => {
|
nonPositiveCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isPositive()).toBe(false);
|
expect(code.isPositive()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -148,7 +149,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
|
||||||
];
|
];
|
||||||
negativeCodes.forEach(codeStr => {
|
negativeCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isNegative()).toBe(true);
|
expect(code.isNegative()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -160,7 +161,7 @@ describe('AdminTrustReasonCode', () => {
|
|||||||
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
'ADMIN_ACTION_RULE_CLARITY_BONUS',
|
||||||
];
|
];
|
||||||
nonNegativeCodes.forEach(codeStr => {
|
nonNegativeCodes.forEach(codeStr => {
|
||||||
const code = AdminTrustReasonCode.fromValue(codeStr as any);
|
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
|
||||||
expect(code.isNegative()).toBe(false);
|
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 { IdentityDomainValidationError } from '../errors/IdentityDomainError';
|
||||||
|
import { describe, it, expect } from 'vitest';
|
||||||
|
|
||||||
describe('DrivingReasonCode', () => {
|
describe('DrivingReasonCode', () => {
|
||||||
describe('create', () => {
|
describe('create', () => {
|
||||||
@@ -77,7 +78,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_PACE_RELATIVE_GAIN',
|
'DRIVING_PACE_RELATIVE_GAIN',
|
||||||
];
|
];
|
||||||
performanceCodes.forEach(codeStr => {
|
performanceCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isPerformance()).toBe(true);
|
expect(code.isPerformance()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -89,7 +90,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||||
];
|
];
|
||||||
nonPerformanceCodes.forEach(codeStr => {
|
nonPerformanceCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isPerformance()).toBe(false);
|
expect(code.isPerformance()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -103,7 +104,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_PENALTY_INVOLVEMENT_PENALTY',
|
'DRIVING_PENALTY_INVOLVEMENT_PENALTY',
|
||||||
];
|
];
|
||||||
cleanDrivingCodes.forEach(codeStr => {
|
cleanDrivingCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isCleanDriving()).toBe(true);
|
expect(code.isCleanDriving()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -115,7 +116,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||||
];
|
];
|
||||||
nonCleanDrivingCodes.forEach(codeStr => {
|
nonCleanDrivingCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isCleanDriving()).toBe(false);
|
expect(code.isCleanDriving()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -131,7 +132,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||||
];
|
];
|
||||||
reliabilityCodes.forEach(codeStr => {
|
reliabilityCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isReliability()).toBe(true);
|
expect(code.isReliability()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -142,7 +143,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_INCIDENTS_PENALTY',
|
'DRIVING_INCIDENTS_PENALTY',
|
||||||
];
|
];
|
||||||
nonReliabilityCodes.forEach(codeStr => {
|
nonReliabilityCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isReliability()).toBe(false);
|
expect(code.isReliability()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -160,7 +161,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_AFK_PENALTY',
|
'DRIVING_AFK_PENALTY',
|
||||||
];
|
];
|
||||||
penaltyCodes.forEach(codeStr => {
|
penaltyCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isPenalty()).toBe(true);
|
expect(code.isPenalty()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -172,7 +173,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
'DRIVING_SEASON_ATTENDANCE_BONUS',
|
||||||
];
|
];
|
||||||
nonPenaltyCodes.forEach(codeStr => {
|
nonPenaltyCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isPenalty()).toBe(false);
|
expect(code.isPenalty()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -187,7 +188,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_PACE_RELATIVE_GAIN',
|
'DRIVING_PACE_RELATIVE_GAIN',
|
||||||
];
|
];
|
||||||
bonusCodes.forEach(codeStr => {
|
bonusCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isBonus()).toBe(true);
|
expect(code.isBonus()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -198,7 +199,7 @@ describe('DrivingReasonCode', () => {
|
|||||||
'DRIVING_DNS_PENALTY',
|
'DRIVING_DNS_PENALTY',
|
||||||
];
|
];
|
||||||
nonBonusCodes.forEach(codeStr => {
|
nonBonusCodes.forEach(codeStr => {
|
||||||
const code = DrivingReasonCode.fromValue(codeStr as any);
|
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
|
||||||
expect(code.isBonus()).toBe(false);
|
expect(code.isBonus()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,8 +5,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||||
|
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||||
|
|
||||||
export interface DeleteMediaInput {
|
export interface DeleteMediaInput {
|
||||||
mediaId: string;
|
mediaId: string;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import { AvatarRepository } from '../../domain/repositories/AvatarRepository';
|
||||||
|
|
||||||
export interface GetAvatarInput {
|
export interface GetAvatarInput {
|
||||||
driverId: string;
|
driverId: string;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||||
|
|
||||||
export interface GetMediaInput {
|
export interface GetMediaInput {
|
||||||
mediaId: string;
|
mediaId: string;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* Handles the business logic for requesting avatar generation from a face photo.
|
* 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 { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
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 { RacingSuitColor } from '../../domain/types/AvatarGenerationRequest';
|
||||||
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
|
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
|
||||||
import type { FaceValidationPort } from '../ports/FaceValidationPort';
|
import type { FaceValidationPort } from '../ports/FaceValidationPort';
|
||||||
|
import { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
|
||||||
|
|
||||||
export interface RequestAvatarGenerationInput {
|
export interface RequestAvatarGenerationInput {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -5,7 +5,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
|
||||||
|
|
||||||
export interface SelectAvatarInput {
|
export interface SelectAvatarInput {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
|
|||||||
@@ -4,12 +4,13 @@
|
|||||||
* Handles the business logic for updating a driver's avatar.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { Avatar } from '../../domain/entities/Avatar';
|
import { Avatar } from '../../domain/entities/Avatar';
|
||||||
|
import { AvatarRepository } from '../../domain/repositories/AvatarRepository';
|
||||||
|
|
||||||
export interface UpdateAvatarInput {
|
export interface UpdateAvatarInput {
|
||||||
driverId: string;
|
driverId: string;
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { Media } from '../../domain/entities/Media';
|
import { Media } from '../../domain/entities/Media';
|
||||||
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
import type { MediaStoragePort } from '../ports/MediaStoragePort';
|
||||||
|
import { MediaRepository } from '../../domain/repositories/MediaRepository';
|
||||||
|
|
||||||
// Define Multer file type locally since @types/multer is not available
|
// Define Multer file type locally since @types/multer is not available
|
||||||
export interface MulterFile {
|
export interface MulterFile {
|
||||||
|
|||||||
@@ -4,12 +4,15 @@
|
|||||||
* Retrieves unread notifications for a recipient.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { Notification } from '../../domain/entities/Notification';
|
import type { Notification } from '../../domain/entities/Notification';
|
||||||
|
|
||||||
|
export interface GetUnreadNotificationsInput {
|
||||||
|
recipientId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface GetUnreadNotificationsResult {
|
export interface GetUnreadNotificationsResult {
|
||||||
notifications: Notification[];
|
notifications: Notification[];
|
||||||
|
|||||||
@@ -4,11 +4,15 @@
|
|||||||
* Marks a notification as read.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
|
||||||
|
export interface MarkNotificationReadCommand {
|
||||||
|
notificationId: string;
|
||||||
|
recipientId: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface MarkNotificationReadResult {
|
export interface MarkNotificationReadResult {
|
||||||
notificationId: string;
|
notificationId: string;
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
* Manages user notification preferences.
|
* Manages user notification preferences.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { NotificationPreferenceRepository } from '@/notifications/domain/repositories/NotificationPreferenceRepository';
|
import { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
|
||||||
import { NotificationChannel, NotificationType } from '@/notifications/domain/types/NotificationTypes';
|
import { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
|
||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
* based on their preferences.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 type { NotificationData } from '../../domain/entities/Notification';
|
||||||
import { Notification } from '../../domain/entities/Notification';
|
import { Notification } from '../../domain/entities/Notification';
|
||||||
import type { NotificationDeliveryResult, NotificationGatewayRegistry } from '../ports/NotificationGateway';
|
import type { NotificationDeliveryResult, NotificationGatewayRegistry } from '../ports/NotificationGateway';
|
||||||
|
import { NotificationRepository } from '../../domain/repositories/NotificationRepository';
|
||||||
|
import { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
|
||||||
|
|
||||||
export interface SendNotificationCommand {
|
export interface SendNotificationCommand {
|
||||||
recipientId: string;
|
recipientId: string;
|
||||||
@@ -93,7 +95,7 @@ export class SendNotificationUseCase {
|
|||||||
|
|
||||||
// Check quiet hours (skip external channels during quiet hours)
|
// Check quiet hours (skip external channels during quiet hours)
|
||||||
const effectiveChannels = preferences.isInQuietHours()
|
const effectiveChannels = preferences.isInQuietHours()
|
||||||
? channels.filter(ch => ch === 'in_app')
|
? channels.filter((ch: NotificationChannel) => ch === 'in_app')
|
||||||
: channels;
|
: channels;
|
||||||
|
|
||||||
// Ensure at least in_app is used
|
// Ensure at least in_app is used
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { DomainError } from '@core/shared/errors/DomainError';
|
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
||||||
import type { CommonDomainErrorKind } from '@core/shared/errors/DomainError';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Domain Error: NotificationDomainError
|
* 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 { ApplicationError, CommonApplicationErrorKind } from '@core/shared/errors/ApplicationError';
|
||||||
import type { CommonApplicationErrorKind } from '@core/shared/errors/ApplicationError';
|
|
||||||
|
|
||||||
export abstract class RacingApplicationError
|
export abstract class RacingApplicationError
|
||||||
extends Error
|
extends Error
|
||||||
|
|||||||
@@ -2,15 +2,16 @@
|
|||||||
* Tests for GetTeamRatingLedgerQuery
|
* 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 { GetTeamRatingLedgerQuery, GetTeamRatingLedgerQueryHandler } from './GetTeamRatingLedgerQuery';
|
||||||
import { TeamRatingEvent } from '../../domain/entities/TeamRatingEvent';
|
import { TeamRatingEvent } from '../../domain/entities/TeamRatingEvent';
|
||||||
import { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId';
|
import { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId';
|
||||||
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
|
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
|
||||||
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
|
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
|
||||||
|
import type { TeamRatingEventRepository } from '../../domain/repositories/TeamRatingEventRepository';
|
||||||
|
|
||||||
describe('GetTeamRatingLedgerQuery', () => {
|
describe('GetTeamRatingLedgerQuery', () => {
|
||||||
let mockRatingEventRepo: any;
|
let mockRatingEventRepo: { findEventsPaginated: Mock };
|
||||||
let handler: GetTeamRatingLedgerQueryHandler;
|
let handler: GetTeamRatingLedgerQueryHandler;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -18,7 +19,7 @@ describe('GetTeamRatingLedgerQuery', () => {
|
|||||||
findEventsPaginated: vi.fn(),
|
findEventsPaginated: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
handler = new GetTeamRatingLedgerQueryHandler(mockRatingEventRepo);
|
handler = new GetTeamRatingLedgerQueryHandler(mockRatingEventRepo as unknown as TeamRatingEventRepository);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('execute', () => {
|
describe('execute', () => {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
* Tests for GetTeamRatingsSummaryQuery
|
* 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 { GetTeamRatingsSummaryQuery, GetTeamRatingsSummaryQueryHandler } from './GetTeamRatingsSummaryQuery';
|
||||||
import { TeamRatingSnapshot } from '../../domain/services/TeamRatingSnapshotCalculator';
|
import { TeamRatingSnapshot } from '../../domain/services/TeamRatingSnapshotCalculator';
|
||||||
import { TeamRatingValue } from '../../domain/value-objects/TeamRatingValue';
|
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 { TeamRatingEventId } from '../../domain/value-objects/TeamRatingEventId';
|
||||||
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
|
import { TeamRatingDimensionKey } from '../../domain/value-objects/TeamRatingDimensionKey';
|
||||||
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
|
import { TeamRatingDelta } from '../../domain/value-objects/TeamRatingDelta';
|
||||||
|
import type { TeamRatingRepository } from '../../domain/repositories/TeamRatingRepository';
|
||||||
|
import type { TeamRatingEventRepository } from '../../domain/repositories/TeamRatingEventRepository';
|
||||||
|
|
||||||
describe('GetTeamRatingsSummaryQuery', () => {
|
describe('GetTeamRatingsSummaryQuery', () => {
|
||||||
let mockTeamRatingRepo: any;
|
let mockTeamRatingRepo: { findByTeamId: Mock };
|
||||||
let mockRatingEventRepo: any;
|
let mockRatingEventRepo: { getAllByTeamId: Mock };
|
||||||
let handler: GetTeamRatingsSummaryQueryHandler;
|
let handler: GetTeamRatingsSummaryQueryHandler;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -25,8 +27,8 @@ describe('GetTeamRatingsSummaryQuery', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handler = new GetTeamRatingsSummaryQueryHandler(
|
handler = new GetTeamRatingsSummaryQueryHandler(
|
||||||
mockTeamRatingRepo,
|
mockTeamRatingRepo as unknown as TeamRatingRepository,
|
||||||
mockRatingEventRepo
|
mockRatingEventRepo as unknown as TeamRatingEventRepository
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
|
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 { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { SeasonSponsorship } from '../../domain/entities/season/SeasonSponsorship';
|
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 {
|
export interface AcceptSponsorshipRequestInput {
|
||||||
requestId: string;
|
requestId: string;
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import { TeamRatingEvent } from '@core/racing/domain/entities/TeamRatingEvent';
|
|||||||
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
|
import { TeamRatingEventId } from '@core/racing/domain/value-objects/TeamRatingEventId';
|
||||||
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
|
import { TeamRatingDimensionKey } from '@core/racing/domain/value-objects/TeamRatingDimensionKey';
|
||||||
import { TeamRatingDelta } from '@core/racing/domain/value-objects/TeamRatingDelta';
|
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
|
// Mock repositories
|
||||||
class MockTeamRatingEventRepository implements TeamRatingEventRepository {
|
class MockTeamRatingEventRepository implements TeamRatingEventRepository {
|
||||||
@@ -27,7 +29,7 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
|
|||||||
return this.events.filter(e => e.teamId === teamId);
|
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);
|
const events = await this.getAllByTeamId(teamId);
|
||||||
return {
|
return {
|
||||||
items: events,
|
items: events,
|
||||||
@@ -44,13 +46,13 @@ class MockTeamRatingEventRepository implements TeamRatingEventRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class MockTeamRatingRepository implements TeamRatingRepository {
|
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;
|
return this.snapshots.get(teamId) || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async save(snapshot: any): Promise<any> {
|
async save(snapshot: TeamRating): Promise<TeamRating> {
|
||||||
this.snapshots.set(snapshot.teamId, snapshot);
|
this.snapshots.set(snapshot.teamId, snapshot);
|
||||||
return snapshot;
|
return snapshot;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,11 +5,14 @@
|
|||||||
* (driver, team, race, or season/league).
|
* (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 { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
|
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
|
||||||
import { Money, isCurrency } from '../../domain/value-objects/Money';
|
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 {
|
export interface ApplyForSponsorshipInput {
|
||||||
sponsorId: string;
|
sponsorId: string;
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
||||||
import { ApplyPenaltyUseCase } from './ApplyPenaltyUseCase';
|
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', () => {
|
describe('ApplyPenaltyUseCase', () => {
|
||||||
let mockPenaltyRepo: {
|
let mockPenaltyRepo: {
|
||||||
@@ -43,11 +48,13 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when race does not exist', async () => {
|
it('should return error when race does not exist', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue(null);
|
mockRaceRepo.findById.mockResolvedValue(null);
|
||||||
|
|
||||||
@@ -65,11 +72,13 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when steward does not have authority', async () => {
|
it('should return error when steward does not have authority', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
|
|
||||||
@@ -95,11 +104,13 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when protest does not exist', async () => {
|
it('should return error when protest does not exist', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
|
|
||||||
@@ -127,11 +138,13 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when protest is not upheld', async () => {
|
it('should return error when protest is not upheld', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
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 () => {
|
it('should return error when protest is not for this race', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
|
|
||||||
@@ -191,11 +206,13 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create penalty and return result on success', async () => {
|
it('should create penalty and return result on success', async () => {
|
||||||
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
|
const useCase = new ApplyPenaltyUseCase(
|
||||||
mockProtestRepo as any,
|
mockPenaltyRepo as unknown as PenaltyRepository,
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger as any);
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
|
mockLogger as unknown as Logger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
|
|
||||||
@@ -223,7 +240,7 @@ describe('ApplyPenaltyUseCase', () => {
|
|||||||
expect(presented.penaltyId).toBeDefined();
|
expect(presented.penaltyId).toBeDefined();
|
||||||
|
|
||||||
expect(mockPenaltyRepo.create).toHaveBeenCalledTimes(1);
|
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 };
|
type ToStringable = { toString(): string };
|
||||||
const asString = (value: unknown): string => {
|
const asString = (value: unknown): string => {
|
||||||
|
|||||||
@@ -5,11 +5,15 @@
|
|||||||
* The penalty can be standalone or linked to an upheld protest.
|
* 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 { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { Penalty } from '../../domain/entities/penalty/Penalty';
|
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 {
|
export interface ApplyPenaltyInput {
|
||||||
raceId: string;
|
raceId: string;
|
||||||
@@ -62,7 +66,7 @@ export class ApplyPenaltyUseCase {
|
|||||||
// Validate steward has authority (owner or admin of the league)
|
// Validate steward has authority (owner or admin of the league)
|
||||||
const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
|
const memberships = await this.leagueMembershipRepository.getLeagueMembers(race.leagueId);
|
||||||
const stewardMembership = memberships.find(
|
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')) {
|
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}.`,
|
`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);
|
return Result.ok(result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,7 @@ import type { LeagueRepository } from '../../domain/repositories/LeagueRepositor
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { randomUUID } from 'crypto';
|
import { randomUUID } from 'crypto';
|
||||||
import { JoinedAt } from '../../domain/value-objects/JoinedAt';
|
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
|
||||||
import { LeagueId } from '../../domain/entities/LeagueId';
|
|
||||||
import { DriverId } from '../../domain/entities/DriverId';
|
|
||||||
import { MembershipRole } from '../../domain/entities/MembershipRole';
|
|
||||||
import { MembershipStatus } from '../../domain/entities/MembershipStatus';
|
|
||||||
|
|
||||||
export interface ApproveLeagueJoinRequestInput {
|
export interface ApproveLeagueJoinRequestInput {
|
||||||
leagueId: string;
|
leagueId: string;
|
||||||
@@ -55,14 +51,16 @@ export class ApproveLeagueJoinRequestUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await this.leagueMembershipRepository.removeJoinRequest(input.joinRequestId);
|
await this.leagueMembershipRepository.removeJoinRequest(input.joinRequestId);
|
||||||
await this.leagueMembershipRepository.saveMembership({
|
await this.leagueMembershipRepository.saveMembership(
|
||||||
id: randomUUID(),
|
LeagueMembership.create({
|
||||||
leagueId: LeagueId.create(input.leagueId),
|
id: randomUUID(),
|
||||||
driverId: DriverId.create(request.driverId.toString()),
|
leagueId: input.leagueId,
|
||||||
role: MembershipRole.create('member'),
|
driverId: request.driverId.toString(),
|
||||||
status: MembershipStatus.create('active'),
|
role: 'member',
|
||||||
joinedAt: JoinedAt.create(new Date()),
|
status: 'active',
|
||||||
});
|
joinedAt: new Date(),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const result: ApproveLeagueJoinRequestResult = { success: true, message: 'Join request approved.' };
|
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 { Race } from '../../domain/entities/Race';
|
||||||
import { SessionType } from '../../domain/value-objects/SessionType';
|
import { SessionType } from '../../domain/value-objects/SessionType';
|
||||||
import { CancelRaceUseCase } from './CancelRaceUseCase';
|
import { CancelRaceUseCase } from './CancelRaceUseCase';
|
||||||
|
import { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
|
|
||||||
describe('CancelRaceUseCase', () => {
|
describe('CancelRaceUseCase', () => {
|
||||||
let useCase: CancelRaceUseCase;
|
let useCase: CancelRaceUseCase;
|
||||||
@@ -27,8 +29,8 @@ describe('CancelRaceUseCase', () => {
|
|||||||
info: vi.fn(),
|
info: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new CancelRaceUseCase(raceRepository as any,
|
useCase = new CancelRaceUseCase(raceRepository as unknown as RaceRepository,
|
||||||
logger as any);
|
logger as unknown as Logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should cancel race successfully', async () => {
|
it('should cancel race successfully', async () => {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { Result } from '@core/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 type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { Race } from '../../domain/entities/Race';
|
import type { Race } from '../../domain/entities/Race';
|
||||||
|
import { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
|
||||||
export type CancelRaceInput = {
|
export type CancelRaceInput = {
|
||||||
raceId: string;
|
raceId: string;
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import { RaceEvent } from '../../domain/entities/RaceEvent';
|
|||||||
import { Session } from '../../domain/entities/Session';
|
import { Session } from '../../domain/entities/Session';
|
||||||
import { SessionType } from '../../domain/value-objects/SessionType';
|
import { SessionType } from '../../domain/value-objects/SessionType';
|
||||||
import { CloseRaceEventStewardingUseCase } from './CloseRaceEventStewardingUseCase';
|
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', () => {
|
describe('CloseRaceEventStewardingUseCase', () => {
|
||||||
let useCase: CloseRaceEventStewardingUseCase;
|
let useCase: CloseRaceEventStewardingUseCase;
|
||||||
@@ -42,11 +47,11 @@ describe('CloseRaceEventStewardingUseCase', () => {
|
|||||||
logger = {
|
logger = {
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new CloseRaceEventStewardingUseCase(logger as any,
|
useCase = new CloseRaceEventStewardingUseCase(logger as unknown as Logger,
|
||||||
raceEventRepository as any,
|
raceEventRepository as unknown as RaceEventRepository,
|
||||||
raceRegistrationRepository as any,
|
raceRegistrationRepository as unknown as RaceRegistrationRepository,
|
||||||
penaltyRepository as any,
|
penaltyRepository as unknown as PenaltyRepository,
|
||||||
domainEventPublisher as any);
|
domainEventPublisher as unknown as DomainEventPublisher);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should close stewarding for expired events successfully', async () => {
|
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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { RaceEvent } from '../../domain/entities/RaceEvent';
|
import type { RaceEvent } from '../../domain/entities/RaceEvent';
|
||||||
import { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
|
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 = {
|
export type CloseRaceEventStewardingInput = {
|
||||||
raceId: string;
|
raceId: string;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { DriverRepository } from '../../domain/repositories/DriverRepositor
|
|||||||
import { Driver } from '../../domain/entities/Driver';
|
import { Driver } from '../../domain/entities/Driver';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 {
|
export interface CompleteDriverOnboardingInput {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
||||||
import { CompleteRaceUseCase, type CompleteRaceInput, type CompleteRaceResult } from './CompleteRaceUseCase';
|
import { CompleteRaceUseCase, type CompleteRaceInput } from './CompleteRaceUseCase';
|
||||||
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
|
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
|
||||||
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
|
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
|
||||||
@@ -39,10 +39,10 @@ describe('CompleteRaceUseCase', () => {
|
|||||||
save: vi.fn(),
|
save: vi.fn(),
|
||||||
};
|
};
|
||||||
getDriverRating = vi.fn();
|
getDriverRating = vi.fn();
|
||||||
useCase = new CompleteRaceUseCase(raceRepository as any,
|
useCase = new CompleteRaceUseCase(raceRepository as unknown as RaceRepository,
|
||||||
raceRegistrationRepository as any,
|
raceRegistrationRepository as unknown as RaceRegistrationRepository,
|
||||||
resultRepository as any,
|
resultRepository as unknown as ResultRepository,
|
||||||
standingRepository as any,
|
standingRepository as unknown as StandingRepository,
|
||||||
getDriverRating);
|
getDriverRating);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,16 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
|
|||||||
|
|
||||||
import { Race } from '../../domain/entities/Race';
|
import { Race } from '../../domain/entities/Race';
|
||||||
import type { Season } from '../../domain/entities/season/Season';
|
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 = {
|
export type CreateLeagueSeasonScheduleRaceResult = {
|
||||||
raceId: string;
|
raceId: string;
|
||||||
@@ -88,6 +97,9 @@ export class CreateLeagueSeasonScheduleRaceUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private isWithinSeasonWindow(season: Season, scheduledAt: Date): boolean {
|
private isWithinSeasonWindow(season: Season, scheduledAt: Date): boolean {
|
||||||
|
if (!season.startDate || !season.endDate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return scheduledAt >= season.startDate && scheduledAt <= season.endDate;
|
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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
@@ -13,6 +13,9 @@ import {
|
|||||||
MIN_RANKED_LEAGUE_DRIVERS,
|
MIN_RANKED_LEAGUE_DRIVERS,
|
||||||
} from '../../domain/value-objects/LeagueVisibility';
|
} from '../../domain/value-objects/LeagueVisibility';
|
||||||
import { PointsTable } from '../../domain/value-objects/PointsTable';
|
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 = {
|
export type CreateLeagueWithSeasonAndScoringCommand = {
|
||||||
name: string;
|
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 { Season } from '@core/racing/domain/entities/season/Season';
|
||||||
import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository';
|
import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository';
|
||||||
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
|
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
|
||||||
import {
|
import {
|
||||||
CreateSeasonForLeagueUseCase,
|
CreateSeasonForLeagueUseCase,
|
||||||
type CreateSeasonForLeagueInput,
|
type CreateSeasonForLeagueInput,
|
||||||
type CreateSeasonForLeagueResult,
|
|
||||||
type LeagueConfigFormModel,
|
type LeagueConfigFormModel,
|
||||||
} from '@core/racing/application/use-cases/CreateSeasonForLeagueUseCase';
|
} 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 {
|
function createLeagueConfigFormModel(overrides?: Partial<LeagueConfigFormModel>): LeagueConfigFormModel {
|
||||||
return {
|
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', () => {
|
describe('CreateSeasonForLeagueUseCase', () => {
|
||||||
const mockLeagueFindById = vi.fn();
|
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,
|
findById: mockLeagueFindById,
|
||||||
findAll: vi.fn(),
|
findAll: vi.fn(),
|
||||||
findByOwnerId: vi.fn(),
|
findByOwnerId: vi.fn(),
|
||||||
@@ -87,7 +89,15 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
|||||||
|
|
||||||
const mockSeasonFindById = vi.fn();
|
const mockSeasonFindById = vi.fn();
|
||||||
const mockSeasonAdd = 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,
|
findById: mockSeasonFindById,
|
||||||
findByLeagueId: vi.fn(),
|
findByLeagueId: vi.fn(),
|
||||||
create: vi.fn(),
|
create: vi.fn(),
|
||||||
@@ -105,7 +115,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
|||||||
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
||||||
mockSeasonAdd.mockResolvedValue(undefined);
|
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({
|
const config = createLeagueConfigFormModel({
|
||||||
basics: {
|
basics: {
|
||||||
@@ -157,7 +170,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
|||||||
mockSeasonFindById.mockResolvedValue(sourceSeason);
|
mockSeasonFindById.mockResolvedValue(sourceSeason);
|
||||||
mockSeasonAdd.mockResolvedValue(undefined);
|
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 = {
|
const command: CreateSeasonForLeagueInput = {
|
||||||
leagueId: 'league-1',
|
leagueId: 'league-1',
|
||||||
@@ -177,7 +193,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
|||||||
it('returns error when league not found', async () => {
|
it('returns error when league not found', async () => {
|
||||||
mockLeagueFindById.mockResolvedValue(null);
|
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 = {
|
const command: CreateSeasonForLeagueInput = {
|
||||||
leagueId: 'missing-league',
|
leagueId: 'missing-league',
|
||||||
@@ -197,7 +216,10 @@ describe('CreateSeasonForLeagueUseCase', () => {
|
|||||||
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
|
||||||
mockSeasonFindById.mockResolvedValue(undefined);
|
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 = {
|
const command: CreateSeasonForLeagueInput = {
|
||||||
leagueId: 'league-1',
|
leagueId: 'league-1',
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
*
|
*
|
||||||
* Creates a new sponsor.
|
* 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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
|
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
|
||||||
|
import { SponsorRepository } from '../../domain/repositories/SponsorRepository';
|
||||||
|
|
||||||
export interface CreateSponsorInput {
|
export interface CreateSponsorInput {
|
||||||
name: string;
|
name: string;
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import {
|
|||||||
CreateTeamUseCase,
|
CreateTeamUseCase,
|
||||||
type CreateTeamInput
|
type CreateTeamInput
|
||||||
} from './CreateTeamUseCase';
|
} from './CreateTeamUseCase';
|
||||||
|
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
|
|
||||||
describe('CreateTeamUseCase', () => {
|
describe('CreateTeamUseCase', () => {
|
||||||
let useCase: CreateTeamUseCase;
|
let useCase: CreateTeamUseCase;
|
||||||
@@ -34,9 +37,9 @@ describe('CreateTeamUseCase', () => {
|
|||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new CreateTeamUseCase(teamRepository as any,
|
useCase = new CreateTeamUseCase(teamRepository as unknown as TeamRepository,
|
||||||
membershipRepository as any,
|
membershipRepository as unknown as TeamMembershipRepository,
|
||||||
logger as any);
|
logger as unknown as Logger);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create team successfully', async () => {
|
it('should create team successfully', async () => {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Creates a new team.
|
* 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 { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from 'uuid';
|
||||||
@@ -13,6 +13,9 @@ import type {
|
|||||||
TeamMembershipStatus,
|
TeamMembershipStatus,
|
||||||
TeamRole,
|
TeamRole,
|
||||||
} from '../../domain/types/TeamMembership';
|
} from '../../domain/types/TeamMembership';
|
||||||
|
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
|
|
||||||
export interface CreateTeamInput {
|
export interface CreateTeamInput {
|
||||||
name: string;
|
name: string;
|
||||||
tag: string;
|
tag: string;
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 = {
|
export type DeleteLeagueSeasonScheduleRaceResult = {
|
||||||
success: true;
|
success: true;
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
|
|||||||
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
|
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
|
||||||
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
|
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
|
||||||
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
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', () => {
|
describe('FileProtestUseCase', () => {
|
||||||
let mockProtestRepo: {
|
let mockProtestRepo: {
|
||||||
@@ -29,9 +30,11 @@ describe('FileProtestUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when race does not exist', async () => {
|
it('should return error when race does not exist', async () => {
|
||||||
const useCase = new FileProtestUseCase(mockProtestRepo as any,
|
const useCase = new FileProtestUseCase(
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any);
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue(null);
|
mockRaceRepo.findById.mockResolvedValue(null);
|
||||||
|
|
||||||
@@ -49,9 +52,11 @@ describe('FileProtestUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when protesting against self', async () => {
|
it('should return error when protesting against self', async () => {
|
||||||
const useCase = new FileProtestUseCase(mockProtestRepo as any,
|
const useCase = new FileProtestUseCase(
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any);
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
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 () => {
|
it('should return error when protesting driver is not an active member', async () => {
|
||||||
const useCase = new FileProtestUseCase(mockProtestRepo as any,
|
const useCase = new FileProtestUseCase(
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any);
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
|
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
|
||||||
@@ -92,9 +99,11 @@ describe('FileProtestUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should create protest and return protestId on success', async () => {
|
it('should create protest and return protestId on success', async () => {
|
||||||
const useCase = new FileProtestUseCase(mockProtestRepo as any,
|
const useCase = new FileProtestUseCase(
|
||||||
mockRaceRepo as any,
|
mockProtestRepo as unknown as ProtestRepository,
|
||||||
mockLeagueMembershipRepo as any);
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
|
mockLeagueMembershipRepo as unknown as LeagueMembershipRepository
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
|
||||||
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
|
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
|
||||||
@@ -114,7 +123,7 @@ describe('FileProtestUseCase', () => {
|
|||||||
expect(result.isOk()).toBe(true);
|
expect(result.isOk()).toBe(true);
|
||||||
const presented = result.unwrap();
|
const presented = result.unwrap();
|
||||||
expect(mockProtestRepo.create).toHaveBeenCalledTimes(1);
|
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.raceId.toString()).toBe('race1');
|
||||||
expect(created.protestingDriverId.toString()).toBe('driver1');
|
expect(created.protestingDriverId.toString()).toBe('driver1');
|
||||||
|
|||||||
@@ -27,11 +27,11 @@ describe('GetAllLeaguesWithCapacityAndScoringUseCase', () => {
|
|||||||
|
|
||||||
it('should return enriched leagues with capacity and scoring', async () => {
|
it('should return enriched leagues with capacity and scoring', async () => {
|
||||||
const useCase = new GetAllLeaguesWithCapacityAndScoringUseCase(
|
const useCase = new GetAllLeaguesWithCapacityAndScoringUseCase(
|
||||||
mockLeagueRepo as any,
|
mockLeagueRepo as unknown as LeagueRepository,
|
||||||
mockMembershipRepo as any,
|
mockMembershipRepo as unknown as LeagueMembershipRepository,
|
||||||
mockSeasonRepo as any,
|
mockSeasonRepo as unknown as SeasonRepository,
|
||||||
mockScoringConfigRepo as any,
|
mockScoringConfigRepo as unknown as LeagueScoringConfigRepository,
|
||||||
mockGameRepo as any,
|
mockGameRepo as unknown as GameRepository,
|
||||||
{ getPresetById: vi.fn().mockReturnValue({ id: 'preset1', name: 'Default' }) }
|
{ getPresetById: vi.fn().mockReturnValue({ id: 'preset1', name: 'Default' }) }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { Logger } from '@core/shared/domain/Logger';
|
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 { League } from '../../domain/entities/League';
|
||||||
import { Race } from '../../domain/entities/Race';
|
import { Race } from '../../domain/entities/Race';
|
||||||
import {
|
import {
|
||||||
@@ -7,9 +7,24 @@ import {
|
|||||||
type GetAllRacesPageDataInput
|
type GetAllRacesPageDataInput
|
||||||
} from './GetAllRacesPageDataUseCase';
|
} from './GetAllRacesPageDataUseCase';
|
||||||
|
|
||||||
|
import { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
import { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
||||||
|
|
||||||
describe('GetAllRacesPageDataUseCase', () => {
|
describe('GetAllRacesPageDataUseCase', () => {
|
||||||
const mockRaceFindAll = vi.fn();
|
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(),
|
findById: vi.fn(),
|
||||||
findAll: mockRaceFindAll,
|
findAll: mockRaceFindAll,
|
||||||
findByLeagueId: vi.fn(),
|
findByLeagueId: vi.fn(),
|
||||||
@@ -24,7 +39,16 @@ describe('GetAllRacesPageDataUseCase', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const mockLeagueFindAll = vi.fn();
|
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(),
|
findById: vi.fn(),
|
||||||
findAll: mockLeagueFindAll,
|
findAll: mockLeagueFindAll,
|
||||||
findByOwnerId: vi.fn(),
|
findByOwnerId: vi.fn(),
|
||||||
@@ -47,9 +71,11 @@ describe('GetAllRacesPageDataUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return races and filters data', async () => {
|
it('should return races and filters data', async () => {
|
||||||
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
|
const useCase = new GetAllRacesPageDataUseCase(
|
||||||
mockLeagueRepo,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger);
|
mockLeagueRepo as unknown as LeagueRepository,
|
||||||
|
mockLogger
|
||||||
|
);
|
||||||
|
|
||||||
const race1 = Race.create({
|
const race1 = Race.create({
|
||||||
id: 'race1',
|
id: 'race1',
|
||||||
@@ -132,9 +158,11 @@ describe('GetAllRacesPageDataUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return empty result when no races or leagues', async () => {
|
it('should return empty result when no races or leagues', async () => {
|
||||||
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
|
const useCase = new GetAllRacesPageDataUseCase(
|
||||||
mockLeagueRepo,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger);
|
mockLeagueRepo as unknown as LeagueRepository,
|
||||||
|
mockLogger
|
||||||
|
);
|
||||||
|
|
||||||
mockRaceFindAll.mockResolvedValue([]);
|
mockRaceFindAll.mockResolvedValue([]);
|
||||||
mockLeagueFindAll.mockResolvedValue([]);
|
mockLeagueFindAll.mockResolvedValue([]);
|
||||||
@@ -157,9 +185,11 @@ describe('GetAllRacesPageDataUseCase', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should return error when repository throws', async () => {
|
it('should return error when repository throws', async () => {
|
||||||
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
|
const useCase = new GetAllRacesPageDataUseCase(
|
||||||
mockLeagueRepo,
|
mockRaceRepo as unknown as RaceRepository,
|
||||||
mockLogger);
|
mockLeagueRepo as unknown as LeagueRepository,
|
||||||
|
mockLogger
|
||||||
|
);
|
||||||
|
|
||||||
const error = new Error('Repository error');
|
const error = new Error('Repository error');
|
||||||
mockRaceFindAll.mockRejectedValue(error);
|
mockRaceFindAll.mockRejectedValue(error);
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 = {};
|
export type GetAllRacesPageDataInput = {};
|
||||||
|
|
||||||
@@ -46,12 +50,12 @@ export class GetAllRacesPageDataUseCase {
|
|||||||
]);
|
]);
|
||||||
this.logger.info(`Found ${allRaces.length} races and ${allLeagues.length} leagues.`);
|
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
|
const races: GetAllRacesPageRaceItem[] = allRaces
|
||||||
.slice()
|
.slice()
|
||||||
.sort((a, b) => b.scheduledAt.getTime() - a.scheduledAt.getTime())
|
.sort((a: Race, b: Race) => b.scheduledAt.getTime() - a.scheduledAt.getTime())
|
||||||
.map(race => ({
|
.map((race: Race) => ({
|
||||||
id: race.id,
|
id: race.id,
|
||||||
track: race.track,
|
track: race.track,
|
||||||
car: race.car,
|
car: race.car,
|
||||||
|
|||||||
@@ -2,6 +2,9 @@ import { Result } from '@core/shared/domain/Result';
|
|||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { League } from '../../domain/entities/League';
|
import type { League } from '../../domain/entities/League';
|
||||||
import type { Race } from '../../domain/entities/Race';
|
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 = {};
|
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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 {}
|
export interface GetAllTeamsInput {}
|
||||||
|
|
||||||
@@ -39,6 +42,7 @@ export class GetAllTeamsUseCase {
|
|||||||
async execute(
|
async execute(
|
||||||
_input: GetAllTeamsInput,
|
_input: GetAllTeamsInput,
|
||||||
): Promise<Result<GetAllTeamsResult, ApplicationErrorCode<GetAllTeamsErrorCode, { message: string }>>> {
|
): Promise<Result<GetAllTeamsResult, ApplicationErrorCode<GetAllTeamsErrorCode, { message: string }>>> {
|
||||||
|
void _input;
|
||||||
this.logger.debug('GetAllTeamsUseCase: Fetching all teams');
|
this.logger.debug('GetAllTeamsUseCase: Fetching all teams');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -71,7 +75,7 @@ export class GetAllTeamsUseCase {
|
|||||||
rating: stats?.rating ?? 0,
|
rating: stats?.rating ?? 0,
|
||||||
logoUrl: logoUrl ?? null,
|
logoUrl: logoUrl ?? null,
|
||||||
description: team.description.toString(),
|
description: team.description.toString(),
|
||||||
leagues: team.leagues.map(l => l.toString()),
|
leagues: team.leagues.map((l) => l.toString()),
|
||||||
isRecruiting: team.isRecruiting,
|
isRecruiting: team.isRecruiting,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,11 +4,12 @@
|
|||||||
* Retrieves all liveries for a specific driver.
|
* 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 { UseCase } from '@core/shared/application/UseCase';
|
||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import { LiveryRepository } from '../../domain/repositories/LiveryRepository';
|
||||||
|
|
||||||
export interface GetDriverLiveriesInput {
|
export interface GetDriverLiveriesInput {
|
||||||
driverId: string;
|
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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { Team } from '../../domain/entities/Team';
|
import type { Team } from '../../domain/entities/Team';
|
||||||
|
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
|
|
||||||
export interface GetDriverTeamInput {
|
export interface GetDriverTeamInput {
|
||||||
driverId: string;
|
driverId: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
|
|||||||
import type { Driver } from '../../domain/entities/Driver';
|
import type { Driver } from '../../domain/entities/Driver';
|
||||||
import type { Team } from '../../domain/entities/Team';
|
import type { Team } from '../../domain/entities/Team';
|
||||||
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
|
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
|
||||||
import type { DriverStatsUseCase } from './DriverStatsUseCase';
|
import type { DriverStats, DriverStatsUseCase } from './DriverStatsUseCase';
|
||||||
import type { RankingUseCase } from './RankingUseCase';
|
import type { RankingUseCase } from './RankingUseCase';
|
||||||
import { SkillLevelService, type SkillLevel } from '../../domain/services/SkillLevelService';
|
import { SkillLevelService, type SkillLevel } from '../../domain/services/SkillLevelService';
|
||||||
import { MediaReference } from '@core/domain/media/MediaReference';
|
import { MediaReference } from '@core/domain/media/MediaReference';
|
||||||
@@ -71,10 +71,11 @@ export class GetDriversLeaderboardUseCase implements UseCase<GetDriversLeaderboa
|
|||||||
this.driverStatsUseCase.getDriverStats(driver.id)
|
this.driverStatsUseCase.getDriverStats(driver.id)
|
||||||
);
|
);
|
||||||
const statsResults = await Promise.all(statsPromises);
|
const statsResults = await Promise.all(statsPromises);
|
||||||
const statsMap = new Map<string, any>();
|
const statsMap = new Map<string, DriverStats>();
|
||||||
drivers.forEach((driver, idx) => {
|
drivers.forEach((driver, idx) => {
|
||||||
if (statsResults[idx]) {
|
const stats = statsResults[idx];
|
||||||
statsMap.set(driver.id, statsResults[idx]);
|
if (stats) {
|
||||||
|
statsMap.set(driver.id, stats);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';
|
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;
|
export type SponsorshipEntityType = SponsorableEntityType;
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import type { Logger } from '@core/shared/domain/Logger';
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { League } from '../../domain/entities/League';
|
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 = {
|
export type LeagueAdminPermissions = {
|
||||||
canManageSchedule: boolean;
|
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 {
|
import {
|
||||||
GetLeagueAdminUseCase,
|
GetLeagueAdminUseCase,
|
||||||
type GetLeagueAdminInput,
|
type GetLeagueAdminInput,
|
||||||
type GetLeagueAdminResult,
|
|
||||||
type GetLeagueAdminErrorCode,
|
type GetLeagueAdminErrorCode,
|
||||||
} from './GetLeagueAdminUseCase';
|
} from './GetLeagueAdminUseCase';
|
||||||
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
|
||||||
describe('GetLeagueAdminUseCase', () => {
|
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;
|
let mockFindById: Mock;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -26,7 +34,7 @@ describe('GetLeagueAdminUseCase', () => {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const createUseCase = () => new GetLeagueAdminUseCase(mockLeagueRepo);
|
const createUseCase = () => new GetLeagueAdminUseCase(mockLeagueRepo as unknown as LeagueRepository);
|
||||||
|
|
||||||
const params: GetLeagueAdminInput = {
|
const params: GetLeagueAdminInput = {
|
||||||
leagueId: 'league1',
|
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 {
|
import {
|
||||||
GetLeagueDriverSeasonStatsUseCase,
|
GetLeagueDriverSeasonStatsUseCase,
|
||||||
type GetLeagueDriverSeasonStatsErrorCode,
|
type GetLeagueDriverSeasonStatsErrorCode,
|
||||||
type GetLeagueDriverSeasonStatsInput,
|
type GetLeagueDriverSeasonStatsInput,
|
||||||
type GetLeagueDriverSeasonStatsResult,
|
|
||||||
} from './GetLeagueDriverSeasonStatsUseCase';
|
} from './GetLeagueDriverSeasonStatsUseCase';
|
||||||
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
|
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
|
||||||
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
|
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
|
||||||
@@ -22,12 +21,70 @@ describe('GetLeagueDriverSeasonStatsUseCase', () => {
|
|||||||
const mockDriverFindById = vi.fn();
|
const mockDriverFindById = vi.fn();
|
||||||
|
|
||||||
let useCase: GetLeagueDriverSeasonStatsUseCase;
|
let useCase: GetLeagueDriverSeasonStatsUseCase;
|
||||||
let standingRepository: any;
|
let standingRepository: {
|
||||||
let resultRepository: any;
|
findByLeagueId: Mock;
|
||||||
let penaltyRepository: any;
|
findByDriverIdAndLeagueId: Mock;
|
||||||
let raceRepository: any;
|
findAll: Mock;
|
||||||
let driverRepository: any;
|
save: Mock;
|
||||||
let driverRatingPort: any;
|
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(() => {
|
beforeEach(() => {
|
||||||
mockStandingFindByLeagueId.mockReset();
|
mockStandingFindByLeagueId.mockReset();
|
||||||
@@ -102,12 +159,14 @@ describe('GetLeagueDriverSeasonStatsUseCase', () => {
|
|||||||
updateDriverRating: vi.fn(),
|
updateDriverRating: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
useCase = new GetLeagueDriverSeasonStatsUseCase(standingRepository,
|
useCase = new GetLeagueDriverSeasonStatsUseCase(
|
||||||
resultRepository,
|
standingRepository as unknown as StandingRepository,
|
||||||
penaltyRepository,
|
resultRepository as unknown as ResultRepository,
|
||||||
raceRepository,
|
penaltyRepository as unknown as PenaltyRepository,
|
||||||
driverRepository,
|
raceRepository as unknown as RaceRepository,
|
||||||
driverRatingPort);
|
driverRepository as unknown as DriverRepository,
|
||||||
|
driverRatingPort as unknown as DriverRatingPort
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return league driver season stats for given league id', async () => {
|
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 {
|
import {
|
||||||
GetLeagueFullConfigUseCase,
|
GetLeagueFullConfigUseCase,
|
||||||
type GetLeagueFullConfigInput,
|
type GetLeagueFullConfigInput,
|
||||||
type GetLeagueFullConfigResult,
|
|
||||||
type GetLeagueFullConfigErrorCode,
|
type GetLeagueFullConfigErrorCode,
|
||||||
} from './GetLeagueFullConfigUseCase';
|
} from './GetLeagueFullConfigUseCase';
|
||||||
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
||||||
@@ -13,10 +12,10 @@ import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorC
|
|||||||
|
|
||||||
describe('GetLeagueFullConfigUseCase', () => {
|
describe('GetLeagueFullConfigUseCase', () => {
|
||||||
let useCase: GetLeagueFullConfigUseCase;
|
let useCase: GetLeagueFullConfigUseCase;
|
||||||
let leagueRepository: any;
|
let leagueRepository: { findById: Mock };
|
||||||
let seasonRepository: any;
|
let seasonRepository: { findByLeagueId: Mock };
|
||||||
let leagueScoringConfigRepository: any;
|
let leagueScoringConfigRepository: { findBySeasonId: Mock };
|
||||||
let gameRepository: any;
|
let gameRepository: { findById: Mock };
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
leagueRepository = {
|
leagueRepository = {
|
||||||
@@ -32,10 +31,12 @@ describe('GetLeagueFullConfigUseCase', () => {
|
|||||||
findById: vi.fn(),
|
findById: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
useCase = new GetLeagueFullConfigUseCase(leagueRepository,
|
useCase = new GetLeagueFullConfigUseCase(
|
||||||
seasonRepository,
|
leagueRepository as unknown as LeagueRepository,
|
||||||
leagueScoringConfigRepository,
|
seasonRepository as unknown as SeasonRepository,
|
||||||
gameRepository);
|
leagueScoringConfigRepository as unknown as LeagueScoringConfigRepository,
|
||||||
|
gameRepository as unknown as GameRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return league config when league exists', async () => {
|
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 {
|
import {
|
||||||
GetLeagueMembershipsUseCase,
|
GetLeagueMembershipsUseCase,
|
||||||
type GetLeagueMembershipsInput,
|
type GetLeagueMembershipsInput,
|
||||||
type GetLeagueMembershipsResult,
|
|
||||||
type GetLeagueMembershipsErrorCode,
|
type GetLeagueMembershipsErrorCode,
|
||||||
} from './GetLeagueMembershipsUseCase';
|
} from './GetLeagueMembershipsUseCase';
|
||||||
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
|
import { LeagueMembership } from '../../domain/entities/LeagueMembership';
|
||||||
import { Driver } from '../../domain/entities/Driver';
|
import { Driver } from '../../domain/entities/Driver';
|
||||||
import { League } from '../../domain/entities/League';
|
import { League } from '../../domain/entities/League';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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', () => {
|
describe('GetLeagueMembershipsUseCase', () => {
|
||||||
let useCase: GetLeagueMembershipsUseCase;
|
let useCase: GetLeagueMembershipsUseCase;
|
||||||
@@ -32,9 +34,11 @@ describe('GetLeagueMembershipsUseCase', () => {
|
|||||||
leagueRepository = {
|
leagueRepository = {
|
||||||
findById: vi.fn(),
|
findById: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new GetLeagueMembershipsUseCase(leagueMembershipRepository as any,
|
useCase = new GetLeagueMembershipsUseCase(
|
||||||
driverRepository as any,
|
leagueMembershipRepository as unknown as LeagueMembershipRepository,
|
||||||
leagueRepository as any);
|
driverRepository as unknown as DriverRepository,
|
||||||
|
leagueRepository as unknown as LeagueRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return league memberships with drivers', async () => {
|
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 {
|
import {
|
||||||
GetLeagueOwnerSummaryUseCase,
|
GetLeagueOwnerSummaryUseCase,
|
||||||
type GetLeagueOwnerSummaryInput,
|
type GetLeagueOwnerSummaryInput,
|
||||||
type GetLeagueOwnerSummaryResult,
|
|
||||||
type GetLeagueOwnerSummaryErrorCode,
|
type GetLeagueOwnerSummaryErrorCode,
|
||||||
} from './GetLeagueOwnerSummaryUseCase';
|
} from './GetLeagueOwnerSummaryUseCase';
|
||||||
import { Driver } from '../../domain/entities/Driver';
|
import { Driver } from '../../domain/entities/Driver';
|
||||||
import { League } from '../../domain/entities/League';
|
import { League } from '../../domain/entities/League';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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', () => {
|
describe('GetLeagueOwnerSummaryUseCase', () => {
|
||||||
let useCase: GetLeagueOwnerSummaryUseCase;
|
let useCase: GetLeagueOwnerSummaryUseCase;
|
||||||
@@ -40,10 +43,10 @@ describe('GetLeagueOwnerSummaryUseCase', () => {
|
|||||||
findByLeagueId: vi.fn(),
|
findByLeagueId: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new GetLeagueOwnerSummaryUseCase(
|
useCase = new GetLeagueOwnerSummaryUseCase(
|
||||||
leagueRepository as any,
|
leagueRepository as unknown as LeagueRepository,
|
||||||
driverRepository as any,
|
driverRepository as unknown as DriverRepository,
|
||||||
leagueMembershipRepository as any,
|
leagueMembershipRepository as unknown as LeagueMembershipRepository,
|
||||||
standingRepository as any
|
standingRepository as unknown as StandingRepository
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
GetLeagueScheduleUseCase,
|
GetLeagueScheduleUseCase,
|
||||||
type GetLeagueScheduleInput,
|
type GetLeagueScheduleInput,
|
||||||
type GetLeagueScheduleResult,
|
|
||||||
type GetLeagueScheduleErrorCode,
|
type GetLeagueScheduleErrorCode,
|
||||||
} from './GetLeagueScheduleUseCase';
|
} from './GetLeagueScheduleUseCase';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 { Race } from '../../domain/entities/Race';
|
||||||
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
||||||
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
|
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
|
||||||
|
|
||||||
describe('GetLeagueScheduleUseCase', () => {
|
describe('GetLeagueScheduleUseCase', () => {
|
||||||
let useCase: GetLeagueScheduleUseCase;
|
let useCase: GetLeagueScheduleUseCase;
|
||||||
@@ -42,7 +43,7 @@ describe('GetLeagueScheduleUseCase', () => {
|
|||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
} as unknown as Logger;
|
} as unknown as Logger;
|
||||||
useCase = new GetLeagueScheduleUseCase(leagueRepository as unknown as LeagueRepository,
|
useCase = new GetLeagueScheduleUseCase(leagueRepository as unknown as LeagueRepository,
|
||||||
seasonRepository as any,
|
seasonRepository as unknown as SeasonRepository,
|
||||||
raceRepository as unknown as RaceRepository,
|
raceRepository as unknown as RaceRepository,
|
||||||
logger);
|
logger);
|
||||||
});
|
});
|
||||||
@@ -109,7 +110,7 @@ describe('GetLeagueScheduleUseCase', () => {
|
|||||||
raceRepository.findByLeagueId.mockResolvedValue([janRace, febRace]);
|
raceRepository.findByLeagueId.mockResolvedValue([janRace, febRace]);
|
||||||
|
|
||||||
// Season 1 covers January
|
// 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);
|
expect(resultSeason1.isOk()).toBe(true);
|
||||||
const resultValue1 = resultSeason1.unwrap();
|
const resultValue1 = resultSeason1.unwrap();
|
||||||
expect(resultValue1.seasonId).toBe('season-jan');
|
expect(resultValue1.seasonId).toBe('season-jan');
|
||||||
@@ -117,7 +118,7 @@ describe('GetLeagueScheduleUseCase', () => {
|
|||||||
expect(resultValue1.races.map(r => r.race.id)).toEqual(['race-jan']);
|
expect(resultValue1.races.map(r => r.race.id)).toEqual(['race-jan']);
|
||||||
|
|
||||||
// Season 2 covers February
|
// 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);
|
expect(resultSeason2.isOk()).toBe(true);
|
||||||
const resultValue2 = resultSeason2.unwrap();
|
const resultValue2 = resultSeason2.unwrap();
|
||||||
expect(resultValue2.seasonId).toBe('season-feb');
|
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 type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { League } from '../../domain/entities/League';
|
import type { League } from '../../domain/entities/League';
|
||||||
import type { Race } from '../../domain/entities/Race';
|
import type { Race } from '../../domain/entities/Race';
|
||||||
import type { Season } from '../../domain/entities/season/Season';
|
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 =
|
export type GetLeagueScheduleErrorCode =
|
||||||
| 'LEAGUE_NOT_FOUND'
|
| 'LEAGUE_NOT_FOUND'
|
||||||
@@ -51,7 +54,7 @@ export class GetLeagueScheduleUseCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const seasons = await this.seasonRepository.findByLeagueId(params.leagueId);
|
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) {
|
if (!activeSeason) {
|
||||||
return Result.err({
|
return Result.err({
|
||||||
code: 'SEASON_NOT_FOUND',
|
code: 'SEASON_NOT_FOUND',
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
|
||||||
import { GetLeagueScoringConfigUseCase } from './GetLeagueScoringConfigUseCase';
|
import { GetLeagueScoringConfigUseCase } from './GetLeagueScoringConfigUseCase';
|
||||||
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
|
||||||
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
|
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
|
||||||
@@ -13,10 +12,26 @@ import type { LeagueScoringPreset } from '../../domain/types/LeagueScoringPreset
|
|||||||
|
|
||||||
describe('GetLeagueScoringConfigUseCase', () => {
|
describe('GetLeagueScoringConfigUseCase', () => {
|
||||||
let useCase: GetLeagueScoringConfigUseCase;
|
let useCase: GetLeagueScoringConfigUseCase;
|
||||||
let mockLeagueRepository: any;
|
let mockLeagueRepository: {
|
||||||
let mockSeasonRepository: any;
|
findById: any;
|
||||||
let mockLeagueScoringConfigRepository: any;
|
exists: any;
|
||||||
let mockGameRepository: 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 };
|
let mockPresetProvider: { getPresetById: any };
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
@@ -49,11 +64,11 @@ describe('GetLeagueScoringConfigUseCase', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useCase = new GetLeagueScoringConfigUseCase(
|
useCase = new GetLeagueScoringConfigUseCase(
|
||||||
mockLeagueRepository,
|
mockLeagueRepository as unknown as LeagueRepository,
|
||||||
mockSeasonRepository,
|
mockSeasonRepository as unknown as SeasonRepository,
|
||||||
mockLeagueScoringConfigRepository,
|
mockLeagueScoringConfigRepository as unknown as LeagueScoringConfigRepository,
|
||||||
mockGameRepository,
|
mockGameRepository as unknown as GameRepository,
|
||||||
mockPresetProvider,
|
mockPresetProvider as any,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
GetRaceDetailUseCase,
|
GetRaceDetailUseCase,
|
||||||
type GetRaceDetailInput,
|
type GetRaceDetailInput,
|
||||||
type GetRaceDetailResult,
|
|
||||||
type GetRaceDetailErrorCode,
|
type GetRaceDetailErrorCode,
|
||||||
} from './GetRaceDetailUseCase';
|
} from './GetRaceDetailUseCase';
|
||||||
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
GetRacePenaltiesUseCase,
|
GetRacePenaltiesUseCase,
|
||||||
type GetRacePenaltiesInput,
|
type GetRacePenaltiesInput,
|
||||||
type GetRacePenaltiesResult,
|
|
||||||
type GetRacePenaltiesErrorCode,
|
type GetRacePenaltiesErrorCode,
|
||||||
} from './GetRacePenaltiesUseCase';
|
} from './GetRacePenaltiesUseCase';
|
||||||
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
|
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
|
||||||
|
|||||||
@@ -9,6 +9,9 @@ import { Protest } from '../../domain/entities/Protest';
|
|||||||
import { Driver } from '../../domain/entities/Driver';
|
import { Driver } from '../../domain/entities/Driver';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
|
||||||
|
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
|
||||||
|
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
|
||||||
|
|
||||||
describe('GetRaceProtestsUseCase', () => {
|
describe('GetRaceProtestsUseCase', () => {
|
||||||
let useCase: GetRaceProtestsUseCase;
|
let useCase: GetRaceProtestsUseCase;
|
||||||
let protestRepository: { findByRaceId: Mock };
|
let protestRepository: { findByRaceId: Mock };
|
||||||
@@ -17,8 +20,10 @@ describe('GetRaceProtestsUseCase', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
protestRepository = { findByRaceId: vi.fn() };
|
protestRepository = { findByRaceId: vi.fn() };
|
||||||
driverRepository = { findById: vi.fn() };
|
driverRepository = { findById: vi.fn() };
|
||||||
useCase = new GetRaceProtestsUseCase(protestRepository as any,
|
useCase = new GetRaceProtestsUseCase(
|
||||||
driverRepository as any);
|
protestRepository as unknown as ProtestRepository,
|
||||||
|
driverRepository as unknown as DriverRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return protests with drivers', async () => {
|
it('should return protests with drivers', async () => {
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
GetRaceRegistrationsUseCase,
|
GetRaceRegistrationsUseCase,
|
||||||
type GetRaceRegistrationsInput,
|
type GetRaceRegistrationsInput,
|
||||||
type GetRaceRegistrationsResult,
|
|
||||||
type GetRaceRegistrationsErrorCode,
|
type GetRaceRegistrationsErrorCode,
|
||||||
} from './GetRaceRegistrationsUseCase';
|
} from './GetRaceRegistrationsUseCase';
|
||||||
import { RaceRegistration } from '@core/racing/domain/entities/RaceRegistration';
|
import { RaceRegistration } from '@core/racing/domain/entities/RaceRegistration';
|
||||||
import { Race } from '@core/racing/domain/entities/Race';
|
import { Race } from '@core/racing/domain/entities/Race';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
|
||||||
|
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
|
||||||
|
|
||||||
describe('GetRaceRegistrationsUseCase', () => {
|
describe('GetRaceRegistrationsUseCase', () => {
|
||||||
let useCase: GetRaceRegistrationsUseCase;
|
let useCase: GetRaceRegistrationsUseCase;
|
||||||
@@ -17,8 +18,10 @@ describe('GetRaceRegistrationsUseCase', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
raceRepository = { findById: vi.fn() };
|
raceRepository = { findById: vi.fn() };
|
||||||
registrationRepository = { findByRaceId: vi.fn() };
|
registrationRepository = { findByRaceId: vi.fn() };
|
||||||
useCase = new GetRaceRegistrationsUseCase(raceRepository as any,
|
useCase = new GetRaceRegistrationsUseCase(
|
||||||
registrationRepository as any);
|
raceRepository as unknown as RaceRepository,
|
||||||
|
registrationRepository as unknown as RaceRegistrationRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return race and registrations on success', async () => {
|
it('should return race and registrations on success', async () => {
|
||||||
|
|||||||
@@ -27,11 +27,13 @@ describe('GetRaceResultsDetailUseCase', () => {
|
|||||||
driverRepository = { findAll: vi.fn() };
|
driverRepository = { findAll: vi.fn() };
|
||||||
penaltyRepository = { findByRaceId: vi.fn() };
|
penaltyRepository = { findByRaceId: vi.fn() };
|
||||||
|
|
||||||
useCase = new GetRaceResultsDetailUseCase(raceRepository as any,
|
useCase = new GetRaceResultsDetailUseCase(
|
||||||
leagueRepository as any,
|
raceRepository as unknown as RaceRepository,
|
||||||
resultRepository as any,
|
leagueRepository as unknown as LeagueRepository,
|
||||||
driverRepository as any,
|
resultRepository as unknown as ResultRepository,
|
||||||
penaltyRepository as any);
|
driverRepository as unknown as DriverRepository,
|
||||||
|
penaltyRepository as unknown as PenaltyRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('presents race results detail when race exists', async () => {
|
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 {
|
import {
|
||||||
GetRaceWithSOFUseCase,
|
GetRaceWithSOFUseCase,
|
||||||
type GetRaceWithSOFInput,
|
type GetRaceWithSOFInput,
|
||||||
type GetRaceWithSOFResult,
|
|
||||||
type GetRaceWithSOFErrorCode,
|
type GetRaceWithSOFErrorCode,
|
||||||
} from './GetRaceWithSOFUseCase';
|
} from './GetRaceWithSOFUseCase';
|
||||||
import { Race } from '../../domain/entities/Race';
|
import { Race } from '../../domain/entities/Race';
|
||||||
import { SessionType } from '../../domain/value-objects/SessionType';
|
import { SessionType } from '../../domain/value-objects/SessionType';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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', () => {
|
describe('GetRaceWithSOFUseCase', () => {
|
||||||
let useCase: GetRaceWithSOFUseCase;
|
let useCase: GetRaceWithSOFUseCase;
|
||||||
@@ -34,10 +36,10 @@ describe('GetRaceWithSOFUseCase', () => {
|
|||||||
};
|
};
|
||||||
getDriverRating = vi.fn();
|
getDriverRating = vi.fn();
|
||||||
useCase = new GetRaceWithSOFUseCase(
|
useCase = new GetRaceWithSOFUseCase(
|
||||||
raceRepository as any,
|
raceRepository as unknown as RaceRepository,
|
||||||
registrationRepository as any,
|
registrationRepository as unknown as RaceRegistrationRepository,
|
||||||
resultRepository as any,
|
resultRepository as unknown as ResultRepository,
|
||||||
getDriverRating
|
getDriverRating as any
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ import type { Logger } from '@core/shared/domain/Logger';
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import type { Race } from '../../domain/entities/Race';
|
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 = {
|
export type GetRacesPageRaceItem = {
|
||||||
race: Race;
|
race: Race;
|
||||||
@@ -35,16 +41,16 @@ export class GetRacesPageDataUseCase {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const leagueMap = new Map(
|
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
|
const filteredRaces = input.leagueId
|
||||||
? allRaces.filter(race => race.leagueId === input.leagueId)
|
? allRaces.filter((race: Race) => race.leagueId === input.leagueId)
|
||||||
: allRaces;
|
: 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,
|
race,
|
||||||
leagueName: leagueMap.get(race.leagueId) ?? 'Unknown League',
|
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 {
|
import {
|
||||||
GetSeasonDetailsUseCase,
|
GetSeasonDetailsUseCase,
|
||||||
type GetSeasonDetailsInput,
|
type GetSeasonDetailsInput,
|
||||||
type GetSeasonDetailsResult,
|
|
||||||
type GetSeasonDetailsErrorCode,
|
type GetSeasonDetailsErrorCode,
|
||||||
} from './GetSeasonDetailsUseCase';
|
} from './GetSeasonDetailsUseCase';
|
||||||
import { Season } from '../../domain/entities/season/Season';
|
import { Season } from '../../domain/entities/season/Season';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
|
||||||
|
|
||||||
describe('GetSeasonDetailsUseCase', () => {
|
describe('GetSeasonDetailsUseCase', () => {
|
||||||
let useCase: GetSeasonDetailsUseCase;
|
let useCase: GetSeasonDetailsUseCase;
|
||||||
@@ -19,7 +19,7 @@ describe('GetSeasonDetailsUseCase', () => {
|
|||||||
findById: vi.fn(),
|
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 () => {
|
it('returns full details for a season', async () => {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
|
|||||||
import {
|
import {
|
||||||
GetSeasonSponsorshipsUseCase,
|
GetSeasonSponsorshipsUseCase,
|
||||||
type GetSeasonSponsorshipsInput,
|
type GetSeasonSponsorshipsInput,
|
||||||
type GetSeasonSponsorshipsResult,
|
|
||||||
type GetSeasonSponsorshipsErrorCode,
|
type GetSeasonSponsorshipsErrorCode,
|
||||||
} from './GetSeasonSponsorshipsUseCase';
|
} from './GetSeasonSponsorshipsUseCase';
|
||||||
import type { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
|
import type { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
|
||||||
@@ -52,11 +51,13 @@ describe('GetSeasonSponsorshipsUseCase', () => {
|
|||||||
findByLeagueId: vi.fn(),
|
findByLeagueId: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
useCase = new GetSeasonSponsorshipsUseCase(seasonSponsorshipRepository as any,
|
useCase = new GetSeasonSponsorshipsUseCase(
|
||||||
seasonRepository as any,
|
seasonSponsorshipRepository as unknown as SeasonSponsorshipRepository,
|
||||||
leagueRepository as any,
|
seasonRepository as unknown as SeasonRepository,
|
||||||
leagueMembershipRepository as any,
|
leagueRepository as unknown as LeagueRepository,
|
||||||
raceRepository as any);
|
leagueMembershipRepository as unknown as LeagueMembershipRepository,
|
||||||
|
raceRepository as unknown as RaceRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('returns SEASON_NOT_FOUND when season does not exist', async () => {
|
it('returns SEASON_NOT_FOUND when season does not exist', async () => {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export class GetSponsorsUseCase {
|
|||||||
constructor(private readonly sponsorRepository: SponsorRepository) {}
|
constructor(private readonly sponsorRepository: SponsorRepository) {}
|
||||||
|
|
||||||
async execute(_input: GetSponsorsInput): Promise<Result<GetSponsorsResult, ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>>> {
|
async execute(_input: GetSponsorsInput): Promise<Result<GetSponsorsResult, ApplicationErrorCode<GetSponsorsErrorCode, { message: string }>>> {
|
||||||
|
void _input;
|
||||||
try {
|
try {
|
||||||
const sponsors = await this.sponsorRepository.findAll();
|
const sponsors = await this.sponsorRepository.findAll();
|
||||||
return Result.ok({ sponsors });
|
return Result.ok({ sponsors });
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ import {
|
|||||||
type GetTeamJoinRequestsResult,
|
type GetTeamJoinRequestsResult,
|
||||||
type GetTeamJoinRequestsErrorCode,
|
type GetTeamJoinRequestsErrorCode,
|
||||||
} from './GetTeamJoinRequestsUseCase';
|
} from './GetTeamJoinRequestsUseCase';
|
||||||
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
import { DriverRepository } from '../../domain/repositories/DriverRepository';
|
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
|
||||||
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
import { Driver } from '../../domain/entities/Driver';
|
import { Driver } from '../../domain/entities/Driver';
|
||||||
import { Team } from '../../domain/entities/Team';
|
import { Team } from '../../domain/entities/Team';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
@@ -34,9 +34,11 @@ describe('GetTeamJoinRequestsUseCase', () => {
|
|||||||
findById: vi.fn(),
|
findById: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
useCase = new GetTeamJoinRequestsUseCase(membershipRepository as any,
|
useCase = new GetTeamJoinRequestsUseCase(
|
||||||
driverRepository as any,
|
membershipRepository as unknown as TeamMembershipRepository,
|
||||||
teamRepository as any);
|
driverRepository as unknown as DriverRepository,
|
||||||
|
teamRepository as unknown as TeamRepository
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return join requests with drivers when team exists', async () => {
|
it('should return join requests with drivers when team exists', async () => {
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import {
|
|||||||
type GetTeamMembersErrorCode,
|
type GetTeamMembersErrorCode,
|
||||||
type GetTeamMembersInput
|
type GetTeamMembersInput
|
||||||
} from './GetTeamMembersUseCase';
|
} 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', () => {
|
describe('GetTeamMembersUseCase', () => {
|
||||||
let useCase: GetTeamMembersUseCase;
|
let useCase: GetTeamMembersUseCase;
|
||||||
@@ -41,10 +45,12 @@ describe('GetTeamMembersUseCase', () => {
|
|||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new GetTeamMembersUseCase(membershipRepository as any,
|
useCase = new GetTeamMembersUseCase(
|
||||||
driverRepository as any,
|
membershipRepository as unknown as TeamMembershipRepository,
|
||||||
teamRepository as any,
|
driverRepository as unknown as DriverRepository,
|
||||||
logger as any);
|
teamRepository as unknown as TeamRepository,
|
||||||
|
logger as unknown as Logger
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return team members with driver entities', async () => {
|
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 { Driver } from '../../domain/entities/Driver';
|
||||||
import type { Team } from '../../domain/entities/Team';
|
import type { Team } from '../../domain/entities/Team';
|
||||||
import type { TeamMembership } from '../../domain/types/TeamMembership';
|
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 = {
|
export type GetTeamMembersInput = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
import {
|
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
|
||||||
GetTeamMembershipUseCase,
|
|
||||||
type GetTeamMembershipErrorCode,
|
|
||||||
type GetTeamMembershipInput
|
|
||||||
} from './GetTeamMembershipUseCase';
|
|
||||||
|
|
||||||
describe('GetTeamMembershipUseCase', () => {
|
describe('GetTeamMembershipUseCase', () => {
|
||||||
const mockGetMembership = vi.fn();
|
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,
|
getMembership: mockGetMembership,
|
||||||
getActiveMembershipForDriver: vi.fn(),
|
getActiveMembershipForDriver: vi.fn(),
|
||||||
getTeamMembers: vi.fn(),
|
getTeamMembers: vi.fn(),
|
||||||
@@ -32,7 +38,7 @@ describe('GetTeamMembershipUseCase', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
vi.clearAllMocks();
|
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 () => {
|
it('should return membership data when membership exists', async () => {
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
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 = {
|
export type GetTeamMembershipInput = {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
driverId: string;
|
driverId: string;
|
||||||
|
|||||||
@@ -7,10 +7,15 @@ import {
|
|||||||
type GetTeamsLeaderboardInput
|
type GetTeamsLeaderboardInput
|
||||||
} from './GetTeamsLeaderboardUseCase';
|
} from './GetTeamsLeaderboardUseCase';
|
||||||
|
|
||||||
|
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
|
|
||||||
describe('GetTeamsLeaderboardUseCase', () => {
|
describe('GetTeamsLeaderboardUseCase', () => {
|
||||||
let useCase: GetTeamsLeaderboardUseCase;
|
let useCase: GetTeamsLeaderboardUseCase;
|
||||||
let teamRepository: {
|
let teamRepository: {
|
||||||
findAll: Mock;
|
findAll: Mock;
|
||||||
|
findById: Mock;
|
||||||
};
|
};
|
||||||
let teamMembershipRepository: {
|
let teamMembershipRepository: {
|
||||||
getTeamMembers: Mock;
|
getTeamMembers: Mock;
|
||||||
@@ -26,6 +31,7 @@ describe('GetTeamsLeaderboardUseCase', () => {
|
|||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
teamRepository = {
|
teamRepository = {
|
||||||
findAll: vi.fn(),
|
findAll: vi.fn(),
|
||||||
|
findById: vi.fn(),
|
||||||
};
|
};
|
||||||
teamMembershipRepository = {
|
teamMembershipRepository = {
|
||||||
getTeamMembers: vi.fn(),
|
getTeamMembers: vi.fn(),
|
||||||
@@ -37,10 +43,11 @@ describe('GetTeamsLeaderboardUseCase', () => {
|
|||||||
warn: vi.fn(),
|
warn: vi.fn(),
|
||||||
error: vi.fn(),
|
error: vi.fn(),
|
||||||
};
|
};
|
||||||
useCase = new GetTeamsLeaderboardUseCase(teamRepository as any,
|
useCase = new GetTeamsLeaderboardUseCase(
|
||||||
teamMembershipRepository as any,
|
teamRepository as unknown as TeamRepository,
|
||||||
|
teamMembershipRepository as unknown as TeamMembershipRepository,
|
||||||
getDriverStats as any,
|
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 { SkillLevelService, type SkillLevel } from '@core/racing/domain/services/SkillLevelService';
|
||||||
import type { Logger } from '@core/shared/domain/Logger';
|
import type { Logger } from '@core/shared/domain/Logger';
|
||||||
import { Result } from '@core/shared/domain/Result';
|
import { Result } from '@core/shared/domain/Result';
|
||||||
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
|
||||||
|
import { TeamRepository } from '../../domain/repositories/TeamRepository';
|
||||||
|
import { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
|
||||||
|
|
||||||
interface DriverStatsAdapter {
|
interface DriverStatsAdapter {
|
||||||
rating: number | null;
|
rating: number | null;
|
||||||
@@ -55,7 +57,7 @@ export class GetTeamsLeaderboardUseCase {
|
|||||||
const items: TeamLeaderboardItem[] = [];
|
const items: TeamLeaderboardItem[] = [];
|
||||||
|
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
allTeams.map(async (team) => {
|
allTeams.map(async (team: Team) => {
|
||||||
const memberships = await this.teamMembershipRepository.getTeamMembers(team.id);
|
const memberships = await this.teamMembershipRepository.getTeamMembers(team.id);
|
||||||
const memberCount = memberships.length;
|
const memberCount = memberships.length;
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class GetTotalDriversUseCase {
|
|||||||
async execute(
|
async execute(
|
||||||
_input: GetTotalDriversInput,
|
_input: GetTotalDriversInput,
|
||||||
): Promise<Result<GetTotalDriversResult, ApplicationErrorCode<GetTotalDriversErrorCode, { message: string }>>> {
|
): Promise<Result<GetTotalDriversResult, ApplicationErrorCode<GetTotalDriversErrorCode, { message: string }>>> {
|
||||||
|
void _input;
|
||||||
try {
|
try {
|
||||||
const drivers = await this.driverRepository.findAll();
|
const drivers = await this.driverRepository.findAll();
|
||||||
const totalDrivers = drivers.length;
|
const totalDrivers = drivers.length;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export class GetTotalLeaguesUseCase {
|
|||||||
async execute(
|
async execute(
|
||||||
_input: GetTotalLeaguesInput,
|
_input: GetTotalLeaguesInput,
|
||||||
): Promise<Result<GetTotalLeaguesResult, ApplicationErrorCode<GetTotalLeaguesErrorCode, { message: string }>>> {
|
): Promise<Result<GetTotalLeaguesResult, ApplicationErrorCode<GetTotalLeaguesErrorCode, { message: string }>>> {
|
||||||
|
void _input;
|
||||||
try {
|
try {
|
||||||
const leagues = await this.leagueRepository.findAll();
|
const leagues = await this.leagueRepository.findAll();
|
||||||
const totalLeagues = leagues.length;
|
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