view models
This commit is contained in:
80
apps/website/lib/services/teams/TeamJoinService.test.ts
Normal file
80
apps/website/lib/services/teams/TeamJoinService.test.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { TeamJoinService } from './TeamJoinService';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
|
||||
describe('TeamJoinService', () => {
|
||||
let service: TeamJoinService;
|
||||
let mockApiClient: TeamsApiClient;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getJoinRequests: vi.fn(),
|
||||
} as any;
|
||||
|
||||
service = new TeamJoinService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getJoinRequests', () => {
|
||||
it('should return view models for join requests', async () => {
|
||||
const mockDto = {
|
||||
requests: [
|
||||
{
|
||||
id: 'request-1',
|
||||
teamId: 'team-1',
|
||||
driverId: 'driver-1',
|
||||
requestedAt: '2023-01-01T00:00:00Z',
|
||||
message: 'Please accept me',
|
||||
},
|
||||
{
|
||||
id: 'request-2',
|
||||
teamId: 'team-1',
|
||||
driverId: 'driver-2',
|
||||
requestedAt: '2023-01-02T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getJoinRequests = vi.fn().mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getJoinRequests('team-1', 'user-1', true);
|
||||
|
||||
expect(mockApiClient.getJoinRequests).toHaveBeenCalledWith('team-1');
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0].id).toBe('request-1');
|
||||
expect(result[0].canApprove).toBe(true);
|
||||
expect(result[1].id).toBe('request-2');
|
||||
expect(result[1].canApprove).toBe(true);
|
||||
});
|
||||
|
||||
it('should pass correct parameters to view model constructor', async () => {
|
||||
const mockDto = {
|
||||
requests: [
|
||||
{
|
||||
id: 'request-1',
|
||||
teamId: 'team-1',
|
||||
driverId: 'driver-1',
|
||||
requestedAt: '2023-01-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getJoinRequests = vi.fn().mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getJoinRequests('team-1', 'user-1', false);
|
||||
|
||||
expect(result[0].canApprove).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('approveJoinRequest', () => {
|
||||
it('should throw not implemented error', async () => {
|
||||
await expect(service.approveJoinRequest()).rejects.toThrow('Not implemented: API endpoint for approving join requests');
|
||||
});
|
||||
});
|
||||
|
||||
describe('rejectJoinRequest', () => {
|
||||
it('should throw not implemented error', async () => {
|
||||
await expect(service.rejectJoinRequest()).rejects.toThrow('Not implemented: API endpoint for rejecting join requests');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,12 +1,9 @@
|
||||
import { TeamJoinRequestViewModel, type TeamJoinRequestDTO } from '@/lib/view-models/TeamJoinRequestViewModel';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
import { TeamJoinRequestViewModel } from '../../view-models';
|
||||
|
||||
type TeamJoinRequestDTO = {
|
||||
id: string;
|
||||
teamId: string;
|
||||
driverId: string;
|
||||
requestedAt: string;
|
||||
message?: string;
|
||||
// TODO: Create generated DTO when API spec is available
|
||||
type TeamJoinRequestsDto = {
|
||||
requests: TeamJoinRequestDTO[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -24,14 +21,14 @@ export class TeamJoinService {
|
||||
* Get team join requests with view model transformation
|
||||
*/
|
||||
async getJoinRequests(teamId: string, currentUserId: string, isOwner: boolean): Promise<TeamJoinRequestViewModel[]> {
|
||||
const dto = await this.apiClient.getJoinRequests(teamId);
|
||||
const dto = await this.apiClient.getJoinRequests(teamId) as TeamJoinRequestsDto;
|
||||
return dto.requests.map((r: TeamJoinRequestDTO) => new TeamJoinRequestViewModel(r, currentUserId, isOwner));
|
||||
}
|
||||
|
||||
/**
|
||||
* Approve a team join request
|
||||
*/
|
||||
async approveJoinRequest(teamId: string, requestId: string): Promise<void> {
|
||||
async approveJoinRequest(): Promise<void> {
|
||||
// TODO: implement API call when endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for approving join requests');
|
||||
}
|
||||
@@ -39,7 +36,7 @@ export class TeamJoinService {
|
||||
/**
|
||||
* Reject a team join request
|
||||
*/
|
||||
async rejectJoinRequest(teamId: string, requestId: string): Promise<void> {
|
||||
async rejectJoinRequest(): Promise<void> {
|
||||
// TODO: implement API call when endpoint is available
|
||||
throw new Error('Not implemented: API endpoint for rejecting join requests');
|
||||
}
|
||||
|
||||
205
apps/website/lib/services/teams/TeamService.test.ts
Normal file
205
apps/website/lib/services/teams/TeamService.test.ts
Normal file
@@ -0,0 +1,205 @@
|
||||
import { describe, it, expect, vi, Mocked } from 'vitest';
|
||||
import { TeamService } from './TeamService';
|
||||
import { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
import { TeamSummaryViewModel } from '../../view-models/TeamSummaryViewModel';
|
||||
import { TeamDetailsViewModel } from '../../view-models/TeamDetailsViewModel';
|
||||
import { TeamMemberViewModel } from '../../view-models/TeamMemberViewModel';
|
||||
|
||||
describe('TeamService', () => {
|
||||
let mockApiClient: Mocked<TeamsApiClient>;
|
||||
let service: TeamService;
|
||||
|
||||
beforeEach(() => {
|
||||
mockApiClient = {
|
||||
getAll: vi.fn(),
|
||||
getDetails: vi.fn(),
|
||||
getMembers: vi.fn(),
|
||||
create: vi.fn(),
|
||||
update: vi.fn(),
|
||||
getDriverTeam: vi.fn(),
|
||||
} as Mocked<TeamsApiClient>;
|
||||
|
||||
service = new TeamService(mockApiClient);
|
||||
});
|
||||
|
||||
describe('getAllTeams', () => {
|
||||
it('should call apiClient.getAll and return array of TeamSummaryViewModel', async () => {
|
||||
const mockDto = {
|
||||
teams: [
|
||||
{
|
||||
id: 'team-1',
|
||||
name: 'Test Team',
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
memberCount: 5,
|
||||
rating: 1500,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getAll.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getAllTeams();
|
||||
|
||||
expect(mockApiClient.getAll).toHaveBeenCalled();
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(TeamSummaryViewModel);
|
||||
expect(result[0].id).toBe('team-1');
|
||||
expect(result[0].name).toBe('Test Team');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getAll fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getAll.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getAllTeams()).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
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: [],
|
||||
};
|
||||
|
||||
mockApiClient.getDetails.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getTeamDetails('team-1', 'user-1');
|
||||
|
||||
expect(mockApiClient.getDetails).toHaveBeenCalledWith('team-1');
|
||||
expect(result).toBeInstanceOf(TeamDetailsViewModel);
|
||||
expect(result?.id).toBe('team-1');
|
||||
expect(result?.name).toBe('Test Team');
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDetails returns null', async () => {
|
||||
mockApiClient.getDetails.mockResolvedValue(null);
|
||||
|
||||
const result = await service.getTeamDetails('team-1', 'user-1');
|
||||
|
||||
expect(mockApiClient.getDetails).toHaveBeenCalledWith('team-1');
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getDetails fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getDetails.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getTeamDetails('team-1', 'user-1')).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTeamMembers', () => {
|
||||
it('should call apiClient.getMembers and return array of TeamMemberViewModel', async () => {
|
||||
const mockDto = {
|
||||
members: [
|
||||
{
|
||||
driverId: 'driver-1',
|
||||
driver: { id: 'driver-1', name: 'Driver One', avatarUrl: 'avatar.png', iracingId: '123', rating: 1400 },
|
||||
role: 'member',
|
||||
joinedAt: '2023-01-01T00:00:00Z',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
mockApiClient.getMembers.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.getTeamMembers('team-1', 'user-1', 'owner-1');
|
||||
|
||||
expect(mockApiClient.getMembers).toHaveBeenCalledWith('team-1');
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(TeamMemberViewModel);
|
||||
expect(result[0].driverId).toBe('driver-1');
|
||||
expect(result[0].role).toBe('member');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getMembers fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getMembers.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getTeamMembers('team-1', 'user-1', 'owner-1')).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createTeam', () => {
|
||||
it('should call apiClient.create and return the result', async () => {
|
||||
const input = { name: 'New Team', tag: 'NT', description: 'A new team' };
|
||||
|
||||
const mockOutput = { id: 'team-123', success: true };
|
||||
|
||||
mockApiClient.create.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.createTeam(input);
|
||||
|
||||
expect(mockApiClient.create).toHaveBeenCalledWith(input);
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.create fails', async () => {
|
||||
const input = { name: 'New Team', tag: 'NT' };
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.create.mockRejectedValue(error);
|
||||
|
||||
await expect(service.createTeam(input)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateTeam', () => {
|
||||
it('should call apiClient.update and return the result', async () => {
|
||||
const input = { name: 'Updated Team', description: 'Updated description' };
|
||||
|
||||
const mockOutput = { success: true };
|
||||
|
||||
mockApiClient.update.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.updateTeam('team-1', input);
|
||||
|
||||
expect(mockApiClient.update).toHaveBeenCalledWith('team-1', input);
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.update fails', async () => {
|
||||
const input = { name: 'Updated Team' };
|
||||
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.update.mockRejectedValue(error);
|
||||
|
||||
await expect(service.updateTeam('team-1', input)).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDriverTeam', () => {
|
||||
it('should call apiClient.getDriverTeam and return the result', async () => {
|
||||
const mockOutput = { teamId: 'team-1', teamName: 'Test Team', role: 'member' };
|
||||
|
||||
mockApiClient.getDriverTeam.mockResolvedValue(mockOutput);
|
||||
|
||||
const result = await service.getDriverTeam('driver-1');
|
||||
|
||||
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
|
||||
expect(result).toEqual(mockOutput);
|
||||
});
|
||||
|
||||
it('should return null when apiClient.getDriverTeam returns null', async () => {
|
||||
mockApiClient.getDriverTeam.mockResolvedValue(null);
|
||||
|
||||
const result = await service.getDriverTeam('driver-1');
|
||||
|
||||
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith('driver-1');
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.getDriverTeam fails', async () => {
|
||||
const error = new Error('API call failed');
|
||||
mockApiClient.getDriverTeam.mockRejectedValue(error);
|
||||
|
||||
await expect(service.getDriverTeam('driver-1')).rejects.toThrow('API call failed');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,14 +1,20 @@
|
||||
import { TeamDetailsViewModel } from '@/lib/view-models/TeamDetailsViewModel';
|
||||
import { TeamMemberViewModel } from '@/lib/view-models/TeamMemberViewModel';
|
||||
import { TeamSummaryViewModel } from '@/lib/view-models/TeamSummaryViewModel';
|
||||
import { CreateTeamViewModel } from '@/lib/view-models/CreateTeamViewModel';
|
||||
import { UpdateTeamViewModel } from '@/lib/view-models/UpdateTeamViewModel';
|
||||
import { DriverTeamViewModel } from '@/lib/view-models/DriverTeamViewModel';
|
||||
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
||||
import { TeamSummaryViewModel, TeamDetailsViewModel, TeamMemberViewModel } from '../../view-models';
|
||||
|
||||
// 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?: any; role: string; joinedAt: string };
|
||||
type TeamMemberDTO = { driverId: string; driver?: DriverDTO; role: string; joinedAt: string };
|
||||
|
||||
/**
|
||||
* Team Service
|
||||
@@ -49,23 +55,26 @@ export class TeamService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new team
|
||||
* Create a new team with view model transformation
|
||||
*/
|
||||
async createTeam(input: CreateTeamInputDto): Promise<CreateTeamOutputDto> {
|
||||
return await this.apiClient.create(input);
|
||||
async createTeam(input: CreateTeamInputDto): Promise<CreateTeamViewModel> {
|
||||
const dto = await this.apiClient.create(input);
|
||||
return new CreateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update team
|
||||
* Update team with view model transformation
|
||||
*/
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamOutputDto> {
|
||||
return await this.apiClient.update(teamId, input);
|
||||
async updateTeam(teamId: string, input: UpdateTeamInputDto): Promise<UpdateTeamViewModel> {
|
||||
const dto = await this.apiClient.update(teamId, input);
|
||||
return new UpdateTeamViewModel(dto);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get driver's team
|
||||
* Get driver's team with view model transformation
|
||||
*/
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamDto | null> {
|
||||
return await this.apiClient.getDriverTeam(driverId);
|
||||
async getDriverTeam(driverId: string): Promise<DriverTeamViewModel | null> {
|
||||
const dto = await this.apiClient.getDriverTeam(driverId);
|
||||
return dto ? new DriverTeamViewModel(dto) : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user