resolve manual DTOs
This commit is contained in:
@@ -1,18 +1,8 @@
|
||||
import { AnalyticsApiClient } from '../../api/analytics/AnalyticsApiClient';
|
||||
import { RecordPageViewOutputViewModel } from '../../view-models/RecordPageViewOutputViewModel';
|
||||
import { RecordEngagementOutputViewModel } from '../../view-models/RecordEngagementOutputViewModel';
|
||||
|
||||
// TODO: Create proper DTOs in generated types
|
||||
interface RecordPageViewInputDTO {
|
||||
path: string;
|
||||
userId?: string;
|
||||
}
|
||||
|
||||
interface RecordEngagementInputDTO {
|
||||
eventType: string;
|
||||
userId?: string;
|
||||
metadata?: Record<string, unknown>;
|
||||
}
|
||||
import { RecordPageViewInputDTO } from '../../types/generated/RecordPageViewInputDTO';
|
||||
import { RecordEngagementInputDTO } from '../../types/generated/RecordEngagementInputDTO';
|
||||
|
||||
/**
|
||||
* Analytics Service
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import { LeagueMembership } from '@/lib/types/LeagueMembership';
|
||||
import { MembershipRole } from '@/lib/types/MembershipRole';
|
||||
import { MembershipStatus } from '@/lib/types/MembershipStatus';
|
||||
import type { LeagueMembership } from '@core/racing/domain/entities/LeagueMembership';
|
||||
import type { MembershipRole } from '@core/racing/domain/entities/MembershipRole';
|
||||
import type { MembershipStatus } from '@core/racing/domain/entities/MembershipStatus';
|
||||
|
||||
export class LeagueMembershipService {
|
||||
// In-memory cache for memberships (populated via API calls)
|
||||
|
||||
@@ -15,7 +15,6 @@ import { LeagueDetailViewModel } from "@/lib/view-models/LeagueDetailViewModel";
|
||||
import { LeagueDetailPageViewModel, SponsorInfo } from "@/lib/view-models/LeagueDetailPageViewModel";
|
||||
import { RaceViewModel } from "@/lib/view-models/RaceViewModel";
|
||||
import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
|
||||
import { DriverDTO } from "@/lib/types/DriverDTO";
|
||||
import { RaceDTO } from "@/lib/types/generated/RaceDTO";
|
||||
import { LeagueScoringConfigDTO } from "@/lib/types/LeagueScoringConfigDTO";
|
||||
import { LeagueStatsDTO } from "@/lib/types/generated/LeagueStatsDTO";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { LeaguesApiClient } from "@/lib/api/leagues/LeaguesApiClient";
|
||||
import { DriversApiClient } from "@/lib/api/drivers/DriversApiClient";
|
||||
import type { LeagueConfigFormModel } from "@/lib/types/LeagueConfigFormModel";
|
||||
import type { LeagueScoringPresetDTO } from "@/lib/types/LeagueScoringPresetDTO";
|
||||
import type { DriverDTO } from "@/lib/types/DriverDTO";
|
||||
import type { LeagueConfigFormModel } from "@core/racing/application";
|
||||
import type { LeagueScoringPresetDTO } from "@core/racing/application/ports/LeagueScoringPresetProvider";
|
||||
import type { GetDriverOutputDTO } from "@/lib/types/generated/GetDriverOutputDTO";
|
||||
import { LeagueSettingsViewModel } from "@/lib/view-models/LeagueSettingsViewModel";
|
||||
import { DriverSummaryViewModel } from "@/lib/view-models/DriverSummaryViewModel";
|
||||
|
||||
@@ -50,7 +50,7 @@ export class LeagueSettingsService {
|
||||
// TODO: get rating and rank from API
|
||||
owner = new DriverSummaryViewModel({
|
||||
driver: ownerDriver,
|
||||
rating: ownerDriver.rating ?? null,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
});
|
||||
}
|
||||
@@ -64,7 +64,7 @@ export class LeagueSettingsService {
|
||||
if (driver) {
|
||||
members.push(new DriverSummaryViewModel({
|
||||
driver,
|
||||
rating: driver.rating ?? null,
|
||||
rating: null, // TODO: get from API
|
||||
rank: null, // TODO: get from API
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
import { apiClient } from '@/lib/apiClient';
|
||||
import { LeagueWizardCommandModel } from '@/lib/command-models/leagues/LeagueWizardCommandModel';
|
||||
import { CreateLeagueResult } from '@/lib/types/CreateLeagueResult';
|
||||
import { CreateLeagueOutputDTO } from '@/lib/types/generated/CreateLeagueOutputDTO';
|
||||
|
||||
export class LeagueWizardService {
|
||||
static async createLeague(
|
||||
form: LeagueWizardCommandModel,
|
||||
ownerId: string,
|
||||
): Promise<CreateLeagueResult> {
|
||||
): Promise<CreateLeagueOutputDTO> {
|
||||
const command = form.toCreateLeagueCommand(ownerId);
|
||||
const result = await apiClient.leagues.create(command);
|
||||
|
||||
return {
|
||||
leagueId: result.leagueId,
|
||||
success: result.success,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// Static method for backward compatibility
|
||||
static async createLeagueFromConfig(
|
||||
form: LeagueWizardCommandModel,
|
||||
ownerId: string,
|
||||
): Promise<CreateLeagueResult> {
|
||||
): Promise<CreateLeagueOutputDTO> {
|
||||
return this.createLeague(form, ownerId);
|
||||
}
|
||||
}
|
||||
@@ -83,9 +83,9 @@ describe('MediaService', () => {
|
||||
const expectedOutput = {
|
||||
id: 'media-123',
|
||||
url: 'https://example.com/image.jpg',
|
||||
type: 'image' as const,
|
||||
category: 'avatar' as const,
|
||||
uploadedAt: new Date('2023-01-15'),
|
||||
type: 'image',
|
||||
category: 'avatar',
|
||||
uploadedAt: '2023-01-15T00:00:00.000Z',
|
||||
size: 2048000,
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
@@ -98,7 +98,7 @@ describe('MediaService', () => {
|
||||
expect(result.url).toBe('https://example.com/image.jpg');
|
||||
expect(result.type).toBe('image');
|
||||
expect(result.category).toBe('avatar');
|
||||
expect(result.uploadedAt).toEqual(new Date('2023-01-15'));
|
||||
expect(result.uploadedAt).toEqual(new Date('2023-01-15T00:00:00.000Z'));
|
||||
expect(result.size).toBe(2048000);
|
||||
expect(result.formattedSize).toBe('2000.00 KB');
|
||||
});
|
||||
@@ -109,8 +109,8 @@ describe('MediaService', () => {
|
||||
const expectedOutput = {
|
||||
id: 'media-456',
|
||||
url: 'https://example.com/video.mp4',
|
||||
type: 'video' as const,
|
||||
uploadedAt: new Date('2023-02-20'),
|
||||
type: 'video',
|
||||
uploadedAt: '2023-02-20T00:00:00.000Z',
|
||||
};
|
||||
mockApiClient.getMedia.mockResolvedValue(expectedOutput);
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { MembershipFeeService, GetMembershipFeesOutputDto } from './MembershipFeeService';
|
||||
import { MembershipFeeService } from './MembershipFeeService';
|
||||
import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
import { MembershipFeeViewModel } from '../../view-models';
|
||||
import type { MembershipFeeDto } from '../../types/generated';
|
||||
@@ -17,36 +17,30 @@ describe('MembershipFeeService', () => {
|
||||
});
|
||||
|
||||
describe('getMembershipFees', () => {
|
||||
it('should call apiClient.getMembershipFees with correct leagueId and return mapped view models', async () => {
|
||||
it('should call apiClient.getMembershipFees with correct leagueId and return fee and payments', async () => {
|
||||
const leagueId = 'league-123';
|
||||
const mockFees: MembershipFeeDto[] = [
|
||||
{ id: 'fee-1', leagueId: 'league-123' },
|
||||
{ id: 'fee-2', leagueId: 'league-123' },
|
||||
];
|
||||
const mockOutput: GetMembershipFeesOutputDto = { fees: mockFees };
|
||||
const mockFee: MembershipFeeDto = { id: 'fee-1', leagueId: 'league-123', seasonId: undefined, type: 'season', amount: 100, enabled: true, createdAt: new Date(), updatedAt: new Date() };
|
||||
const mockPayments: any[] = [];
|
||||
const mockOutput = { fee: mockFee, payments: mockPayments };
|
||||
mockApiClient.getMembershipFees.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith(leagueId);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result[0].id).toEqual('fee-1');
|
||||
expect(result[0].leagueId).toEqual('league-123');
|
||||
expect(result[1]).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result[1].id).toEqual('fee-2');
|
||||
expect(result[1].leagueId).toEqual('league-123');
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith({ leagueId });
|
||||
expect(result.fee).toBeInstanceOf(MembershipFeeViewModel);
|
||||
expect(result.fee!.id).toEqual('fee-1');
|
||||
expect(result.payments).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty array when no fees are returned', async () => {
|
||||
it('should return null fee when no fee is returned', async () => {
|
||||
const leagueId = 'league-456';
|
||||
const mockOutput: GetMembershipFeesOutputDto = { fees: [] };
|
||||
const mockOutput = { fee: null, payments: [] };
|
||||
mockApiClient.getMembershipFees.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getMembershipFees(leagueId);
|
||||
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith(leagueId);
|
||||
expect(result).toEqual([]);
|
||||
expect(mockApiClient.getMembershipFees).toHaveBeenCalledWith({ leagueId });
|
||||
expect(result.fee).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,7 +4,8 @@ import { PaymentsApiClient } from '../../api/payments/PaymentsApiClient';
|
||||
|
||||
// TODO: This DTO should be generated from OpenAPI spec when the endpoint is added
|
||||
export interface GetMembershipFeesOutputDto {
|
||||
fees: MembershipFeeDto[];
|
||||
fee: MembershipFeeDto | null;
|
||||
payments: import('./MemberPaymentDto').MemberPaymentDto[];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -21,8 +22,11 @@ export class MembershipFeeService {
|
||||
/**
|
||||
* Get membership fees by league ID with view model transformation
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<MembershipFeeViewModel[]> {
|
||||
const dto = await this.apiClient.getMembershipFees(leagueId);
|
||||
return dto.fees.map((fee: MembershipFeeDto) => new MembershipFeeViewModel(fee));
|
||||
async getMembershipFees(leagueId: string): Promise<{ fee: MembershipFeeViewModel | null; payments: any[] }> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId });
|
||||
return {
|
||||
fee: dto.fee ? new MembershipFeeViewModel(dto.fee) : null,
|
||||
payments: dto.payments // TODO: map to view models if needed
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ describe('PaymentService', () => {
|
||||
|
||||
const result = await service.getPayments();
|
||||
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith(undefined);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(PaymentViewModel);
|
||||
expect(result[0].id).toBe('payment-1');
|
||||
@@ -54,7 +54,7 @@ describe('PaymentService', () => {
|
||||
|
||||
await service.getPayments('league-1', 'user-1');
|
||||
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith('league-1', 'user-1');
|
||||
expect(mockApiClient.getPayments).toHaveBeenCalledWith({ leagueId: 'league-1', payerId: 'user-1' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -189,7 +189,7 @@ describe('PaymentService', () => {
|
||||
|
||||
const result = await service.getPrizes();
|
||||
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined, undefined);
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith(undefined);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(PrizeViewModel);
|
||||
expect(result[0].id).toBe('prize-1');
|
||||
@@ -201,7 +201,7 @@ describe('PaymentService', () => {
|
||||
|
||||
await service.getPrizes('league-1', 'season-1');
|
||||
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith('league-1', 'season-1');
|
||||
expect(mockApiClient.getPrizes).toHaveBeenCalledWith({ leagueId: 'league-1', seasonId: 'season-1' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -232,9 +232,9 @@ describe('PaymentService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith('user-1');
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(result).toBeInstanceOf(WalletViewModel);
|
||||
expect(result.id).toBe('wallet-1');
|
||||
expect(result.balance).toBe(1000);
|
||||
|
||||
@@ -31,8 +31,9 @@ export class PaymentService {
|
||||
/**
|
||||
* Get all payments with optional filters
|
||||
*/
|
||||
async getPayments(leagueId?: string, driverId?: string): Promise<PaymentViewModel[]> {
|
||||
const dto = await this.apiClient.getPayments(leagueId, driverId);
|
||||
async getPayments(leagueId?: string, payerId?: string): Promise<PaymentViewModel[]> {
|
||||
const query = leagueId || payerId ? { leagueId, payerId } : undefined;
|
||||
const dto = await this.apiClient.getPayments(query);
|
||||
return dto.payments.map((payment: PaymentDTO) => new PaymentViewModel(payment));
|
||||
}
|
||||
|
||||
@@ -60,8 +61,8 @@ export class PaymentService {
|
||||
/**
|
||||
* Get membership fees for a league
|
||||
*/
|
||||
async getMembershipFees(leagueId: string): Promise<MembershipFeeViewModel | null> {
|
||||
const dto = await this.apiClient.getMembershipFees(leagueId);
|
||||
async getMembershipFees(leagueId: string, driverId?: string): Promise<MembershipFeeViewModel | null> {
|
||||
const dto = await this.apiClient.getMembershipFees({ leagueId, driverId });
|
||||
return dto.fee ? new MembershipFeeViewModel(dto.fee) : null;
|
||||
}
|
||||
|
||||
@@ -69,22 +70,23 @@ export class PaymentService {
|
||||
* Get prizes with optional filters
|
||||
*/
|
||||
async getPrizes(leagueId?: string, seasonId?: string): Promise<PrizeViewModel[]> {
|
||||
const dto = await this.apiClient.getPrizes(leagueId, seasonId);
|
||||
const query = leagueId || seasonId ? { leagueId, seasonId } : undefined;
|
||||
const dto = await this.apiClient.getPrizes(query);
|
||||
return dto.prizes.map((prize: PrizeDto) => new PrizeViewModel(prize));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get wallet for a driver
|
||||
* Get wallet for a league
|
||||
*/
|
||||
async getWallet(driverId: string): Promise<WalletViewModel> {
|
||||
const dto = await this.apiClient.getWallet(driverId);
|
||||
async getWallet(leagueId: string): Promise<WalletViewModel> {
|
||||
const dto = await this.apiClient.getWallet({ leagueId });
|
||||
return new WalletViewModel({ ...dto.wallet, transactions: dto.transactions });
|
||||
}
|
||||
|
||||
/**
|
||||
* Get payment history for a user (driver)
|
||||
*/
|
||||
async getPaymentHistory(driverId: string): Promise<PaymentViewModel[]> {
|
||||
return await this.getPayments(undefined, driverId);
|
||||
async getPaymentHistory(payerId: string): Promise<PaymentViewModel[]> {
|
||||
return await this.getPayments(undefined, payerId);
|
||||
}
|
||||
}
|
||||
@@ -42,9 +42,9 @@ describe('WalletService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith('user-1');
|
||||
expect(mockApiClient.getWallet).toHaveBeenCalledWith({ leagueId: 'league-1' });
|
||||
expect(result).toBeInstanceOf(WalletViewModel);
|
||||
expect(result.id).toBe('wallet-1');
|
||||
expect(result.balance).toBe(1000);
|
||||
@@ -69,7 +69,7 @@ describe('WalletService', () => {
|
||||
|
||||
mockApiClient.getWallet.mockResolvedValue(mockResponse);
|
||||
|
||||
const result = await service.getWallet('user-1');
|
||||
const result = await service.getWallet('league-1');
|
||||
|
||||
expect(result.transactions).toHaveLength(0);
|
||||
});
|
||||
|
||||
@@ -102,8 +102,10 @@ describe('RaceResultsService', () => {
|
||||
const input = { raceId, results: [{ position: 1 }] };
|
||||
|
||||
const mockDto = {
|
||||
success: true,
|
||||
raceId,
|
||||
importedCount: 10,
|
||||
driversProcessed: 10,
|
||||
resultsRecorded: 10,
|
||||
errors: ['Error 1'],
|
||||
};
|
||||
|
||||
@@ -114,7 +116,8 @@ describe('RaceResultsService', () => {
|
||||
expect(mockApiClient.importResults).toHaveBeenCalledWith(raceId, input);
|
||||
expect(result).toBeInstanceOf(ImportRaceResultsSummaryViewModel);
|
||||
expect(result.raceId).toBe(raceId);
|
||||
expect(result.importedCount).toBe(10);
|
||||
expect(result.driversProcessed).toBe(10);
|
||||
expect(result.resultsRecorded).toBe(10);
|
||||
expect(result.errors).toEqual(['Error 1']);
|
||||
});
|
||||
|
||||
@@ -123,8 +126,10 @@ describe('RaceResultsService', () => {
|
||||
const input = { raceId, results: [] };
|
||||
|
||||
const mockDto = {
|
||||
success: true,
|
||||
raceId,
|
||||
importedCount: 5,
|
||||
driversProcessed: 5,
|
||||
resultsRecorded: 5,
|
||||
errors: [],
|
||||
};
|
||||
|
||||
@@ -132,7 +137,8 @@ describe('RaceResultsService', () => {
|
||||
|
||||
const result = await service.importResults(raceId, input);
|
||||
|
||||
expect(result.importedCount).toBe(5);
|
||||
expect(result.driversProcessed).toBe(5);
|
||||
expect(result.resultsRecorded).toBe(5);
|
||||
expect(result.errors).toEqual([]);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,15 +2,16 @@ import { RacesApiClient } from '../../api/races/RacesApiClient';
|
||||
import { RaceResultsDetailViewModel } from '../../view-models/RaceResultsDetailViewModel';
|
||||
import { RaceWithSOFViewModel } from '../../view-models/RaceWithSOFViewModel';
|
||||
import { ImportRaceResultsSummaryViewModel } from '../../view-models/ImportRaceResultsSummaryViewModel';
|
||||
import type { ImportRaceResultsDTO } from '../../types/generated/ImportRaceResultsDTO';
|
||||
|
||||
// TODO: Move this type to apps/website/lib/types/generated when available
|
||||
type ImportRaceResultsInputDto = { raceId: string; results: Array<unknown> };
|
||||
|
||||
// TODO: Move this type to apps/website/lib/types/generated when available
|
||||
// Define types
|
||||
type ImportRaceResultsInputDto = ImportRaceResultsDTO;
|
||||
type ImportRaceResultsSummaryDto = {
|
||||
success: boolean;
|
||||
raceId: string;
|
||||
importedCount: number;
|
||||
errors: string[];
|
||||
driversProcessed: number;
|
||||
resultsRecorded: number;
|
||||
errors?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -41,10 +42,10 @@ export class RaceResultsService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Import race results and get summary
|
||||
*/
|
||||
async importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryViewModel> {
|
||||
const dto = await this.apiClient.importResults(raceId, input) as ImportRaceResultsSummaryDto;
|
||||
return new ImportRaceResultsSummaryViewModel(dto);
|
||||
}
|
||||
* Import race results and get summary
|
||||
*/
|
||||
async importResults(raceId: string, input: ImportRaceResultsInputDto): Promise<ImportRaceResultsSummaryViewModel> {
|
||||
const dto = await this.apiClient.importResults(raceId, input);
|
||||
return new ImportRaceResultsSummaryViewModel(dto);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,10 @@ describe('SponsorService', () => {
|
||||
getSponsorships: vi.fn(),
|
||||
create: vi.fn(),
|
||||
getPricing: vi.fn(),
|
||||
getSponsor: vi.fn(),
|
||||
getPendingSponsorshipRequests: vi.fn(),
|
||||
acceptSponsorshipRequest: vi.fn(),
|
||||
rejectSponsorshipRequest: vi.fn(),
|
||||
} as Mocked<SponsorsApiClient>;
|
||||
|
||||
service = new SponsorService(mockApiClient);
|
||||
|
||||
@@ -29,11 +29,16 @@ describe('TeamService', () => {
|
||||
{
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
memberCount: 5,
|
||||
rating: 1500,
|
||||
leagues: ['league-1'],
|
||||
specialization: 'endurance' as const,
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
};
|
||||
|
||||
mockApiClient.getAll.mockResolvedValue(mockDto);
|
||||
@@ -45,6 +50,7 @@ describe('TeamService', () => {
|
||||
expect(result[0]).toBeInstanceOf(TeamSummaryViewModel);
|
||||
expect(result[0].id).toBe('team-1');
|
||||
expect(result[0].name).toBe('Test Team');
|
||||
expect(result[0].tag).toBe('TT');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getAll fails', async () => {
|
||||
@@ -58,13 +64,24 @@ describe('TeamService', () => {
|
||||
describe('getTeamDetails', () => {
|
||||
it('should call apiClient.getDetails and return TeamDetailsViewModel when data exists', async () => {
|
||||
const mockDto = {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
description: 'A test team',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
memberCount: 5,
|
||||
ownerId: 'owner-1',
|
||||
members: [],
|
||||
team: {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
ownerId: 'owner-1',
|
||||
leagues: ['league-1'],
|
||||
createdAt: '2023-01-01T00:00:00Z',
|
||||
specialization: 'endurance',
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
membership: {
|
||||
role: 'member',
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
},
|
||||
canManage: false,
|
||||
};
|
||||
|
||||
mockApiClient.getDetails.mockResolvedValue(mockDto);
|
||||
@@ -75,6 +92,7 @@ describe('TeamService', () => {
|
||||
expect(result).toBeInstanceOf(TeamDetailsViewModel);
|
||||
expect(result?.id).toBe('team-1');
|
||||
expect(result?.name).toBe('Test Team');
|
||||
expect(result?.tag).toBe('TT');
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDetails returns null', async () => {
|
||||
@@ -100,11 +118,17 @@ describe('TeamService', () => {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: { id: 'driver-1', name: 'Driver One', avatarUrl: 'avatar.png', iracingId: '123', rating: 1400 },
|
||||
role: 'member',
|
||||
driverName: 'Driver One',
|
||||
role: 'member' as const,
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
avatarUrl: 'avatar.png',
|
||||
},
|
||||
],
|
||||
totalCount: 1,
|
||||
ownerCount: 0,
|
||||
managerCount: 0,
|
||||
memberCount: 1,
|
||||
};
|
||||
|
||||
mockApiClient.getMembers.mockResolvedValue(mockDto);
|
||||
@@ -176,14 +200,35 @@ describe('TeamService', () => {
|
||||
|
||||
describe('getDriverTeam', () => {
|
||||
it('should call apiClient.getDriverTeam and return the result', async () => {
|
||||
const mockOutput = { teamId: 'team-1', teamName: 'Test Team', role: 'member' };
|
||||
const mockOutput = {
|
||||
team: {
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
tag: 'TT',
|
||||
description: 'A test team',
|
||||
ownerId: 'owner-1',
|
||||
leagues: [],
|
||||
specialization: 'endurance' as const,
|
||||
region: 'EU',
|
||||
languages: ['en'],
|
||||
},
|
||||
membership: {
|
||||
role: 'member',
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
isActive: true,
|
||||
},
|
||||
isOwner: false,
|
||||
canManage: false,
|
||||
};
|
||||
|
||||
mockApiClient.getDriverTeam.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getDriverTeam('driver-1');
|
||||
|
||||
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
|
||||
expect(result).toEqual(mockOutput);
|
||||
expect(result?.teamId).toBe('team-1');
|
||||
expect(result?.teamName).toBe('Test Team');
|
||||
expect(result?.role).toBe('member');
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDriverTeam returns null', async () => {
|
||||
|
||||
@@ -6,16 +6,15 @@ import { UpdateTeamViewModel } from '@/lib/view-models/UpdateTeamViewModel';
|
||||
import { DriverTeamViewModel } from '@/lib/view-models/DriverTeamViewModel';
|
||||
import { LeagueMemberDTO } from '@/lib/types/generated/LeagueMemberDTO';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
|
||||
// TODO: Move these types to apps/website/lib/types/generated when available
|
||||
type DriverDTO = { id: string; name: string; avatarUrl?: string; iracingId?: string; rating?: number };
|
||||
type CreateTeamInputDto = { name: string; tag: string; description?: string };
|
||||
type CreateTeamOutputDto = { id: string; success: boolean };
|
||||
type UpdateTeamInputDto = { name?: string; tag?: string; description?: string };
|
||||
type UpdateTeamOutputDto = { success: boolean };
|
||||
type DriverTeamDto = { teamId: string; teamName: string; role: string };
|
||||
type TeamSummaryDTO = { id: string; name: string; logoUrl?: string; memberCount: number; rating: number };
|
||||
type TeamMemberDTO = { driverId: string; driver?: DriverDTO; role: string; joinedAt: string };
|
||||
import type { GetAllTeamsOutputDTO, TeamListItemDTO } from '@/lib/types/generated/GetAllTeamsOutputDTO';
|
||||
import type { GetTeamDetailsOutputDTO } from '@/lib/types/generated/GetTeamDetailsOutputDTO';
|
||||
import type { GetTeamMembersOutputDTO, TeamMemberDTO } from '@/lib/types/generated/GetTeamMembersOutputDTO';
|
||||
import type { CreateTeamInputDTO } from '@/lib/types/generated/CreateTeamInputDTO';
|
||||
import type { CreateTeamOutputDTO } from '@/lib/types/generated/CreateTeamOutputDTO';
|
||||
import type { UpdateTeamInputDTO } from '@/lib/types/generated/UpdateTeamInputDTO';
|
||||
import type { UpdateTeamOutputDTO } from '@/lib/types/generated/UpdateTeamOutputDTO';
|
||||
import type { GetDriverTeamOutputDTO } from '@/lib/types/generated/GetDriverTeamOutputDTO';
|
||||
import type { GetTeamMembershipOutputDTO } from '@/lib/types/generated/GetTeamMembershipOutputDTO';
|
||||
|
||||
/**
|
||||
* Team Service
|
||||
@@ -32,15 +31,15 @@ export class TeamService {
|
||||
* Get all teams with view model transformation
|
||||
*/
|
||||
async getAllTeams(): Promise<TeamSummaryViewModel[]> {
|
||||
const dto = await this.apiClient.getAll();
|
||||
return dto.teams.map((team: TeamSummaryDTO) => new TeamSummaryViewModel(team));
|
||||
const dto: GetAllTeamsOutputDTO = await this.apiClient.getAll();
|
||||
return dto.teams.map((team: TeamListItemDTO) => new TeamSummaryViewModel(team));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get team details with view model transformation
|
||||
*/
|
||||
async getTeamDetails(teamId: string, currentUserId: string): Promise<TeamDetailsViewModel | null> {
|
||||
const dto = await this.apiClient.getDetails(teamId);
|
||||
const dto: GetTeamDetailsOutputDTO | null = await this.apiClient.getDetails(teamId);
|
||||
if (!dto) {
|
||||
return null;
|
||||
}
|
||||
@@ -51,40 +50,40 @@ export class TeamService {
|
||||
* Get team members with view model transformation
|
||||
*/
|
||||
async getTeamMembers(teamId: string, currentUserId: string, teamOwnerId: string): Promise<TeamMemberViewModel[]> {
|
||||
const dto = await this.apiClient.getMembers(teamId);
|
||||
const dto: GetTeamMembersOutputDTO = await this.apiClient.getMembers(teamId);
|
||||
return dto.members.map((member: TeamMemberDTO) => new TeamMemberViewModel(member, currentUserId, teamOwnerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new team with view model transformation
|
||||
*/
|
||||
async createTeam(input: CreateTeamInputDto): Promise<CreateTeamViewModel> {
|
||||
const dto = await this.apiClient.create(input);
|
||||
async createTeam(input: CreateTeamInputDTO): Promise<CreateTeamViewModel> {
|
||||
const dto: CreateTeamOutputDTO = await this.apiClient.create(input);
|
||||
return new CreateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update team with view model transformation
|
||||
*/
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamViewModel> {
|
||||
const dto = await this.apiClient.update(teamId, input);
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDTO): Promise<UpdateTeamViewModel> {
|
||||
const dto: UpdateTeamOutputDTO = await this.apiClient.update(teamId, input);
|
||||
return new UpdateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get driver's team with view model transformation
|
||||
*/
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
||||
const dto = await this.apiClient.getDriverTeam(driverId);
|
||||
return dto ? new DriverTeamViewModel(dto) : null;
|
||||
}
|
||||
* Get driver's team with view model transformation
|
||||
*/
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
||||
const dto: GetDriverTeamOutputDTO | null = await this.apiClient.getDriverTeam(driverId);
|
||||
return dto ? new DriverTeamViewModel(dto) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get team membership for a driver
|
||||
*/
|
||||
async getMembership(teamId: string, driverId: string): Promise<LeagueMemberDTO | null> {
|
||||
return this.apiClient.getMembership(teamId, driverId);
|
||||
}
|
||||
/**
|
||||
* Get team membership for a driver
|
||||
*/
|
||||
async getMembership(teamId: string, driverId: string): Promise<GetTeamMembershipOutputDTO | null> {
|
||||
return this.apiClient.getMembership(teamId, driverId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a driver from the team
|
||||
|
||||
Reference in New Issue
Block a user