module cleanup

This commit is contained in:
2025-12-19 01:22:45 +01:00
parent d617654928
commit d0fac9e6c1
135 changed files with 5104 additions and 1315 deletions

View File

@@ -0,0 +1,15 @@
import { Presenter } from './Presenter';
export interface CreateLeagueResultDTO {
leagueId: string;
seasonId: string;
scoringPresetId?: string;
scoringPresetName?: string;
}
export interface CreateLeagueViewModel {
leagueId: string;
success: boolean;
}
export interface ICreateLeaguePresenter extends Presenter<CreateLeagueResultDTO, CreateLeagueViewModel> {}

View File

@@ -0,0 +1,13 @@
import { Presenter } from './Presenter';
export interface JoinLeagueResultDTO {
id: string;
}
export interface JoinLeagueViewModel {
success: boolean;
membershipId?: string;
error?: string;
}
export interface IJoinLeaguePresenter extends Presenter<JoinLeagueResultDTO, JoinLeagueViewModel> {}

View File

@@ -1,11 +1,11 @@
import type { Presenter } from '@core/shared/presentation/Presenter';
export interface RemoveLeagueMemberViewModel {
success: boolean;
}
import { Presenter } from './Presenter';
export interface RemoveLeagueMemberResultDTO {
success: boolean;
}
export interface RemoveLeagueMemberViewModel {
success: boolean;
}
export interface IRemoveLeagueMemberPresenter extends Presenter<RemoveLeagueMemberResultDTO, RemoveLeagueMemberViewModel> {}

View File

@@ -0,0 +1,12 @@
import { Presenter } from './Presenter';
export interface TransferLeagueOwnershipResultDTO {
success: boolean;
}
export interface TransferLeagueOwnershipViewModel {
success: boolean;
error?: string;
}
export interface ITransferLeagueOwnershipPresenter extends Presenter<TransferLeagueOwnershipResultDTO, TransferLeagueOwnershipViewModel> {}

View File

@@ -1,11 +1,11 @@
import type { Presenter } from '@core/shared/presentation/Presenter';
export interface UpdateLeagueMemberRoleViewModel {
success: boolean;
}
import { Presenter } from './Presenter';
export interface UpdateLeagueMemberRoleResultDTO {
success: boolean;
}
export interface UpdateLeagueMemberRoleViewModel {
success: boolean;
}
export interface IUpdateLeagueMemberRolePresenter extends Presenter<UpdateLeagueMemberRoleResultDTO, UpdateLeagueMemberRoleViewModel> {}

View File

@@ -0,0 +1,118 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { GetTeamMembershipUseCase } from './GetTeamMembershipUseCase';
import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
import type { Logger } from '@core/shared/application';
describe('GetTeamMembershipUseCase', () => {
const mockGetMembership = vi.fn();
const mockMembershipRepo: ITeamMembershipRepository = {
getMembership: mockGetMembership,
getActiveMembershipForDriver: vi.fn(),
getTeamMembers: vi.fn(),
saveMembership: vi.fn(),
removeMembership: vi.fn(),
getJoinRequests: vi.fn(),
countByTeamId: vi.fn(),
saveJoinRequest: vi.fn(),
removeJoinRequest: vi.fn(),
};
const mockLogger: Logger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
beforeEach(() => {
vi.clearAllMocks();
});
it('should return membership data when membership exists', async () => {
const useCase = new GetTeamMembershipUseCase(
mockMembershipRepo,
mockLogger,
);
const teamId = 'team1';
const driverId = 'driver1';
const membership = {
teamId,
driverId,
role: 'manager' as const,
status: 'active' as const,
joinedAt: new Date('2023-01-01'),
};
mockGetMembership.mockResolvedValue(membership);
const result = await useCase.execute({ teamId, driverId });
expect(result.isOk()).toBe(true);
expect(result.value).toEqual({
role: 'manager',
joinedAt: '2023-01-01T00:00:00.000Z',
isActive: true,
});
});
it('should return null when no membership exists', async () => {
const useCase = new GetTeamMembershipUseCase(
mockMembershipRepo,
mockLogger,
);
const teamId = 'team1';
const driverId = 'driver1';
mockGetMembership.mockResolvedValue(null);
const result = await useCase.execute({ teamId, driverId });
expect(result.isOk()).toBe(true);
expect(result.value).toBe(null);
});
it('should map driver role to member', async () => {
const useCase = new GetTeamMembershipUseCase(
mockMembershipRepo,
mockLogger,
);
const teamId = 'team1';
const driverId = 'driver1';
const membership = {
teamId,
driverId,
role: 'driver' as const,
status: 'active' as const,
joinedAt: new Date('2023-01-01'),
};
mockGetMembership.mockResolvedValue(membership);
const result = await useCase.execute({ teamId, driverId });
expect(result.isOk()).toBe(true);
expect(result.value?.role).toBe('member');
});
it('should return error when repository throws', async () => {
const useCase = new GetTeamMembershipUseCase(
mockMembershipRepo,
mockLogger,
);
const teamId = 'team1';
const driverId = 'driver1';
const error = new Error('Repository error');
mockGetMembership.mockRejectedValue(error);
const result = await useCase.execute({ teamId, driverId });
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
expect(result.unwrapErr().details.message).toBe('Failed to retrieve team membership');
});
});

