3 Commits

Author SHA1 Message Date
afef777961 code quality
Some checks failed
CI / lint-typecheck (pull_request) Failing after 12s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
2026-01-26 02:27:37 +01:00
bf2c0fdb0c code quality
Some checks failed
CI / lint-typecheck (pull_request) Failing after 1m29s
CI / tests (pull_request) Has been skipped
CI / contract-tests (pull_request) Has been skipped
CI / e2e-tests (pull_request) Has been skipped
CI / comment-pr (pull_request) Has been skipped
CI / commit-types (pull_request) Has been skipped
2026-01-26 01:54:57 +01:00
49cc91e046 code quality 2026-01-26 01:36:22 +01:00
58 changed files with 638 additions and 234 deletions

View File

@@ -521,7 +521,9 @@
], ],
"parserOptions": { "parserOptions": {
"ecmaVersion": 2022, "ecmaVersion": 2022,
"sourceType": "module" "sourceType": "module",
"project": "./tsconfig.eslint.json",
"tsconfigRootDir": "."
}, },
"settings": { "settings": {
"boundaries/elements": [ "boundaries/elements": [

File diff suppressed because one or more lines are too long

View File

@@ -8,13 +8,13 @@
*/ */
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import type { Mock } from 'vitest';
import { GetDashboardUseCase } from './GetDashboardUseCase'; import { GetDashboardUseCase } from './GetDashboardUseCase';
import { ValidationError } from '../../../shared/errors/ValidationError'; import { ValidationError } from '../../../shared/errors/ValidationError';
import { DriverNotFoundError } from '../../domain/errors/DriverNotFoundError'; import { DriverNotFoundError } from '../../domain/errors/DriverNotFoundError';
import { DashboardRepository } from '../ports/DashboardRepository'; import { DashboardRepository, DriverData, RaceData } from '../ports/DashboardRepository';
import { DashboardEventPublisher } from '../ports/DashboardEventPublisher'; import { DashboardEventPublisher } from '../ports/DashboardEventPublisher';
import { Logger } from '../../../shared/domain/Logger'; import { Logger } from '../../../shared/domain/Logger';
import { DriverData, RaceData, LeagueStandingData, ActivityData } from '../ports/DashboardRepository';
describe('GetDashboardUseCase', () => { describe('GetDashboardUseCase', () => {
let mockDriverRepository: DashboardRepository; let mockDriverRepository: DashboardRepository;
@@ -120,7 +120,7 @@ describe('GetDashboardUseCase', () => {
it('should throw DriverNotFoundError when driverRepository.findDriverById returns null', async () => { it('should throw DriverNotFoundError when driverRepository.findDriverById returns null', async () => {
// Given // Given
const query = { driverId: 'driver-123' }; const query = { driverId: 'driver-123' };
(mockDriverRepository.findDriverById as any).mockResolvedValue(null); (mockDriverRepository.findDriverById as Mock).mockResolvedValue(null);
// When & Then // When & Then
await expect(useCase.execute(query)).rejects.toThrow(DriverNotFoundError); await expect(useCase.execute(query)).rejects.toThrow(DriverNotFoundError);
@@ -143,7 +143,7 @@ describe('GetDashboardUseCase', () => {
const query = { driverId: 'driver-123' }; const query = { driverId: 'driver-123' };
// Mock driver exists // Mock driver exists
(mockDriverRepository.findDriverById as any).mockResolvedValue({ (mockDriverRepository.findDriverById as Mock).mockResolvedValue({
id: 'driver-123', id: 'driver-123',
name: 'Test Driver', name: 'Test Driver',
rating: 1500, rating: 1500,
@@ -155,7 +155,7 @@ describe('GetDashboardUseCase', () => {
} as DriverData); } as DriverData);
// Mock races with missing trackName // Mock races with missing trackName
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([ (mockRaceRepository.getUpcomingRaces as Mock).mockResolvedValue([
{ {
id: 'race-1', id: 'race-1',
trackName: '', // Missing trackName trackName: '', // Missing trackName
@@ -170,8 +170,8 @@ describe('GetDashboardUseCase', () => {
}, },
] as RaceData[]); ] as RaceData[]);
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]); (mockLeagueRepository.getLeagueStandings as Mock).mockResolvedValue([]);
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]); (mockActivityRepository.getRecentActivity as Mock).mockResolvedValue([]);
// When // When
const result = await useCase.execute(query); const result = await useCase.execute(query);
@@ -186,7 +186,7 @@ describe('GetDashboardUseCase', () => {
const query = { driverId: 'driver-123' }; const query = { driverId: 'driver-123' };
// Mock driver exists // Mock driver exists
(mockDriverRepository.findDriverById as any).mockResolvedValue({ (mockDriverRepository.findDriverById as Mock).mockResolvedValue({
id: 'driver-123', id: 'driver-123',
name: 'Test Driver', name: 'Test Driver',
rating: 1500, rating: 1500,
@@ -198,7 +198,7 @@ describe('GetDashboardUseCase', () => {
} as DriverData); } as DriverData);
// Mock races with past dates // Mock races with past dates
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([ (mockRaceRepository.getUpcomingRaces as Mock).mockResolvedValue([
{ {
id: 'race-1', id: 'race-1',
trackName: 'Track A', trackName: 'Track A',
@@ -213,8 +213,8 @@ describe('GetDashboardUseCase', () => {
}, },
] as RaceData[]); ] as RaceData[]);
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]); (mockLeagueRepository.getLeagueStandings as Mock).mockResolvedValue([]);
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]); (mockActivityRepository.getRecentActivity as Mock).mockResolvedValue([]);
// When // When
const result = await useCase.execute(query); const result = await useCase.execute(query);
@@ -229,7 +229,7 @@ describe('GetDashboardUseCase', () => {
const query = { driverId: 'driver-123' }; const query = { driverId: 'driver-123' };
// Mock driver exists // Mock driver exists
(mockDriverRepository.findDriverById as any).mockResolvedValue({ (mockDriverRepository.findDriverById as Mock).mockResolvedValue({
id: 'driver-123', id: 'driver-123',
name: 'Test Driver', name: 'Test Driver',
rating: 1500, rating: 1500,
@@ -241,7 +241,7 @@ describe('GetDashboardUseCase', () => {
} as DriverData); } as DriverData);
// Mock races with various invalid states // Mock races with various invalid states
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([ (mockRaceRepository.getUpcomingRaces as Mock).mockResolvedValue([
{ {
id: 'race-1', id: 'race-1',
trackName: '', // Missing trackName trackName: '', // Missing trackName
@@ -262,8 +262,8 @@ describe('GetDashboardUseCase', () => {
}, },
] as RaceData[]); ] as RaceData[]);
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]); (mockLeagueRepository.getLeagueStandings as Mock).mockResolvedValue([]);
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]); (mockActivityRepository.getRecentActivity as Mock).mockResolvedValue([]);
// When // When
const result = await useCase.execute(query); const result = await useCase.execute(query);
@@ -278,7 +278,7 @@ describe('GetDashboardUseCase', () => {
const query = { driverId: 'driver-123' }; const query = { driverId: 'driver-123' };
// Mock driver exists // Mock driver exists
(mockDriverRepository.findDriverById as any).mockResolvedValue({ (mockDriverRepository.findDriverById as Mock).mockResolvedValue({
id: 'driver-123', id: 'driver-123',
name: 'Test Driver', name: 'Test Driver',
rating: 1500, rating: 1500,
@@ -290,7 +290,7 @@ describe('GetDashboardUseCase', () => {
} as DriverData); } as DriverData);
// Mock races with valid data // Mock races with valid data
(mockRaceRepository.getUpcomingRaces as any).mockResolvedValue([ (mockRaceRepository.getUpcomingRaces as Mock).mockResolvedValue([
{ {
id: 'race-1', id: 'race-1',
trackName: 'Track A', trackName: 'Track A',
@@ -305,8 +305,8 @@ describe('GetDashboardUseCase', () => {
}, },
] as RaceData[]); ] as RaceData[]);
(mockLeagueRepository.getLeagueStandings as any).mockResolvedValue([]); (mockLeagueRepository.getLeagueStandings as Mock).mockResolvedValue([]);
(mockActivityRepository.getRecentActivity as any).mockResolvedValue([]); (mockActivityRepository.getRecentActivity as Mock).mockResolvedValue([]);
// When // When
const result = await useCase.execute(query); const result = await useCase.execute(query);

View File

@@ -5,7 +5,7 @@
*/ */
import { beforeEach, describe, expect, it, vi } from 'vitest'; import { beforeEach, describe, expect, it, vi } from 'vitest';
import { CheckApiHealthUseCase, CheckApiHealthUseCasePorts } from './CheckApiHealthUseCase'; import { CheckApiHealthUseCase } from './CheckApiHealthUseCase';
import { HealthCheckQuery, HealthCheckResult } from '../ports/HealthCheckQuery'; import { HealthCheckQuery, HealthCheckResult } from '../ports/HealthCheckQuery';
import { HealthEventPublisher } from '../ports/HealthEventPublisher'; import { HealthEventPublisher } from '../ports/HealthEventPublisher';

View File

@@ -5,7 +5,7 @@
* This Use Case orchestrates the retrieval of connection status information. * This Use Case orchestrates the retrieval of connection status information.
*/ */
import { HealthCheckQuery, ConnectionHealth, ConnectionStatus } from '../ports/HealthCheckQuery'; import { HealthCheckQuery, ConnectionStatus } from '../ports/HealthCheckQuery';
export interface GetConnectionStatusUseCasePorts { export interface GetConnectionStatusUseCasePorts {
healthCheckAdapter: HealthCheckQuery; healthCheckAdapter: HealthCheckQuery;

View File

@@ -72,7 +72,7 @@ describe('GetUserRatingLedgerQueryHandler', () => {
hasMore: false, hasMore: false,
}); });
const filter: any = { const filter: unknown = {
dimensions: ['trust'], dimensions: ['trust'],
sourceTypes: ['vote'], sourceTypes: ['vote'],
from: '2026-01-01T00:00:00Z', from: '2026-01-01T00:00:00Z',

View File

@@ -6,8 +6,6 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { CastAdminVoteUseCase } from './CastAdminVoteUseCase'; import { CastAdminVoteUseCase } from './CastAdminVoteUseCase';
import { AdminVoteSessionRepository } from '../../domain/repositories/AdminVoteSessionRepository';
import { AdminVoteSession } from '../../domain/entities/AdminVoteSession';
// Mock repository // Mock repository
const createMockRepository = () => ({ const createMockRepository = () => ({
@@ -55,7 +53,7 @@ describe('CastAdminVoteUseCase', () => {
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
voterId: 'voter-123', voterId: 'voter-123',
positive: 'true' as any, positive: 'true' as unknown as boolean,
}); });
expect(result.success).toBe(false); expect(result.success).toBe(false);

View File

@@ -1,17 +1,17 @@
/** /**
* Application Use Case Tests: CloseAdminVoteSessionUseCase * Application Use Case Tests: CloseAdminVoteSessionUseCase
* *
* Tests for closing admin vote sessions and generating rating events * Tests for closing admin vote sessions and generating rating events
*/ */
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { CloseAdminVoteSessionUseCase } from './CloseAdminVoteSessionUseCase'; import { CloseAdminVoteSessionUseCase } from './CloseAdminVoteSessionUseCase';
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
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 { AdminVoteSession } from '../../domain/entities/AdminVoteSession'; import { AdminVoteSession, AdminVoteOutcome } from '../../domain/entities/AdminVoteSession';
import { RatingEventFactory } from '../../domain/services/RatingEventFactory';
import { RatingSnapshotCalculator } from '../../domain/services/RatingSnapshotCalculator';
// Mock repositories // Mock repositories
const createMockRepositories = () => ({ const createMockRepositories = () => ({
@@ -55,14 +55,14 @@ describe('CloseAdminVoteSessionUseCase', () => {
beforeEach(() => { beforeEach(() => {
mockRepositories = createMockRepositories(); mockRepositories = createMockRepositories();
useCase = new CloseAdminVoteSessionUseCase( useCase = new CloseAdminVoteSessionUseCase(
mockRepositories.adminVoteSessionRepository, mockRepositories.adminVoteSessionRepository as unknown as AdminVoteSessionRepository,
mockRepositories.ratingEventRepository, mockRepositories.ratingEventRepository as unknown as RatingEventRepository,
mockRepositories.userRatingRepository mockRepositories.userRatingRepository as unknown as UserRatingRepository
); );
vi.clearAllMocks(); vi.clearAllMocks();
// Default mock for RatingEventFactory.createFromVote to return an empty array // Default mock for RatingEventFactory.createFromVote to return an empty array
// to avoid "events is not iterable" error in tests that don't explicitly mock it // to avoid "events is not iterable" error in tests that don't explicitly mock it
(RatingEventFactory.createFromVote as any).mockReturnValue([]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([]);
}); });
describe('Input validation', () => { describe('Input validation', () => {
@@ -88,7 +88,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should accept valid input', async () => { it('should accept valid input', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -148,7 +157,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should find session by ID when provided', async () => { it('should find session by ID when provided', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -190,7 +208,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Admin ownership validation', () => { describe('Admin ownership validation', () => {
it('should reject when admin does not own the session', async () => { it('should reject when admin does not own the session', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'different-admin', adminId: 'different-admin',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -231,7 +258,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should accept when admin owns the session', async () => { it('should accept when admin owns the session', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -273,7 +309,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Session closure validation', () => { describe('Session closure validation', () => {
it('should reject when session is already closed', async () => { it('should reject when session is already closed', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -314,7 +359,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should accept when session is not closed', async () => { it('should accept when session is not closed', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -356,7 +410,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Voting window validation', () => { describe('Voting window validation', () => {
it('should reject when trying to close outside voting window', async () => { it('should reject when trying to close outside voting window', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -392,7 +455,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
constructor() { constructor() {
super('2026-02-02'); super('2026-02-02');
} }
} as any; } as unknown as typeof Date;
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -408,7 +471,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should accept when trying to close within voting window', async () => { it('should accept when trying to close within voting window', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -444,7 +516,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
constructor() { constructor() {
super('2026-01-15T12:00:00'); super('2026-01-15T12:00:00');
} }
} as any; } as unknown as typeof Date;
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -461,7 +533,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Session closure', () => { describe('Session closure', () => {
it('should call close method on session', async () => { it('should call close method on session', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -501,7 +582,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should save closed session', async () => { it('should save closed session', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -541,7 +631,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should return outcome in success response', async () => { it('should return outcome in success response', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -589,7 +688,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Rating event creation', () => { describe('Rating event creation', () => {
it('should create rating events when outcome is positive', async () => { it('should create rating events when outcome is positive', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -620,7 +728,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession);
const mockEvent = { id: 'event-123' }; const mockEvent = { id: 'event-123' };
(RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([mockEvent]);
await useCase.execute({ await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -639,7 +747,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should create rating events when outcome is negative', async () => { it('should create rating events when outcome is negative', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -670,7 +787,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession);
const mockEvent = { id: 'event-123' }; const mockEvent = { id: 'event-123' };
(RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([mockEvent]);
await useCase.execute({ await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -689,7 +806,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should not create rating events when outcome is tie', async () => { it('should not create rating events when outcome is tie', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -730,7 +856,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should save created rating events', async () => { it('should save created rating events', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -762,7 +897,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
const mockEvent1 = { id: 'event-123' }; const mockEvent1 = { id: 'event-123' };
const mockEvent2 = { id: 'event-124' }; const mockEvent2 = { id: 'event-124' };
(RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent1, mockEvent2]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([mockEvent1, mockEvent2]);
await useCase.execute({ await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -776,7 +911,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should return eventsCreated count', async () => { it('should return eventsCreated count', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -808,7 +952,7 @@ describe('CloseAdminVoteSessionUseCase', () => {
const mockEvent1 = { id: 'event-123' }; const mockEvent1 = { id: 'event-123' };
const mockEvent2 = { id: 'event-124' }; const mockEvent2 = { id: 'event-124' };
(RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent1, mockEvent2]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([mockEvent1, mockEvent2]);
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -822,7 +966,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Snapshot recalculation', () => { describe('Snapshot recalculation', () => {
it('should recalculate snapshot when events are created', async () => { it('should recalculate snapshot when events are created', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -853,13 +1006,13 @@ describe('CloseAdminVoteSessionUseCase', () => {
mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession); mockRepositories.adminVoteSessionRepository.findById.mockResolvedValue(mockSession);
const mockEvent = { id: 'event-123' }; const mockEvent = { id: 'event-123' };
(RatingEventFactory.createFromVote as any).mockReturnValue([mockEvent]); (RatingEventFactory.createFromVote as unknown as Mock).mockReturnValue([mockEvent]);
const mockAllEvents = [{ id: 'event-1' }, { id: 'event-2' }]; const mockAllEvents = [{ id: 'event-1' }, { id: 'event-2' }];
mockRepositories.ratingEventRepository.getAllByUserId.mockResolvedValue(mockAllEvents); mockRepositories.ratingEventRepository.getAllByUserId.mockResolvedValue(mockAllEvents);
const mockSnapshot = { userId: 'admin-123', overallReputation: 75 }; const mockSnapshot = { userId: 'admin-123', overallReputation: 75 };
(RatingSnapshotCalculator.calculate as any).mockReturnValue(mockSnapshot); (RatingSnapshotCalculator.calculate as unknown as Mock).mockReturnValue(mockSnapshot);
await useCase.execute({ await useCase.execute({
voteSessionId: 'session-123', voteSessionId: 'session-123',
@@ -873,7 +1026,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should not recalculate snapshot when no events are created (tie)', async () => { it('should not recalculate snapshot when no events are created (tie)', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -941,7 +1103,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
it('should handle save errors gracefully', async () => { it('should handle save errors gracefully', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),
@@ -985,7 +1156,16 @@ describe('CloseAdminVoteSessionUseCase', () => {
describe('Return values', () => { describe('Return values', () => {
it('should return voteSessionId in success response', async () => { it('should return voteSessionId in success response', async () => {
const futureDate = new Date('2026-02-01'); const futureDate = new Date('2026-02-01');
const mockSession: any = { const mockSession: {
id: string;
adminId: string;
startDate: Date;
endDate: Date;
_closed: boolean;
_outcome?: AdminVoteOutcome;
close: Mock;
closed: boolean;
} = {
id: 'session-123', id: 'session-123',
adminId: 'admin-123', adminId: 'admin-123',
startDate: new Date('2026-01-01'), startDate: new Date('2026-01-01'),

View File

@@ -171,7 +171,7 @@ describe('OpenAdminVoteSessionUseCase', () => {
describe('Business rules', () => { describe('Business rules', () => {
it('should reject when session ID already exists', async () => { it('should reject when session ID already exists', async () => {
mockRepository.findById.mockResolvedValue({ id: 'session-1' } as any); mockRepository.findById.mockResolvedValue({ id: 'session-1' } as unknown as AdminVoteSession);
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-1', voteSessionId: 'session-1',
@@ -193,7 +193,7 @@ describe('OpenAdminVoteSessionUseCase', () => {
startDate: new Date('2026-01-05'), startDate: new Date('2026-01-05'),
endDate: new Date('2026-01-10'), endDate: new Date('2026-01-10'),
} }
] as any); ] as unknown as AdminVoteSession[]);
const result = await useCase.execute({ const result = await useCase.execute({
voteSessionId: 'session-1', voteSessionId: 'session-1',

View File

@@ -216,7 +216,7 @@ describe('Company', () => {
id: 'comp-123', id: 'comp-123',
name: 'Acme Racing Team', name: 'Acme Racing Team',
ownerUserId: 'user-123', ownerUserId: 'user-123',
contactEmail: null as any, contactEmail: null,
createdAt, createdAt,
}); });

View File

@@ -133,7 +133,7 @@ describe('PasswordHashingService', () => {
it('should reject verification with null hash', async () => { it('should reject verification with null hash', async () => {
// bcrypt throws an error when hash is null, which is expected behavior // bcrypt throws an error when hash is null, which is expected behavior
await expect(service.verify('password', null as any)).rejects.toThrow(); await expect(service.verify('password', null as unknown as string)).rejects.toThrow();
}); });
it('should reject verification with empty hash', async () => { it('should reject verification with empty hash', async () => {

View File

@@ -216,17 +216,17 @@ describe('EmailAddress', () => {
describe('Edge cases', () => { describe('Edge cases', () => {
it('should handle null input gracefully', () => { it('should handle null input gracefully', () => {
const result = validateEmail(null as any); const result = validateEmail(null as unknown as string);
expect(result.success).toBe(false); expect(result.success).toBe(false);
}); });
it('should handle undefined input gracefully', () => { it('should handle undefined input gracefully', () => {
const result = validateEmail(undefined as any); const result = validateEmail(undefined as unknown as string);
expect(result.success).toBe(false); expect(result.success).toBe(false);
}); });
it('should handle non-string input gracefully', () => { it('should handle non-string input gracefully', () => {
const result = validateEmail(123 as any); const result = validateEmail(123 as unknown as string);
expect(result.success).toBe(false); expect(result.success).toBe(false);
}); });
}); });
@@ -305,13 +305,13 @@ describe('EmailAddress', () => {
it('should handle null input', () => { it('should handle null input', () => {
// The current implementation throws an error when given null // The current implementation throws an error when given null
// This is expected behavior - the function expects a string // This is expected behavior - the function expects a string
expect(() => isDisposableEmail(null as any)).toThrow(); expect(() => isDisposableEmail(null as unknown as string)).toThrow();
}); });
it('should handle undefined input', () => { it('should handle undefined input', () => {
// The current implementation throws an error when given undefined // The current implementation throws an error when given undefined
// This is expected behavior - the function expects a string // This is expected behavior - the function expects a string
expect(() => isDisposableEmail(undefined as any)).toThrow(); expect(() => isDisposableEmail(undefined as unknown as string)).toThrow();
}); });
}); });
}); });

View File

@@ -1,14 +1,16 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetDriverRankingsUseCase, GetDriverRankingsUseCasePorts } from './GetDriverRankingsUseCase'; import { GetDriverRankingsUseCase, GetDriverRankingsUseCasePorts } from './GetDriverRankingsUseCase';
import { ValidationError } from '../../../shared/errors/ValidationError'; import { ValidationError } from '../../../shared/errors/ValidationError';
import { LeaderboardsRepository, LeaderboardDriverData } from '../ports/LeaderboardsRepository';
import { LeaderboardsEventPublisher } from '../ports/LeaderboardsEventPublisher';
describe('GetDriverRankingsUseCase', () => { describe('GetDriverRankingsUseCase', () => {
let mockLeaderboardsRepository: any; let mockLeaderboardsRepository: LeaderboardsRepository;
let mockEventPublisher: any; let mockEventPublisher: LeaderboardsEventPublisher;
let ports: GetDriverRankingsUseCasePorts; let ports: GetDriverRankingsUseCasePorts;
let useCase: GetDriverRankingsUseCase; let useCase: GetDriverRankingsUseCase;
const mockDrivers = [ const mockDrivers: LeaderboardDriverData[] = [
{ id: '1', name: 'Alice', rating: 2000, raceCount: 10, teamId: 't1', teamName: 'Team A' }, { id: '1', name: 'Alice', rating: 2000, raceCount: 10, teamId: 't1', teamName: 'Team A' },
{ id: '2', name: 'Bob', rating: 1500, raceCount: 5, teamId: 't2', teamName: 'Team B' }, { id: '2', name: 'Bob', rating: 1500, raceCount: 5, teamId: 't2', teamName: 'Team B' },
{ id: '3', name: 'Charlie', rating: 1800, raceCount: 8 }, { id: '3', name: 'Charlie', rating: 1800, raceCount: 8 },
@@ -17,10 +19,14 @@ describe('GetDriverRankingsUseCase', () => {
beforeEach(() => { beforeEach(() => {
mockLeaderboardsRepository = { mockLeaderboardsRepository = {
findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]), findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]),
findAllTeams: vi.fn(),
findDriversByTeamId: vi.fn(),
}; };
mockEventPublisher = { mockEventPublisher = {
publishDriverRankingsAccessed: vi.fn().mockResolvedValue(undefined), publishDriverRankingsAccessed: vi.fn().mockResolvedValue(undefined),
publishLeaderboardsError: vi.fn().mockResolvedValue(undefined), publishLeaderboardsError: vi.fn().mockResolvedValue(undefined),
publishGlobalLeaderboardsAccessed: vi.fn(),
publishTeamRankingsAccessed: vi.fn(),
}; };
ports = { ports = {
leaderboardsRepository: mockLeaderboardsRepository, leaderboardsRepository: mockLeaderboardsRepository,
@@ -92,6 +98,6 @@ describe('GetDriverRankingsUseCase', () => {
}); });
it('should throw ValidationError for invalid sortBy', async () => { it('should throw ValidationError for invalid sortBy', async () => {
await expect(useCase.execute({ sortBy: 'invalid' as any })).rejects.toThrow(ValidationError); await expect(useCase.execute({ sortBy: 'invalid' as unknown as 'name' })).rejects.toThrow(ValidationError);
}); });
}); });

View File

@@ -11,7 +11,6 @@ import {
DriverRankingsQuery, DriverRankingsQuery,
DriverRankingsResult, DriverRankingsResult,
DriverRankingEntry, DriverRankingEntry,
PaginationMetadata,
} from '../ports/DriverRankingsQuery'; } from '../ports/DriverRankingsQuery';
import { ValidationError } from '../../../shared/errors/ValidationError'; import { ValidationError } from '../../../shared/errors/ValidationError';

View File

@@ -1,30 +1,35 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { GetGlobalLeaderboardsUseCase, GetGlobalLeaderboardsUseCasePorts } from './GetGlobalLeaderboardsUseCase'; import { GetGlobalLeaderboardsUseCase, GetGlobalLeaderboardsUseCasePorts } from './GetGlobalLeaderboardsUseCase';
import { LeaderboardsRepository, LeaderboardDriverData, LeaderboardTeamData } from '../ports/LeaderboardsRepository';
import { LeaderboardsEventPublisher } from '../ports/LeaderboardsEventPublisher';
describe('GetGlobalLeaderboardsUseCase', () => { describe('GetGlobalLeaderboardsUseCase', () => {
let mockLeaderboardsRepository: any; let mockLeaderboardsRepository: LeaderboardsRepository;
let mockEventPublisher: any; let mockEventPublisher: LeaderboardsEventPublisher;
let ports: GetGlobalLeaderboardsUseCasePorts; let ports: GetGlobalLeaderboardsUseCasePorts;
let useCase: GetGlobalLeaderboardsUseCase; let useCase: GetGlobalLeaderboardsUseCase;
const mockDrivers = [ const mockDrivers: LeaderboardDriverData[] = [
{ id: 'd1', name: 'Alice', rating: 2000, raceCount: 10 }, { id: 'd1', name: 'Alice', rating: 2000, raceCount: 10 },
{ id: 'd2', name: 'Bob', rating: 1500, raceCount: 5 }, { id: 'd2', name: 'Bob', rating: 1500, raceCount: 5 },
]; ];
const mockTeams = [ const mockTeams: LeaderboardTeamData[] = [
{ id: 't1', name: 'Team A', rating: 2500, memberCount: 5, raceCount: 20 }, { id: 't1', name: 'Team A', rating: 2500, memberCount: 5, raceCount: 20 },
{ id: 't2', name: 'Team B', rating: 2200, memberCount: 3, raceCount: 15 }, { id: 't2', name: 'Team B', rating: 2200, memberCount: 3, raceCount: 15 },
]; ];
beforeEach(() => { beforeEach(() => {
mockLeaderboardsRepository = { mockLeaderboardsRepository = {
findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]), findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]) as unknown as Mock,
findAllTeams: vi.fn().mockResolvedValue([...mockTeams]), findAllTeams: vi.fn().mockResolvedValue([...mockTeams]) as unknown as Mock,
findDriversByTeamId: vi.fn() as unknown as Mock,
}; };
mockEventPublisher = { mockEventPublisher = {
publishGlobalLeaderboardsAccessed: vi.fn().mockResolvedValue(undefined), publishGlobalLeaderboardsAccessed: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
publishLeaderboardsError: vi.fn().mockResolvedValue(undefined), publishLeaderboardsError: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
publishDriverRankingsAccessed: vi.fn() as unknown as Mock,
publishTeamRankingsAccessed: vi.fn() as unknown as Mock,
}; };
ports = { ports = {
leaderboardsRepository: mockLeaderboardsRepository, leaderboardsRepository: mockLeaderboardsRepository,
@@ -57,7 +62,7 @@ describe('GetGlobalLeaderboardsUseCase', () => {
}); });
it('should handle errors and publish error event', async () => { it('should handle errors and publish error event', async () => {
mockLeaderboardsRepository.findAllDrivers.mockRejectedValue(new Error('Repo error')); (mockLeaderboardsRepository.findAllDrivers as Mock).mockRejectedValue(new Error('Repo error'));
await expect(useCase.execute()).rejects.toThrow('Repo error'); await expect(useCase.execute()).rejects.toThrow('Repo error');
expect(mockEventPublisher.publishLeaderboardsError).toHaveBeenCalled(); expect(mockEventPublisher.publishLeaderboardsError).toHaveBeenCalled();

View File

@@ -1,19 +1,21 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { GetTeamRankingsUseCase, GetTeamRankingsUseCasePorts } from './GetTeamRankingsUseCase'; import { GetTeamRankingsUseCase, GetTeamRankingsUseCasePorts } from './GetTeamRankingsUseCase';
import { ValidationError } from '../../../shared/errors/ValidationError'; import { ValidationError } from '../../../shared/errors/ValidationError';
import { LeaderboardsRepository, LeaderboardTeamData, LeaderboardDriverData } from '../ports/LeaderboardsRepository';
import { LeaderboardsEventPublisher } from '../ports/LeaderboardsEventPublisher';
describe('GetTeamRankingsUseCase', () => { describe('GetTeamRankingsUseCase', () => {
let mockLeaderboardsRepository: any; let mockLeaderboardsRepository: LeaderboardsRepository;
let mockEventPublisher: any; let mockEventPublisher: LeaderboardsEventPublisher;
let ports: GetTeamRankingsUseCasePorts; let ports: GetTeamRankingsUseCasePorts;
let useCase: GetTeamRankingsUseCase; let useCase: GetTeamRankingsUseCase;
const mockTeams = [ const mockTeams: LeaderboardTeamData[] = [
{ id: 't1', name: 'Team A', rating: 2500, memberCount: 0, raceCount: 20 }, { id: 't1', name: 'Team A', rating: 2500, memberCount: 0, raceCount: 20 },
{ id: 't2', name: 'Team B', rating: 2200, memberCount: 0, raceCount: 15 }, { id: 't2', name: 'Team B', rating: 2200, memberCount: 0, raceCount: 15 },
]; ];
const mockDrivers = [ const mockDrivers: LeaderboardDriverData[] = [
{ id: 'd1', name: 'Alice', rating: 2000, raceCount: 10, teamId: 't1', teamName: 'Team A' }, { id: 'd1', name: 'Alice', rating: 2000, raceCount: 10, teamId: 't1', teamName: 'Team A' },
{ id: 'd2', name: 'Bob', rating: 1500, raceCount: 5, teamId: 't1', teamName: 'Team A' }, { id: 'd2', name: 'Bob', rating: 1500, raceCount: 5, teamId: 't1', teamName: 'Team A' },
{ id: 'd3', name: 'Charlie', rating: 1800, raceCount: 8, teamId: 't2', teamName: 'Team B' }, { id: 'd3', name: 'Charlie', rating: 1800, raceCount: 8, teamId: 't2', teamName: 'Team B' },
@@ -22,12 +24,15 @@ describe('GetTeamRankingsUseCase', () => {
beforeEach(() => { beforeEach(() => {
mockLeaderboardsRepository = { mockLeaderboardsRepository = {
findAllTeams: vi.fn().mockResolvedValue([...mockTeams]), findAllTeams: vi.fn().mockResolvedValue([...mockTeams]) as unknown as Mock,
findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]), findAllDrivers: vi.fn().mockResolvedValue([...mockDrivers]) as unknown as Mock,
findDriversByTeamId: vi.fn() as unknown as Mock,
}; };
mockEventPublisher = { mockEventPublisher = {
publishTeamRankingsAccessed: vi.fn().mockResolvedValue(undefined), publishTeamRankingsAccessed: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
publishLeaderboardsError: vi.fn().mockResolvedValue(undefined), publishLeaderboardsError: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
publishGlobalLeaderboardsAccessed: vi.fn() as unknown as Mock,
publishDriverRankingsAccessed: vi.fn() as unknown as Mock,
}; };
ports = { ports = {
leaderboardsRepository: mockLeaderboardsRepository, leaderboardsRepository: mockLeaderboardsRepository,

View File

@@ -11,7 +11,6 @@ import {
TeamRankingsQuery, TeamRankingsQuery,
TeamRankingsResult, TeamRankingsResult,
TeamRankingEntry, TeamRankingEntry,
PaginationMetadata,
} from '../ports/TeamRankingsQuery'; } from '../ports/TeamRankingsQuery';
import { ValidationError } from '../../../shared/errors/ValidationError'; import { ValidationError } from '../../../shared/errors/ValidationError';
@@ -20,6 +19,14 @@ export interface GetTeamRankingsUseCasePorts {
eventPublisher: LeaderboardsEventPublisher; eventPublisher: LeaderboardsEventPublisher;
} }
interface DiscoveredTeam {
id: string;
name: string;
rating: number;
memberCount: number;
raceCount: number;
}
export class GetTeamRankingsUseCase { export class GetTeamRankingsUseCase {
constructor(private readonly ports: GetTeamRankingsUseCasePorts) {} constructor(private readonly ports: GetTeamRankingsUseCasePorts) {}
@@ -57,7 +64,7 @@ export class GetTeamRankingsUseCase {
}); });
// Discover teams that only exist in the drivers repository // Discover teams that only exist in the drivers repository
const discoveredTeams: any[] = []; const discoveredTeams: DiscoveredTeam[] = [];
driverCounts.forEach((count, teamId) => { driverCounts.forEach((count, teamId) => {
if (!allTeams.some(t => t.id === teamId)) { if (!allTeams.some(t => t.id === teamId)) {
const driverWithTeam = allDrivers.find(d => d.teamId === teamId); const driverWithTeam = allDrivers.find(d => d.teamId === teamId);

View File

@@ -1,3 +1,18 @@
export interface ScoringSystem {
// Define scoring system properties based on your domain
// This is a placeholder - adjust based on actual scoring system structure
pointsPerPosition?: Record<number, number>;
bonusPoints?: {
polePosition?: number;
fastestLap?: number;
cleanRace?: number;
};
penalties?: {
timePenalty?: number;
pointsDeduction?: number;
};
}
export interface LeagueCreateCommand { export interface LeagueCreateCommand {
name: string; name: string;
description?: string; description?: string;
@@ -16,7 +31,7 @@ export interface LeagueCreateCommand {
tracks?: string[]; tracks?: string[];
// Scoring // Scoring
scoringSystem?: any; scoringSystem?: ScoringSystem;
bonusPointsEnabled: boolean; bonusPointsEnabled: boolean;
penaltiesEnabled: boolean; penaltiesEnabled: boolean;

View File

@@ -1,3 +1,5 @@
import { ScoringSystem } from './LeagueCreateCommand';
export interface LeagueCreatedEvent { export interface LeagueCreatedEvent {
type: 'LeagueCreatedEvent'; type: 'LeagueCreatedEvent';
leagueId: string; leagueId: string;
@@ -5,10 +7,33 @@ export interface LeagueCreatedEvent {
timestamp: Date; timestamp: Date;
} }
export interface LeagueUpdates {
name?: string;
description?: string;
visibility?: 'public' | 'private';
maxDrivers?: number;
approvalRequired?: boolean;
lateJoinAllowed?: boolean;
raceFrequency?: string;
raceDay?: string;
raceTime?: string;
tracks?: string[];
scoringSystem?: ScoringSystem;
bonusPointsEnabled?: boolean;
penaltiesEnabled?: boolean;
protestsEnabled?: boolean;
appealsEnabled?: boolean;
stewardTeam?: string[];
gameType?: string;
skillLevel?: string;
category?: string;
tags?: string[];
}
export interface LeagueUpdatedEvent { export interface LeagueUpdatedEvent {
type: 'LeagueUpdatedEvent'; type: 'LeagueUpdatedEvent';
leagueId: string; leagueId: string;
updates: Partial<any>; updates: Partial<LeagueUpdates>;
timestamp: Date; timestamp: Date;
} }

View File

@@ -1,3 +1,5 @@
import { ScoringSystem } from './LeagueCreateCommand';
export interface LeagueData { export interface LeagueData {
id: string; id: string;
name: string; name: string;
@@ -20,7 +22,7 @@ export interface LeagueData {
tracks: string[] | null; tracks: string[] | null;
// Scoring // Scoring
scoringSystem: any | null; scoringSystem: ScoringSystem | null;
bonusPointsEnabled: boolean; bonusPointsEnabled: boolean;
penaltiesEnabled: boolean; penaltiesEnabled: boolean;

View File

@@ -1,6 +1,6 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { EventPublisher } from '../ports/EventPublisher'; import { EventPublisher } from '../../../shared/ports/EventPublisher';
import { ApproveMembershipRequestCommand } from '../ports/ApproveMembershipRequestCommand'; import { ApproveMembershipRequestCommand } from '../ports/ApproveMembershipRequestCommand';
export class ApproveMembershipRequestUseCase { export class ApproveMembershipRequestUseCase {

View File

@@ -1,28 +1,63 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { CreateLeagueUseCase } from './CreateLeagueUseCase'; import { CreateLeagueUseCase } from './CreateLeagueUseCase';
import { LeagueCreateCommand } from '../ports/LeagueCreateCommand'; import { LeagueCreateCommand } from '../ports/LeagueCreateCommand';
import { LeagueRepository } from '../ports/LeagueRepository';
import { LeagueEventPublisher } from '../ports/LeagueEventPublisher';
describe('CreateLeagueUseCase', () => { describe('CreateLeagueUseCase', () => {
let mockLeagueRepository: any; let mockLeagueRepository: LeagueRepository;
let mockEventPublisher: any; let mockEventPublisher: LeagueEventPublisher;
let useCase: CreateLeagueUseCase; let useCase: CreateLeagueUseCase;
beforeEach(() => { beforeEach(() => {
mockLeagueRepository = { mockLeagueRepository = {
create: vi.fn().mockImplementation((data) => Promise.resolve(data)), create: vi.fn().mockImplementation((data) => Promise.resolve(data)) as unknown as Mock,
updateStats: vi.fn().mockResolvedValue(undefined), findById: vi.fn() as unknown as Mock,
updateFinancials: vi.fn().mockResolvedValue(undefined), findByName: vi.fn() as unknown as Mock,
updateStewardingMetrics: vi.fn().mockResolvedValue(undefined), findByOwner: vi.fn() as unknown as Mock,
updatePerformanceMetrics: vi.fn().mockResolvedValue(undefined), search: vi.fn() as unknown as Mock,
updateRatingMetrics: vi.fn().mockResolvedValue(undefined), update: vi.fn() as unknown as Mock,
updateTrendMetrics: vi.fn().mockResolvedValue(undefined), delete: vi.fn() as unknown as Mock,
updateSuccessRateMetrics: vi.fn().mockResolvedValue(undefined), getStats: vi.fn() as unknown as Mock,
updateResolutionTimeMetrics: vi.fn().mockResolvedValue(undefined), updateStats: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
updateComplexSuccessRateMetrics: vi.fn().mockResolvedValue(undefined), getFinancials: vi.fn() as unknown as Mock,
updateComplexResolutionTimeMetrics: vi.fn().mockResolvedValue(undefined), updateFinancials: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getStewardingMetrics: vi.fn() as unknown as Mock,
updateStewardingMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getPerformanceMetrics: vi.fn() as unknown as Mock,
updatePerformanceMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getRatingMetrics: vi.fn() as unknown as Mock,
updateRatingMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getTrendMetrics: vi.fn() as unknown as Mock,
updateTrendMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getSuccessRateMetrics: vi.fn() as unknown as Mock,
updateSuccessRateMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateResolutionTimeMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getComplexSuccessRateMetrics: vi.fn() as unknown as Mock,
updateComplexSuccessRateMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getComplexResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateComplexResolutionTimeMetrics: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
getLeagueMembers: vi.fn() as unknown as Mock,
getPendingRequests: vi.fn() as unknown as Mock,
addLeagueMembers: vi.fn() as unknown as Mock,
updateLeagueMember: vi.fn() as unknown as Mock,
removeLeagueMember: vi.fn() as unknown as Mock,
addPendingRequests: vi.fn() as unknown as Mock,
removePendingRequest: vi.fn() as unknown as Mock,
}; };
mockEventPublisher = { mockEventPublisher = {
emitLeagueCreated: vi.fn().mockResolvedValue(undefined), emitLeagueCreated: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
emitLeagueUpdated: vi.fn() as unknown as Mock,
emitLeagueDeleted: vi.fn() as unknown as Mock,
emitLeagueAccessed: vi.fn() as unknown as Mock,
emitLeagueRosterAccessed: vi.fn() as unknown as Mock,
getLeagueCreatedEventCount: vi.fn().mockReturnValue(0) as unknown as Mock,
getLeagueUpdatedEventCount: vi.fn().mockReturnValue(0) as unknown as Mock,
getLeagueDeletedEventCount: vi.fn().mockReturnValue(0) as unknown as Mock,
getLeagueAccessedEventCount: vi.fn().mockReturnValue(0) as unknown as Mock,
getLeagueRosterAccessedEventCount: vi.fn().mockReturnValue(0) as unknown as Mock,
clear: vi.fn() as unknown as Mock,
}; };
useCase = new CreateLeagueUseCase(mockLeagueRepository, mockEventPublisher); useCase = new CreateLeagueUseCase(mockLeagueRepository, mockEventPublisher);
}); });
@@ -51,12 +86,12 @@ describe('CreateLeagueUseCase', () => {
}); });
it('should throw error if name is missing', async () => { it('should throw error if name is missing', async () => {
const command: any = { ownerId: 'owner-1' }; const command = { ownerId: 'owner-1' } as unknown as LeagueCreateCommand;
await expect(useCase.execute(command)).rejects.toThrow('League name is required'); await expect(useCase.execute(command)).rejects.toThrow('League name is required');
}); });
it('should throw error if ownerId is missing', async () => { it('should throw error if ownerId is missing', async () => {
const command: any = { name: 'League' }; const command = { name: 'League' } as unknown as LeagueCreateCommand;
await expect(useCase.execute(command)).rejects.toThrow('Owner ID is required'); await expect(useCase.execute(command)).rejects.toThrow('Owner ID is required');
}); });
}); });

View File

@@ -1,19 +1,94 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
import { DemoteAdminUseCase } from './DemoteAdminUseCase'; import { DemoteAdminUseCase } from './DemoteAdminUseCase';
import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../../racing/domain/repositories/DriverRepository';
import { LeagueEventPublisher } from '../ports/LeagueEventPublisher';
describe('DemoteAdminUseCase', () => { describe('DemoteAdminUseCase', () => {
let mockLeagueRepository: any; let mockLeagueRepository: LeagueRepository;
let mockDriverRepository: any; let mockDriverRepository: DriverRepository;
let mockEventPublisher: any; let mockEventPublisher: LeagueEventPublisher;
let useCase: DemoteAdminUseCase; let useCase: DemoteAdminUseCase;
beforeEach(() => { beforeEach(() => {
mockLeagueRepository = { mockLeagueRepository = {
updateLeagueMember: vi.fn().mockResolvedValue(undefined), updateLeagueMember: vi.fn().mockResolvedValue(undefined) as unknown as Mock,
create: vi.fn() as unknown as Mock,
findById: vi.fn() as unknown as Mock,
findByName: vi.fn() as unknown as Mock,
findByOwner: vi.fn() as unknown as Mock,
search: vi.fn() as unknown as Mock,
update: vi.fn() as unknown as Mock,
delete: vi.fn() as unknown as Mock,
getStats: vi.fn() as unknown as Mock,
updateStats: vi.fn() as unknown as Mock,
getFinancials: vi.fn() as unknown as Mock,
updateFinancials: vi.fn() as unknown as Mock,
getStewardingMetrics: vi.fn() as unknown as Mock,
updateStewardingMetrics: vi.fn() as unknown as Mock,
getPerformanceMetrics: vi.fn() as unknown as Mock,
updatePerformanceMetrics: vi.fn() as unknown as Mock,
getRatingMetrics: vi.fn() as unknown as Mock,
updateRatingMetrics: vi.fn() as unknown as Mock,
getTrendMetrics: vi.fn() as unknown as Mock,
updateTrendMetrics: vi.fn() as unknown as Mock,
getSuccessRateMetrics: vi.fn() as unknown as Mock,
updateSuccessRateMetrics: vi.fn() as unknown as Mock,
getResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateResolutionTimeMetrics: vi.fn() as unknown as Mock,
getComplexSuccessRateMetrics: vi.fn() as unknown as Mock,
updateComplexSuccessRateMetrics: vi.fn() as unknown as Mock,
getComplexResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateComplexResolutionTimeMetrics: vi.fn() as unknown as Mock,
getLeagueMembers: vi.fn() as unknown as Mock,
getPendingRequests: vi.fn() as unknown as Mock,
addLeagueMembers: vi.fn() as unknown as Mock,
removeLeagueMember: vi.fn() as unknown as Mock,
addPendingRequests: vi.fn() as unknown as Mock,
removePendingRequest: vi.fn() as unknown as Mock,
}; };
mockDriverRepository = {}; mockDriverRepository = {
mockEventPublisher = {}; findById: vi.fn() as unknown as Mock,
useCase = new DemoteAdminUseCase(mockLeagueRepository, mockDriverRepository, mockEventPublisher as any); findByName: vi.fn() as unknown as Mock,
findByEmail: vi.fn() as unknown as Mock,
search: vi.fn() as unknown as Mock,
update: vi.fn() as unknown as Mock,
delete: vi.fn() as unknown as Mock,
getStats: vi.fn() as unknown as Mock,
updateStats: vi.fn() as unknown as Mock,
getPerformanceMetrics: vi.fn() as unknown as Mock,
updatePerformanceMetrics: vi.fn() as unknown as Mock,
getRatingMetrics: vi.fn() as unknown as Mock,
updateRatingMetrics: vi.fn() as unknown as Mock,
getTrendMetrics: vi.fn() as unknown as Mock,
updateTrendMetrics: vi.fn() as unknown as Mock,
getSuccessRateMetrics: vi.fn() as unknown as Mock,
updateSuccessRateMetrics: vi.fn() as unknown as Mock,
getResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateResolutionTimeMetrics: vi.fn() as unknown as Mock,
getComplexSuccessRateMetrics: vi.fn() as unknown as Mock,
updateComplexSuccessRateMetrics: vi.fn() as unknown as Mock,
getComplexResolutionTimeMetrics: vi.fn() as unknown as Mock,
updateComplexResolutionTimeMetrics: vi.fn() as unknown as Mock,
getDriverMemberships: vi.fn() as unknown as Mock,
addDriverMembership: vi.fn() as unknown as Mock,
updateDriverMembership: vi.fn() as unknown as Mock,
removeDriverMembership: vi.fn() as unknown as Mock,
};
mockEventPublisher = {
emitLeagueCreated: vi.fn() as unknown as Mock,
emitLeagueUpdated: vi.fn() as unknown as Mock,
emitLeagueDeleted: vi.fn() as unknown as Mock,
emitLeagueAccessed: vi.fn() as unknown as Mock,
emitLeagueRosterAccessed: vi.fn() as unknown as Mock,
getLeagueCreatedEventCount: vi.fn() as unknown as Mock,
getLeagueUpdatedEventCount: vi.fn() as unknown as Mock,
getLeagueDeletedEventCount: vi.fn() as unknown as Mock,
getLeagueAccessedEventCount: vi.fn() as unknown as Mock,
getLeagueRosterAccessedEventCount: vi.fn() as unknown as Mock,
clear: vi.fn() as unknown as Mock,
};
useCase = new DemoteAdminUseCase(mockLeagueRepository, mockDriverRepository, mockEventPublisher);
}); });
it('should update member role to member', async () => { it('should update member role to member', async () => {

View File

@@ -1,5 +1,5 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { LeagueEventPublisher } from '../ports/LeagueEventPublisher'; import { LeagueEventPublisher } from '../ports/LeagueEventPublisher';
import { DemoteAdminCommand } from '../ports/DemoteAdminCommand'; import { DemoteAdminCommand } from '../ports/DemoteAdminCommand';

View File

@@ -1,8 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { JoinLeagueUseCase } from './JoinLeagueUseCase'; import { JoinLeagueUseCase } from './JoinLeagueUseCase';
import type { LeagueRepository } from '../ports/LeagueRepository';
import type { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import type { EventPublisher } from '../../../shared/ports/EventPublisher';
import type { JoinLeagueCommand } from '../ports/JoinLeagueCommand'; import type { JoinLeagueCommand } from '../ports/JoinLeagueCommand';
const mockLeagueRepository = { const mockLeagueRepository = {

View File

@@ -1,6 +1,6 @@
import { LeagueRepository, LeagueData } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { EventPublisher } from '../ports/EventPublisher'; import { EventPublisher } from '../../../shared/ports/EventPublisher';
import { JoinLeagueCommand } from '../ports/JoinLeagueCommand'; import { JoinLeagueCommand } from '../ports/JoinLeagueCommand';
export class JoinLeagueUseCase { export class JoinLeagueUseCase {
@@ -16,7 +16,7 @@ export class JoinLeagueUseCase {
throw new Error('League not found'); throw new Error('League not found');
} }
const driver = await this.driverRepository.findDriverById(command.driverId); const driver = await this.driverRepository.findById(command.driverId);
if (!driver) { if (!driver) {
throw new Error('Driver not found'); throw new Error('Driver not found');
} }
@@ -26,7 +26,7 @@ export class JoinLeagueUseCase {
{ {
id: `request-${Date.now()}`, id: `request-${Date.now()}`,
driverId: command.driverId, driverId: command.driverId,
name: driver.name, name: driver.name.toString(),
requestDate: new Date(), requestDate: new Date(),
}, },
]); ]);
@@ -34,7 +34,7 @@ export class JoinLeagueUseCase {
await this.leagueRepository.addLeagueMembers(command.leagueId, [ await this.leagueRepository.addLeagueMembers(command.leagueId, [
{ {
driverId: command.driverId, driverId: command.driverId,
name: driver.name, name: driver.name.toString(),
role: 'member', role: 'member',
joinDate: new Date(), joinDate: new Date(),
}, },

View File

@@ -1,6 +1,6 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { EventPublisher } from '../ports/EventPublisher'; import { EventPublisher } from '../../../shared/ports/EventPublisher';
import { LeaveLeagueCommand } from '../ports/LeaveLeagueCommand'; import { LeaveLeagueCommand } from '../ports/LeaveLeagueCommand';
export class LeaveLeagueUseCase { export class LeaveLeagueUseCase {

View File

@@ -1,6 +1,6 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { EventPublisher } from '../ports/EventPublisher'; import { EventPublisher } from '../../../shared/ports/EventPublisher';
import { PromoteMemberCommand } from '../ports/PromoteMemberCommand'; import { PromoteMemberCommand } from '../ports/PromoteMemberCommand';
export class PromoteMemberUseCase { export class PromoteMemberUseCase {

View File

@@ -1,5 +1,5 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { LeagueEventPublisher } from '../ports/LeagueEventPublisher'; import { LeagueEventPublisher } from '../ports/LeagueEventPublisher';
import { RejectMembershipRequestCommand } from '../ports/RejectMembershipRequestCommand'; import { RejectMembershipRequestCommand } from '../ports/RejectMembershipRequestCommand';

View File

@@ -1,6 +1,6 @@
import { LeagueRepository } from '../ports/LeagueRepository'; import { LeagueRepository } from '../ports/LeagueRepository';
import { DriverRepository } from '../ports/DriverRepository'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { EventPublisher } from '../ports/EventPublisher'; import { EventPublisher } from '../../../shared/ports/EventPublisher';
import { RemoveMemberCommand } from '../ports/RemoveMemberCommand'; import { RemoveMemberCommand } from '../ports/RemoveMemberCommand';
export class RemoveMemberUseCase { export class RemoveMemberUseCase {

View File

@@ -1,4 +1,3 @@
import { Result } from '@core/shared/domain/Result';
import { describe, expect, it, vi, type Mock } from 'vitest'; import { describe, expect, it, vi, type Mock } from 'vitest';
import type { MediaResolverPort } from '@core/ports/media/MediaResolverPort'; import type { MediaResolverPort } from '@core/ports/media/MediaResolverPort';
import { ResolveMediaReferenceUseCase } from './ResolveMediaReferenceUseCase'; import { ResolveMediaReferenceUseCase } from './ResolveMediaReferenceUseCase';

View File

@@ -19,15 +19,6 @@ describe('NotificationGateway - Interface Contract', () => {
getChannel: vi.fn().mockReturnValue('in_app'), getChannel: vi.fn().mockReturnValue('in_app'),
}; };
const notification = Notification.create({
id: 'test-id',
recipientId: 'driver-1',
type: 'system_announcement',
title: 'Test',
body: 'Test body',
channel: 'in_app',
});
expect(mockGateway.send).toBeDefined(); expect(mockGateway.send).toBeDefined();
expect(typeof mockGateway.send).toBe('function'); expect(typeof mockGateway.send).toBe('function');
}); });

View File

@@ -201,17 +201,6 @@ describe('NotificationPreferenceRepository - Integration', () => {
it('handles workflow: get or create, then update', async () => { it('handles workflow: get or create, then update', async () => {
const defaultPreference = NotificationPreference.createDefault('driver-1'); const defaultPreference = NotificationPreference.createDefault('driver-1');
const updatedPreference = NotificationPreference.create({
id: 'driver-1',
driverId: 'driver-1',
channels: {
in_app: { enabled: true },
email: { enabled: true },
discord: { enabled: false },
push: { enabled: false },
},
});
const mockRepository: NotificationPreferenceRepository = { const mockRepository: NotificationPreferenceRepository = {
findByDriverId: vi.fn().mockResolvedValue(null), findByDriverId: vi.fn().mockResolvedValue(null),
save: vi.fn().mockResolvedValue(undefined), save: vi.fn().mockResolvedValue(undefined),

View File

@@ -24,7 +24,7 @@ describe('MediaResolverPort - Comprehensive Tests', () => {
it('should define resolve method signature correctly', () => { it('should define resolve method signature correctly', () => {
// Verify the interface has the correct method signature // Verify the interface has the correct method signature
const testInterface: MediaResolverPort = { const testInterface: MediaResolverPort = {
resolve: async (ref: MediaReference): Promise<string | null> => { resolve: async (): Promise<string | null> => {
return null; return null;
}, },
}; };
@@ -124,7 +124,6 @@ describe('MediaResolverPort - Comprehensive Tests', () => {
it('should return null for generated reference without generationRequestId', () => { it('should return null for generated reference without generationRequestId', () => {
// Create a reference with missing generationRequestId // Create a reference with missing generationRequestId
const ref = MediaReference.createGenerated('valid-id');
// Manually create an invalid reference // Manually create an invalid reference
const invalidRef = { type: 'generated' } as MediaReference; const invalidRef = { type: 'generated' } as MediaReference;
const result = ResolutionStrategies.generated(invalidRef); const result = ResolutionStrategies.generated(invalidRef);
@@ -164,7 +163,6 @@ describe('MediaResolverPort - Comprehensive Tests', () => {
it('should return null for uploaded reference without mediaId', () => { it('should return null for uploaded reference without mediaId', () => {
// Create a reference with missing mediaId // Create a reference with missing mediaId
const ref = MediaReference.createUploaded('valid-id');
// Manually create an invalid reference // Manually create an invalid reference
const invalidRef = { type: 'uploaded' } as MediaReference; const invalidRef = { type: 'uploaded' } as MediaReference;
const result = ResolutionStrategies.uploaded(invalidRef); const result = ResolutionStrategies.uploaded(invalidRef);
@@ -284,7 +282,7 @@ describe('MediaResolverPort - Comprehensive Tests', () => {
describe('isMediaResolverPort Type Guard', () => { describe('isMediaResolverPort Type Guard', () => {
it('should return true for valid MediaResolverPort implementation', () => { it('should return true for valid MediaResolverPort implementation', () => {
const validResolver: MediaResolverPort = { const validResolver: MediaResolverPort = {
resolve: async (ref: MediaReference): Promise<string | null> => { resolve: async (): Promise<string | null> => {
return '/test/path'; return '/test/path';
}, },
}; };
@@ -332,7 +330,7 @@ describe('MediaResolverPort - Comprehensive Tests', () => {
it('should return true for object with resolve method and other properties', () => { it('should return true for object with resolve method and other properties', () => {
const validResolver = { const validResolver = {
resolve: async (ref: MediaReference): Promise<string | null> => { resolve: async (): Promise<string | null> => {
return '/test/path'; return '/test/path';
}, },
extraProperty: 'value', extraProperty: 'value',

View File

@@ -180,6 +180,7 @@ export class CompleteRaceUseCase {
startPosition, startPosition,
fastestLap, fastestLap,
incidents, incidents,
points: 0,
}), }),
); );
} }

View File

@@ -1,6 +1,5 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { GetDriverUseCase } from './GetDriverUseCase'; import { GetDriverUseCase } from './GetDriverUseCase';
import { Result } from '@core/shared/domain/Result';
import type { DriverRepository } from '../../domain/repositories/DriverRepository'; import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { Driver } from '../../domain/entities/Driver'; import type { Driver } from '../../domain/entities/Driver';

View File

@@ -1,6 +1,5 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { GetTeamsLeaderboardUseCase } from './GetTeamsLeaderboardUseCase'; import { GetTeamsLeaderboardUseCase } from './GetTeamsLeaderboardUseCase';
import { Result } from '@core/shared/domain/Result';
import type { TeamRepository } from '../../domain/repositories/TeamRepository'; import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository'; import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger'; import type { Logger } from '@core/shared/domain/Logger';

View File

@@ -16,6 +16,7 @@ export type ImportRaceResultDTO = {
fastestLap: number; fastestLap: number;
incidents: number; incidents: number;
startPosition: number; startPosition: number;
points: number;
}; };
export type ImportRaceResultsApiInput = { export type ImportRaceResultsApiInput = {
@@ -145,6 +146,7 @@ export class ImportRaceResultsApiUseCase {
fastestLap: dto.fastestLap, fastestLap: dto.fastestLap,
incidents: dto.incidents, incidents: dto.incidents,
startPosition: dto.startPosition, startPosition: dto.startPosition,
points: dto.points,
}), }),
); );
}), }),

View File

@@ -15,6 +15,7 @@ export type ImportRaceResultRow = {
fastestLap: number; fastestLap: number;
incidents: number; incidents: number;
startPosition: number; startPosition: number;
points: number;
}; };
export type ImportRaceResultsInput = { export type ImportRaceResultsInput = {
@@ -127,6 +128,7 @@ export class ImportRaceResultsUseCase {
fastestLap: row.fastestLap, fastestLap: row.fastestLap,
incidents: row.incidents, incidents: row.incidents,
startPosition: row.startPosition, startPosition: row.startPosition,
points: row.points,
}), }),
); );
}), }),

View File

@@ -1,5 +1,5 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { RankingUseCase, type DriverRanking } from './RankingUseCase'; import { RankingUseCase } from './RankingUseCase';
import type { StandingRepository } from '../../domain/repositories/StandingRepository'; import type { StandingRepository } from '../../domain/repositories/StandingRepository';
import type { DriverRepository } from '../../domain/repositories/DriverRepository'; import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { DriverStatsRepository } from '../../domain/repositories/DriverStatsRepository'; import type { DriverStatsRepository } from '../../domain/repositories/DriverStatsRepository';

View File

@@ -1,4 +1,4 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect } from 'vitest';
import { RaceResultGenerator } from './RaceResultGenerator'; import { RaceResultGenerator } from './RaceResultGenerator';
describe('RaceResultGenerator', () => { describe('RaceResultGenerator', () => {

View File

@@ -62,6 +62,7 @@ export class RaceResultGenerator {
startPosition, startPosition,
fastestLap, fastestLap,
incidents, incidents,
points: 0,
}) })
); );
} }

View File

@@ -1,7 +1,6 @@
import { describe, it, expect, vi } from 'vitest'; import { describe, it, expect, vi } from 'vitest';
import { ChampionshipAggregator } from './ChampionshipAggregator'; import { ChampionshipAggregator } from './ChampionshipAggregator';
import type { DropScoreApplier } from './DropScoreApplier'; import type { DropScoreApplier } from './DropScoreApplier';
import { Points } from '../value-objects/Points';
describe('ChampionshipAggregator', () => { describe('ChampionshipAggregator', () => {
const mockDropScoreApplier = { const mockDropScoreApplier = {

View File

@@ -1,6 +1,6 @@
/** /**
* Unit tests for CalculateRatingUseCase * Unit tests for CalculateRatingUseCase
* *
* Tests business logic and orchestration using mocked ports. * Tests business logic and orchestration using mocked ports.
*/ */
@@ -9,8 +9,11 @@ import { CalculateRatingUseCase } from './CalculateRatingUseCase';
import { Driver } from '../../../racing/domain/entities/Driver'; import { Driver } from '../../../racing/domain/entities/Driver';
import { Race } from '../../../racing/domain/entities/Race'; import { Race } from '../../../racing/domain/entities/Race';
import { Result } from '../../../racing/domain/entities/result/Result'; import { Result } from '../../../racing/domain/entities/result/Result';
import { Rating } from '../../domain/Rating'; import { DriverRepository } from '../../../racing/domain/repositories/DriverRepository';
import { RatingCalculatedEvent } from '../../domain/events/RatingCalculatedEvent'; import { RaceRepository } from '../../../racing/domain/repositories/RaceRepository';
import { ResultRepository } from '../../../racing/domain/repositories/ResultRepository';
import { RatingRepository } from '../../ports/RatingRepository';
import { EventPublisher } from '../../../shared/ports/EventPublisher';
// Mock repositories and publisher // Mock repositories and publisher
const mockDriverRepository = { const mockDriverRepository = {
@@ -39,11 +42,11 @@ describe('CalculateRatingUseCase', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
useCase = new CalculateRatingUseCase({ useCase = new CalculateRatingUseCase({
driverRepository: mockDriverRepository as any, driverRepository: mockDriverRepository as unknown as DriverRepository,
raceRepository: mockRaceRepository as any, raceRepository: mockRaceRepository as unknown as RaceRepository,
resultRepository: mockResultRepository as any, resultRepository: mockResultRepository as unknown as ResultRepository,
ratingRepository: mockRatingRepository as any, ratingRepository: mockRatingRepository as unknown as RatingRepository,
eventPublisher: mockEventPublisher as any, eventPublisher: mockEventPublisher as unknown as EventPublisher,
}); });
}); });

View File

@@ -14,6 +14,7 @@ import { RatingComponents } from '../../domain/RatingComponents';
import { RatingCalculatedEvent } from '../../domain/events/RatingCalculatedEvent'; import { RatingCalculatedEvent } from '../../domain/events/RatingCalculatedEvent';
import { DriverId } from '../../../racing/domain/entities/DriverId'; import { DriverId } from '../../../racing/domain/entities/DriverId';
import { RaceId } from '../../../racing/domain/entities/RaceId'; import { RaceId } from '../../../racing/domain/entities/RaceId';
import { Result as RaceResult } from '../../../racing/domain/entities/result/Result';
export interface CalculateRatingUseCasePorts { export interface CalculateRatingUseCasePorts {
driverRepository: DriverRepository; driverRepository: DriverRepository;
@@ -84,12 +85,12 @@ export class CalculateRatingUseCase {
} }
} }
private calculateComponents(driverResult: any, allResults: any[]): RatingComponents { private calculateComponents(driverResult: RaceResult, allResults: RaceResult[]): RatingComponents {
const position = typeof driverResult.position === 'object' ? (typeof driverResult.position.toNumber === 'function' ? driverResult.position.toNumber() : driverResult.position.value) : driverResult.position; const position = driverResult.position.toNumber();
const totalDrivers = allResults.length; const totalDrivers = allResults.length;
const incidents = typeof driverResult.incidents === 'object' ? (typeof driverResult.incidents.toNumber === 'function' ? driverResult.incidents.toNumber() : driverResult.incidents.value) : driverResult.incidents; const incidents = driverResult.incidents.toNumber();
const lapsCompleted = typeof driverResult.lapsCompleted === 'object' ? (typeof driverResult.lapsCompleted.toNumber === 'function' ? driverResult.lapsCompleted.toNumber() : driverResult.lapsCompleted.value) : (driverResult.lapsCompleted !== undefined ? driverResult.lapsCompleted : (driverResult.totalTime === 0 && (typeof position === 'object' ? position.value : position) > 0 ? 5 : (driverResult.points === 0 && (typeof position === 'object' ? position.value : position) > 0 ? 5 : 20))); const startPosition = driverResult.startPosition.toNumber();
const startPosition = typeof driverResult.startPosition === 'object' ? driverResult.startPosition.toNumber() : driverResult.startPosition; const points = driverResult.points;
// Results Strength: Based on position relative to field size // Results Strength: Based on position relative to field size
const resultsStrength = this.calculateResultsStrength(position, totalDrivers); const resultsStrength = this.calculateResultsStrength(position, totalDrivers);
@@ -104,10 +105,12 @@ export class CalculateRatingUseCase {
const racecraft = this.calculateRacecraft(position, startPosition); const racecraft = this.calculateRacecraft(position, startPosition);
// Reliability: Based on laps completed and DNF/DNS // Reliability: Based on laps completed and DNF/DNS
const reliability = this.calculateReliability(lapsCompleted, position, driverResult.points); // For the Result entity, we need to determine reliability based on position and points
// If position is 0 (DNS) or points are 0 (DNF), reliability is low
const reliability = this.calculateReliabilityFromResult(position, points);
// Team Contribution: Based on points scored // Team Contribution: Based on points scored
const teamContribution = this.calculateTeamContribution(driverResult.points); const teamContribution = this.calculateTeamContribution(points);
return { return {
resultsStrength, resultsStrength,
@@ -119,6 +122,21 @@ export class CalculateRatingUseCase {
}; };
} }
private calculateReliabilityFromResult(position: number, points: number): number {
// DNS (Did Not Start) - position 0
if (position === 0) {
return 1;
}
// DNF (Did Not Finish) - no points but finished (position > 0)
if (points === 0 && position > 0) {
return 20;
}
// Finished with points - high reliability
return 100;
}
private calculateResultsStrength(position: number, totalDrivers: number): number { private calculateResultsStrength(position: number, totalDrivers: number): number {
if (position <= 0) return 1; // DNF/DNS (ensure > 0) if (position <= 0) return 1; // DNF/DNS (ensure > 0)
const drivers = totalDrivers || 1; const drivers = totalDrivers || 1;

View File

@@ -8,6 +8,8 @@ import { Driver } from '../../../racing/domain/entities/Driver';
import { Race } from '../../../racing/domain/entities/Race'; import { Race } from '../../../racing/domain/entities/Race';
import { Result } from '../../../racing/domain/entities/result/Result'; import { Result } from '../../../racing/domain/entities/result/Result';
import { Rating } from '../../domain/Rating'; import { Rating } from '../../domain/Rating';
import { DriverId } from '../../../racing/domain/entities/DriverId';
import { RaceId } from '../../../racing/domain/entities/RaceId';
const mockRatingRepository = { const mockRatingRepository = {
findByDriverAndRace: vi.fn(), findByDriverAndRace: vi.fn(),
@@ -92,8 +94,8 @@ describe('CalculateTeamContributionUseCase', () => {
const points = 12.5; // 50% contribution const points = 12.5; // 50% contribution
const existingRating = Rating.create({ const existingRating = Rating.create({
driverId: 'driver-1' as any, // Simplified for test driverId: DriverId.create('driver-1'),
raceId: 'race-1' as any, raceId: RaceId.create('race-1'),
rating: 1500, rating: 1500,
components: { components: {
resultsStrength: 80, resultsStrength: 80,
@@ -106,10 +108,10 @@ describe('CalculateTeamContributionUseCase', () => {
timestamp: new Date('2023-01-01') timestamp: new Date('2023-01-01')
}); });
mockDriverRepository.findById.mockResolvedValue({ id: driverId } as any); mockDriverRepository.findById.mockResolvedValue({ id: driverId });
mockRaceRepository.findById.mockResolvedValue({ id: raceId } as any); mockRaceRepository.findById.mockResolvedValue({ id: raceId });
mockResultRepository.findByRaceId.mockResolvedValue([ mockResultRepository.findByRaceId.mockResolvedValue([
{ driverId: { toString: () => driverId }, points } as any { driverId: { toString: () => driverId }, points }
]); ]);
mockRatingRepository.findByDriverAndRace.mockResolvedValue(existingRating); mockRatingRepository.findByDriverAndRace.mockResolvedValue(existingRating);

View File

@@ -5,6 +5,8 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { GetRatingLeaderboardUseCase } from './GetRatingLeaderboardUseCase'; import { GetRatingLeaderboardUseCase } from './GetRatingLeaderboardUseCase';
import { Rating } from '../../domain/Rating'; import { Rating } from '../../domain/Rating';
import { DriverId } from '../../../racing/domain/entities/DriverId';
import { RaceId } from '../../../racing/domain/entities/RaceId';
const mockRatingRepository = { const mockRatingRepository = {
findByDriver: vi.fn(), findByDriver: vi.fn(),
@@ -37,37 +39,65 @@ describe('GetRatingLeaderboardUseCase', () => {
const ratingsD1 = [ const ratingsD1 = [
Rating.create({ Rating.create({
driverId: 'd1' as any, driverId: DriverId.create('d1'),
raceId: 'r1' as any, raceId: RaceId.create('r1'),
rating: 1000, rating: 1000,
components: {} as any, components: {
resultsStrength: 0,
consistency: 0,
cleanDriving: 0,
racecraft: 0,
reliability: 0,
teamContribution: 0,
},
timestamp: new Date('2023-01-01') timestamp: new Date('2023-01-01')
}), }),
Rating.create({ Rating.create({
driverId: 'd1' as any, driverId: DriverId.create('d1'),
raceId: 'r2' as any, raceId: RaceId.create('r2'),
rating: 1200, // Latest for D1 rating: 1200, // Latest for D1
components: {} as any, components: {
resultsStrength: 0,
consistency: 0,
cleanDriving: 0,
racecraft: 0,
reliability: 0,
teamContribution: 0,
},
timestamp: new Date('2023-01-02') timestamp: new Date('2023-01-02')
}) })
]; ];
const ratingsD2 = [ const ratingsD2 = [
Rating.create({ Rating.create({
driverId: 'd2' as any, driverId: DriverId.create('d2'),
raceId: 'r1' as any, raceId: RaceId.create('r1'),
rating: 1500, // Latest for D2 rating: 1500, // Latest for D2
components: {} as any, components: {
resultsStrength: 0,
consistency: 0,
cleanDriving: 0,
racecraft: 0,
reliability: 0,
teamContribution: 0,
},
timestamp: new Date('2023-01-01') timestamp: new Date('2023-01-01')
}) })
]; ];
const ratingsD3 = [ const ratingsD3 = [
Rating.create({ Rating.create({
driverId: 'd3' as any, driverId: DriverId.create('d3'),
raceId: 'r1' as any, raceId: RaceId.create('r1'),
rating: 800, // Latest for D3 rating: 800, // Latest for D3
components: {} as any, components: {
resultsStrength: 0,
consistency: 0,
cleanDriving: 0,
racecraft: 0,
reliability: 0,
teamContribution: 0,
},
timestamp: new Date('2023-01-01') timestamp: new Date('2023-01-01')
}) })
]; ];

View File

@@ -40,10 +40,6 @@ export class GetRatingLeaderboardUseCase {
const { limit = 50, offset = 0 } = request; const { limit = 50, offset = 0 } = request;
try { try {
// Get all ratings
const allRatings: Rating[] = [];
const driverIds = new Set<string>();
// Group ratings by driver and get latest rating for each driver // Group ratings by driver and get latest rating for each driver
const driverRatings = new Map<string, Rating>(); const driverRatings = new Map<string, Rating>();

View File

@@ -4,6 +4,7 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'; import { describe, it, expect, vi, beforeEach } from 'vitest';
import { SaveRatingUseCase } from './SaveRatingUseCase'; import { SaveRatingUseCase } from './SaveRatingUseCase';
import { RatingRepository } from '../../ports/RatingRepository';
const mockRatingRepository = { const mockRatingRepository = {
save: vi.fn(), save: vi.fn(),
@@ -15,7 +16,7 @@ describe('SaveRatingUseCase', () => {
beforeEach(() => { beforeEach(() => {
vi.clearAllMocks(); vi.clearAllMocks();
useCase = new SaveRatingUseCase({ useCase = new SaveRatingUseCase({
ratingRepository: mockRatingRepository as any, ratingRepository: mockRatingRepository as unknown as RatingRepository,
}); });
}); });

View File

@@ -1,6 +1,6 @@
/** /**
* Rating Entity * Rating Entity
* *
* Represents a driver's rating calculated after a race. * Represents a driver's rating calculated after a race.
*/ */
@@ -16,6 +16,14 @@ export interface RatingProps {
timestamp: Date; timestamp: Date;
} }
export interface RatingJSON {
driverId: string;
raceId: string;
rating: number;
components: RatingComponents;
timestamp: string;
}
export class Rating { export class Rating {
private constructor(private readonly props: RatingProps) {} private constructor(private readonly props: RatingProps) {}
@@ -43,7 +51,7 @@ export class Rating {
return this.props.timestamp; return this.props.timestamp;
} }
toJSON(): Record<string, any> { toJSON(): RatingJSON {
return { return {
driverId: this.driverId.toString(), driverId: this.driverId.toString(),
raceId: this.raceId.toString(), raceId: this.raceId.toString(),

View File

@@ -1,15 +1,22 @@
/** /**
* RatingCalculatedEvent * RatingCalculatedEvent
* *
* Event published when a driver's rating is calculated. * Event published when a driver's rating is calculated.
*/ */
import { DomainEvent } from '../../../shared/ports/EventPublisher'; import { DomainEvent } from '../../../shared/ports/EventPublisher';
import { Rating } from '../Rating'; import { Rating, RatingJSON } from '../Rating';
export interface RatingCalculatedEventJSON {
type: string;
timestamp: string;
rating: RatingJSON;
}
export class RatingCalculatedEvent implements DomainEvent { export class RatingCalculatedEvent implements DomainEvent {
readonly type = 'RatingCalculatedEvent'; readonly type = 'RatingCalculatedEvent';
readonly timestamp: Date; readonly timestamp: Date;
[key: string]: unknown;
constructor(private readonly rating: Rating) { constructor(private readonly rating: Rating) {
this.timestamp = new Date(); this.timestamp = new Date();
@@ -19,7 +26,7 @@ export class RatingCalculatedEvent implements DomainEvent {
return this.rating; return this.rating;
} }
toJSON(): Record<string, any> { toJSON(): RatingCalculatedEventJSON {
return { return {
type: this.type, type: this.type,
timestamp: this.timestamp.toISOString(), timestamp: this.timestamp.toISOString(),

View File

@@ -95,7 +95,7 @@ describe('DomainEvent', () => {
describe('DomainEventPublisher interface', () => { describe('DomainEventPublisher interface', () => {
it('should have publish method', async () => { it('should have publish method', async () => {
const mockPublisher: DomainEventPublisher = { const mockPublisher: DomainEventPublisher = {
publish: async (event: DomainEvent) => { publish: async () => {
// Mock implementation // Mock implementation
return Promise.resolve(); return Promise.resolve();
} }

View File

@@ -294,7 +294,7 @@ describe('Result', () => {
it('should stop chaining on first error', () => { it('should stop chaining on first error', () => {
const result = Result.ok(2) const result = Result.ok(2)
.andThen((x) => Result.ok(x * 3)) .andThen((x) => Result.ok(x * 3))
.andThen((x) => Result.err(new Error('stopped here'))) .andThen((x) => Result.err(new Error(`stopped at ${x}`)))
.andThen((x) => Result.ok(x + 1)); // This should not execute .andThen((x) => Result.ok(x + 1)); // This should not execute
expect(result.isErr()).toBe(true); expect(result.isErr()).toBe(true);

View File

@@ -36,14 +36,14 @@ describe('ValueObject', () => {
it('should return false when comparing with undefined', () => { it('should return false when comparing with undefined', () => {
const vo = new TestValueObject('test', 42); const vo = new TestValueObject('test', 42);
// Testing that equals method handles undefined gracefully // Testing that equals method handles undefined gracefully
const result = vo.equals as any; const result = vo.equals as (other: unknown) => boolean;
expect(result(undefined)).toBe(false); expect(result(undefined)).toBe(false);
}); });
it('should return false when comparing with null', () => { it('should return false when comparing with null', () => {
const vo = new TestValueObject('test', 42); const vo = new TestValueObject('test', 42);
// Testing that equals method handles null gracefully // Testing that equals method handles null gracefully
const result = vo.equals as any; const result = vo.equals as (other: unknown) => boolean;
expect(result(null)).toBe(false); expect(result(null)).toBe(false);
}); });
}); });

View File

@@ -1,7 +0,0 @@
export * from './DomainEvent';
export * from './Entity';
export * from './Logger';
export * from './Option';
export * from './Result';
export * from './Service';
export * from './ValueObject';

View File

@@ -92,16 +92,16 @@ describe('ApplicationErrorCode', () => {
// This test verifies the type compatibility // This test verifies the type compatibility
type MyErrorCodes = 'USER_NOT_FOUND' | 'VALIDATION_ERROR' | 'PERMISSION_DENIED'; type MyErrorCodes = 'USER_NOT_FOUND' | 'VALIDATION_ERROR' | 'PERMISSION_DENIED';
const userNotFound: ApplicationErrorCode<'USER_NOT_FOUND'> = { const userNotFound: ApplicationErrorCode<MyErrorCodes> = {
code: 'USER_NOT_FOUND' code: 'USER_NOT_FOUND'
}; };
const validationError: ApplicationErrorCode<'VALIDATION_ERROR', { field: string }> = { const validationError: ApplicationErrorCode<MyErrorCodes, { field: string }> = {
code: 'VALIDATION_ERROR', code: 'VALIDATION_ERROR',
details: { field: 'email' } details: { field: 'email' }
}; };
const permissionError: ApplicationErrorCode<'PERMISSION_DENIED', { resource: string }> = { const permissionError: ApplicationErrorCode<MyErrorCodes, { resource: string }> = {
code: 'PERMISSION_DENIED', code: 'PERMISSION_DENIED',
details: { resource: 'admin-panel' } details: { resource: 'admin-panel' }
}; };

View File

@@ -1,6 +1,6 @@
/** /**
* EventPublisher Port * EventPublisher Port
* *
* Defines the interface for publishing domain events. * Defines the interface for publishing domain events.
* This port is implemented by adapters that can publish events. * This port is implemented by adapters that can publish events.
*/ */
@@ -15,5 +15,5 @@ export interface EventPublisher {
export interface DomainEvent { export interface DomainEvent {
type: string; type: string;
timestamp: Date; timestamp: Date;
[key: string]: any; [key: string]: unknown;
} }

14
tsconfig.eslint.json Normal file
View File

@@ -0,0 +1,14 @@
{
"extends": "./tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["apps/website/*"],
"@core/*": ["core/*"],
"@adapters/*": ["adapters/*"],
"@testing/*": ["adapters/testing/*"]
}
},
"include": ["**/*"],
"exclude": ["node_modules", "dist", "**/dist/**", "**/.next/**"]
}