422 lines
13 KiB
TypeScript
422 lines
13 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
import { TeamService } from './TeamService';
|
|
import type { TeamsApiClient } from '../../api/teams/TeamsApiClient';
|
|
import type { TeamDetailsPresenter } from '../../presenters/TeamDetailsPresenter';
|
|
import type { TeamListPresenter } from '../../presenters/TeamListPresenter';
|
|
import type { TeamMembersPresenter } from '../../presenters/TeamMembersPresenter';
|
|
import type {
|
|
AllTeamsDto,
|
|
TeamDetailsDto,
|
|
TeamMembersDto,
|
|
CreateTeamInputDto,
|
|
CreateTeamOutputDto,
|
|
UpdateTeamInputDto,
|
|
UpdateTeamOutputDto,
|
|
DriverTeamDto,
|
|
} from '../../dtos';
|
|
import type { TeamSummaryViewModel, TeamDetailsViewModel, TeamMemberViewModel } from '../../view-models';
|
|
|
|
describe('TeamService', () => {
|
|
let service: TeamService;
|
|
let mockApiClient: TeamsApiClient;
|
|
let mockTeamListPresenter: TeamListPresenter;
|
|
let mockTeamDetailsPresenter: TeamDetailsPresenter;
|
|
let mockTeamMembersPresenter: TeamMembersPresenter;
|
|
|
|
beforeEach(() => {
|
|
mockApiClient = {
|
|
getAll: vi.fn(),
|
|
getDetails: vi.fn(),
|
|
getMembers: vi.fn(),
|
|
create: vi.fn(),
|
|
update: vi.fn(),
|
|
getDriverTeam: vi.fn(),
|
|
} as unknown as TeamsApiClient;
|
|
|
|
mockTeamListPresenter = {
|
|
present: vi.fn(),
|
|
} as unknown as TeamListPresenter;
|
|
|
|
mockTeamDetailsPresenter = {
|
|
present: vi.fn(),
|
|
} as unknown as TeamDetailsPresenter;
|
|
|
|
mockTeamMembersPresenter = {
|
|
present: vi.fn(),
|
|
} as unknown as TeamMembersPresenter;
|
|
|
|
service = new TeamService(
|
|
mockApiClient,
|
|
mockTeamListPresenter,
|
|
mockTeamDetailsPresenter,
|
|
mockTeamMembersPresenter
|
|
);
|
|
});
|
|
|
|
describe('constructor', () => {
|
|
it('should create instance with injected dependencies', () => {
|
|
expect(service).toBeInstanceOf(TeamService);
|
|
});
|
|
});
|
|
|
|
describe('getAllTeams', () => {
|
|
it('should fetch all teams from API and transform via presenter', async () => {
|
|
// Arrange
|
|
const mockDto: AllTeamsDto = {
|
|
teams: [
|
|
{
|
|
id: 'team-1',
|
|
name: 'Team Alpha',
|
|
logoUrl: 'https://example.com/logo1.png',
|
|
memberCount: 5,
|
|
rating: 2500,
|
|
},
|
|
{
|
|
id: 'team-2',
|
|
name: 'Team Beta',
|
|
logoUrl: 'https://example.com/logo2.png',
|
|
memberCount: 3,
|
|
rating: 2300,
|
|
},
|
|
],
|
|
};
|
|
|
|
const mockViewModels: TeamSummaryViewModel[] = [
|
|
{
|
|
id: 'team-1',
|
|
name: 'Team Alpha',
|
|
logoUrl: 'https://example.com/logo1.png',
|
|
memberCount: 5,
|
|
rating: 2500,
|
|
} as TeamSummaryViewModel,
|
|
{
|
|
id: 'team-2',
|
|
name: 'Team Beta',
|
|
logoUrl: 'https://example.com/logo2.png',
|
|
memberCount: 3,
|
|
rating: 2300,
|
|
} as TeamSummaryViewModel,
|
|
];
|
|
|
|
vi.mocked(mockApiClient.getAll).mockResolvedValue(mockDto);
|
|
vi.mocked(mockTeamListPresenter.present).mockReturnValue(mockViewModels);
|
|
|
|
// Act
|
|
const result = await service.getAllTeams();
|
|
|
|
// Assert
|
|
expect(mockApiClient.getAll).toHaveBeenCalled();
|
|
expect(mockTeamListPresenter.present).toHaveBeenCalledWith(mockDto);
|
|
expect(result).toEqual(mockViewModels);
|
|
});
|
|
|
|
it('should handle empty teams list', async () => {
|
|
// Arrange
|
|
const mockDto: AllTeamsDto = {
|
|
teams: [],
|
|
};
|
|
|
|
const mockViewModels: TeamSummaryViewModel[] = [];
|
|
|
|
vi.mocked(mockApiClient.getAll).mockResolvedValue(mockDto);
|
|
vi.mocked(mockTeamListPresenter.present).mockReturnValue(mockViewModels);
|
|
|
|
// Act
|
|
const result = await service.getAllTeams();
|
|
|
|
// Assert
|
|
expect(mockApiClient.getAll).toHaveBeenCalled();
|
|
expect(mockTeamListPresenter.present).toHaveBeenCalledWith(mockDto);
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const error = new Error('Failed to fetch teams');
|
|
vi.mocked(mockApiClient.getAll).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.getAllTeams()).rejects.toThrow('Failed to fetch teams');
|
|
expect(mockApiClient.getAll).toHaveBeenCalled();
|
|
expect(mockTeamListPresenter.present).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('getTeamDetails', () => {
|
|
it('should fetch team details and transform via presenter', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const currentUserId = 'user-456';
|
|
|
|
const mockDto: TeamDetailsDto = {
|
|
id: teamId,
|
|
name: 'Team Alpha',
|
|
description: 'A competitive racing team',
|
|
logoUrl: 'https://example.com/logo.png',
|
|
memberCount: 5,
|
|
ownerId: 'user-789',
|
|
};
|
|
|
|
const mockViewModel: TeamDetailsViewModel = {
|
|
id: teamId,
|
|
name: 'Team Alpha',
|
|
description: 'A competitive racing team',
|
|
logoUrl: 'https://example.com/logo.png',
|
|
memberCount: 5,
|
|
ownerId: 'user-789',
|
|
members: [],
|
|
} as TeamDetailsViewModel;
|
|
|
|
vi.mocked(mockApiClient.getDetails).mockResolvedValue(mockDto);
|
|
vi.mocked(mockTeamDetailsPresenter.present).mockReturnValue(mockViewModel);
|
|
|
|
// Act
|
|
const result = await service.getTeamDetails(teamId, currentUserId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getDetails).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamDetailsPresenter.present).toHaveBeenCalledWith(mockDto, currentUserId);
|
|
expect(result).toEqual(mockViewModel);
|
|
});
|
|
|
|
it('should return null when team is not found', async () => {
|
|
// Arrange
|
|
const teamId = 'non-existent';
|
|
const currentUserId = 'user-456';
|
|
|
|
vi.mocked(mockApiClient.getDetails).mockResolvedValue(null);
|
|
|
|
// Act
|
|
const result = await service.getTeamDetails(teamId, currentUserId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getDetails).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamDetailsPresenter.present).not.toHaveBeenCalled();
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const currentUserId = 'user-456';
|
|
const error = new Error('Failed to fetch team details');
|
|
vi.mocked(mockApiClient.getDetails).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.getTeamDetails(teamId, currentUserId)).rejects.toThrow('Failed to fetch team details');
|
|
expect(mockApiClient.getDetails).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamDetailsPresenter.present).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('getTeamMembers', () => {
|
|
it('should fetch team members and transform via presenter', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const currentUserId = 'user-456';
|
|
const teamOwnerId = 'user-789';
|
|
|
|
const mockDto: TeamMembersDto = {
|
|
members: [
|
|
{
|
|
driverId: 'driver-1',
|
|
role: 'owner',
|
|
joinedAt: '2024-01-01',
|
|
},
|
|
{
|
|
driverId: 'driver-2',
|
|
role: 'member',
|
|
joinedAt: '2024-01-02',
|
|
},
|
|
],
|
|
};
|
|
|
|
const mockViewModels: TeamMemberViewModel[] = [
|
|
{
|
|
driverId: 'driver-1',
|
|
role: 'owner',
|
|
joinedAt: '2024-01-01',
|
|
} as TeamMemberViewModel,
|
|
{
|
|
driverId: 'driver-2',
|
|
role: 'member',
|
|
joinedAt: '2024-01-02',
|
|
} as TeamMemberViewModel,
|
|
];
|
|
|
|
vi.mocked(mockApiClient.getMembers).mockResolvedValue(mockDto);
|
|
vi.mocked(mockTeamMembersPresenter.present).mockReturnValue(mockViewModels);
|
|
|
|
// Act
|
|
const result = await service.getTeamMembers(teamId, currentUserId, teamOwnerId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getMembers).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamMembersPresenter.present).toHaveBeenCalledWith(mockDto, currentUserId, teamOwnerId);
|
|
expect(result).toEqual(mockViewModels);
|
|
});
|
|
|
|
it('should handle empty members list', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const currentUserId = 'user-456';
|
|
const teamOwnerId = 'user-789';
|
|
|
|
const mockDto: TeamMembersDto = {
|
|
members: [],
|
|
};
|
|
|
|
const mockViewModels: TeamMemberViewModel[] = [];
|
|
|
|
vi.mocked(mockApiClient.getMembers).mockResolvedValue(mockDto);
|
|
vi.mocked(mockTeamMembersPresenter.present).mockReturnValue(mockViewModels);
|
|
|
|
// Act
|
|
const result = await service.getTeamMembers(teamId, currentUserId, teamOwnerId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getMembers).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamMembersPresenter.present).toHaveBeenCalledWith(mockDto, currentUserId, teamOwnerId);
|
|
expect(result).toEqual([]);
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const currentUserId = 'user-456';
|
|
const teamOwnerId = 'user-789';
|
|
const error = new Error('Failed to fetch team members');
|
|
vi.mocked(mockApiClient.getMembers).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.getTeamMembers(teamId, currentUserId, teamOwnerId)).rejects.toThrow('Failed to fetch team members');
|
|
expect(mockApiClient.getMembers).toHaveBeenCalledWith(teamId);
|
|
expect(mockTeamMembersPresenter.present).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('createTeam', () => {
|
|
it('should create a new team', async () => {
|
|
// Arrange
|
|
const input: CreateTeamInputDto = {
|
|
name: 'New Team',
|
|
description: 'A new racing team',
|
|
};
|
|
|
|
const mockOutput: CreateTeamOutputDto = {
|
|
id: 'team-new',
|
|
name: 'New Team',
|
|
success: true,
|
|
};
|
|
|
|
vi.mocked(mockApiClient.create).mockResolvedValue(mockOutput);
|
|
|
|
// Act
|
|
const result = await service.createTeam(input);
|
|
|
|
// Assert
|
|
expect(mockApiClient.create).toHaveBeenCalledWith(input);
|
|
expect(result).toEqual(mockOutput);
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const input: CreateTeamInputDto = {
|
|
name: 'New Team',
|
|
description: 'A new racing team',
|
|
};
|
|
const error = new Error('Failed to create team');
|
|
vi.mocked(mockApiClient.create).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.createTeam(input)).rejects.toThrow('Failed to create team');
|
|
expect(mockApiClient.create).toHaveBeenCalledWith(input);
|
|
});
|
|
});
|
|
|
|
describe('updateTeam', () => {
|
|
it('should update team details', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const input: UpdateTeamInputDto = {
|
|
name: 'Updated Team Name',
|
|
description: 'Updated description',
|
|
};
|
|
|
|
const mockOutput: UpdateTeamOutputDto = {
|
|
id: teamId,
|
|
success: true,
|
|
};
|
|
|
|
vi.mocked(mockApiClient.update).mockResolvedValue(mockOutput);
|
|
|
|
// Act
|
|
const result = await service.updateTeam(teamId, input);
|
|
|
|
// Assert
|
|
expect(mockApiClient.update).toHaveBeenCalledWith(teamId, input);
|
|
expect(result).toEqual(mockOutput);
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const teamId = 'team-123';
|
|
const input: UpdateTeamInputDto = {
|
|
name: 'Updated Team Name',
|
|
};
|
|
const error = new Error('Failed to update team');
|
|
vi.mocked(mockApiClient.update).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.updateTeam(teamId, input)).rejects.toThrow('Failed to update team');
|
|
expect(mockApiClient.update).toHaveBeenCalledWith(teamId, input);
|
|
});
|
|
});
|
|
|
|
describe('getDriverTeam', () => {
|
|
it('should fetch driver team', async () => {
|
|
// Arrange
|
|
const driverId = 'driver-123';
|
|
|
|
const mockDto: DriverTeamDto = {
|
|
teamId: 'team-456',
|
|
teamName: 'Team Alpha',
|
|
role: 'member',
|
|
};
|
|
|
|
vi.mocked(mockApiClient.getDriverTeam).mockResolvedValue(mockDto);
|
|
|
|
// Act
|
|
const result = await service.getDriverTeam(driverId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith(driverId);
|
|
expect(result).toEqual(mockDto);
|
|
});
|
|
|
|
it('should return null when driver has no team', async () => {
|
|
// Arrange
|
|
const driverId = 'driver-123';
|
|
|
|
vi.mocked(mockApiClient.getDriverTeam).mockResolvedValue(null);
|
|
|
|
// Act
|
|
const result = await service.getDriverTeam(driverId);
|
|
|
|
// Assert
|
|
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith(driverId);
|
|
expect(result).toBeNull();
|
|
});
|
|
|
|
it('should propagate errors from API client', async () => {
|
|
// Arrange
|
|
const driverId = 'driver-123';
|
|
const error = new Error('Failed to fetch driver team');
|
|
vi.mocked(mockApiClient.getDriverTeam).mockRejectedValue(error);
|
|
|
|
// Act & Assert
|
|
await expect(service.getDriverTeam(driverId)).rejects.toThrow('Failed to fetch driver team');
|
|
expect(mockApiClient.getDriverTeam).toHaveBeenCalledWith(driverId);
|
|
});
|
|
});
|
|
}); |