View File

@@ -0,0 +1,40 @@
import type { ITeamMembershipRepository } from '../../domain/repositories/ITeamMembershipRepository';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { AsyncUseCase, Logger } from '@core/shared/application';
/**
* Use Case for retrieving a driver's membership in a team.
*/
export class GetTeamMembershipUseCase
implements AsyncUseCase<{ teamId: string; driverId: string }, { role: 'owner' | 'manager' | 'member'; joinedAt: string; isActive: boolean } | null, 'REPOSITORY_ERROR'>
{
constructor(
private readonly membershipRepository: ITeamMembershipRepository,
private readonly logger: Logger,
) {}
async execute(input: { teamId: string; driverId: string }): Promise<Result<{ role: 'owner' | 'manager' | 'member'; joinedAt: string; isActive: boolean } | null, ApplicationErrorCode<'REPOSITORY_ERROR'>>> {
this.logger.debug(`Executing GetTeamMembershipUseCase for teamId: ${input.teamId}, driverId: ${input.driverId}`);
try {
const membership = await this.membershipRepository.getMembership(input.teamId, input.driverId);
if (!membership) {
this.logger.debug(`No membership found for teamId: ${input.teamId}, driverId: ${input.driverId}`);
return Result.ok(null);
}
const result = {
role: membership.role === 'driver' ? 'member' : membership.role as 'owner' | 'manager' | 'member',
joinedAt: membership.joinedAt.toISOString(),
isActive: membership.status === 'active',
};
this.logger.info(`Successfully retrieved membership for teamId: ${input.teamId}, driverId: ${input.driverId}`);
return Result.ok(result);
} catch (error) {
this.logger.error(`Error in GetTeamMembershipUseCase for teamId: ${input.teamId}, driverId: ${input.driverId}`, error as Error);
return Result.err({ code: 'REPOSITORY_ERROR', details: { message: 'Failed to retrieve team membership' } });
}
}
}

View File

@@ -1,6 +1,7 @@
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { IRejectLeagueJoinRequestPresenter, RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel } from '../presenters/IRejectLeagueJoinRequestPresenter';
import type { UseCase } from '@core/shared/application/UseCase';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { AsyncUseCase } from '@core/shared/application';
export interface RejectLeagueJoinRequestUseCaseParams {
requestId: string;
@@ -11,13 +12,12 @@ export interface RejectLeagueJoinRequestResultDTO {
message: string;
}
export class RejectLeagueJoinRequestUseCase implements UseCase<RejectLeagueJoinRequestUseCaseParams, RejectLeagueJoinRequestResultDTO, RejectLeagueJoinRequestViewModel, IRejectLeagueJoinRequestPresenter> {
export class RejectLeagueJoinRequestUseCase implements AsyncUseCase<RejectLeagueJoinRequestUseCaseParams, RejectLeagueJoinRequestResultDTO, 'NO_ERROR'> {
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
async execute(params: RejectLeagueJoinRequestUseCaseParams, presenter: IRejectLeagueJoinRequestPresenter): Promise<void> {
async execute(params: RejectLeagueJoinRequestUseCaseParams): Promise<Result<RejectLeagueJoinRequestResultDTO, ApplicationErrorCode<'NO_ERROR'>>> {
await this.leagueMembershipRepository.removeJoinRequest(params.requestId);
const dto: RejectLeagueJoinRequestResultDTO = { success: true, message: 'Join request rejected.' };
presenter.reset();
presenter.present(dto);
return Result.ok(dto);
}
}

View File

@@ -7,6 +7,7 @@ import type { ILeagueRepository } from '@core/racing/domain/repositories/ILeague
import type {
MembershipRole,
} from '@core/racing/domain/entities/LeagueMembership';
import type { TransferLeagueOwnershipResultDTO } from '../presenters/ITransferLeagueOwnershipPresenter';
export interface TransferLeagueOwnershipCommandDTO {
leagueId: string;

View File

@@ -1,6 +1,7 @@
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
import type { UpdateLeagueMemberRoleResultDTO } from '../presenters/IUpdateLeagueMemberRolePresenter';
export interface UpdateLeagueMemberRoleUseCaseParams {
leagueId: string;
@@ -11,7 +12,7 @@ export interface UpdateLeagueMemberRoleUseCaseParams {
export class UpdateLeagueMemberRoleUseCase {
constructor(private readonly leagueMembershipRepository: ILeagueMembershipRepository) {}
async execute(params: UpdateLeagueMemberRoleUseCaseParams): Promise<Result<void, ApplicationErrorCode<'MEMBERSHIP_NOT_FOUND'>>> {
async execute(params: UpdateLeagueMemberRoleUseCaseParams): Promise<Result<UpdateLeagueMemberRoleResultDTO, ApplicationErrorCode<'MEMBERSHIP_NOT_FOUND'>>> {
const memberships = await this.leagueMembershipRepository.getLeagueMembers(params.leagueId);
const membership = memberships.find(m => m.driverId === params.targetDriverId);
if (!membership) {
@@ -21,6 +22,6 @@ export class UpdateLeagueMemberRoleUseCase {
...membership,
role: params.newRole,
});
return Result.ok(undefined);
return Result.ok({ success: true });
}
}