integration tests
Some checks failed
CI / lint-typecheck (pull_request) Failing after 4m50s
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

This commit is contained in:
2026-01-22 23:55:28 +01:00
parent 853ec7b0ce
commit eaf51712a7
29 changed files with 2625 additions and 280 deletions

View File

@@ -20,22 +20,22 @@ import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
import { InMemoryLeagueRepository } from '../../../adapters/leagues/persistence/inmemory/InMemoryLeagueRepository';
import { InMemoryDriverRepository } from '../../../adapters/drivers/persistence/inmemory/InMemoryDriverRepository';
import { InMemoryEventPublisher } from '../../../adapters/events/InMemoryEventPublisher';
import { GetLeagueRosterUseCase } from '../../../core/leagues/use-cases/GetLeagueRosterUseCase';
import { JoinLeagueUseCase } from '../../../core/leagues/use-cases/JoinLeagueUseCase';
import { LeaveLeagueUseCase } from '../../../core/leagues/use-cases/LeaveLeagueUseCase';
import { ApproveMembershipRequestUseCase } from '../../../core/leagues/use-cases/ApproveMembershipRequestUseCase';
import { RejectMembershipRequestUseCase } from '../../../core/leagues/use-cases/RejectMembershipRequestUseCase';
import { PromoteMemberUseCase } from '../../../core/leagues/use-cases/PromoteMemberUseCase';
import { DemoteAdminUseCase } from '../../../core/leagues/use-cases/DemoteAdminUseCase';
import { RemoveMemberUseCase } from '../../../core/leagues/use-cases/RemoveMemberUseCase';
import { LeagueRosterQuery } from '../../../core/leagues/ports/LeagueRosterQuery';
import { JoinLeagueCommand } from '../../../core/leagues/ports/JoinLeagueCommand';
import { LeaveLeagueCommand } from '../../../core/leagues/ports/LeaveLeagueCommand';
import { ApproveMembershipRequestCommand } from '../../../core/leagues/ports/ApproveMembershipRequestCommand';
import { RejectMembershipRequestCommand } from '../../../core/leagues/ports/RejectMembershipRequestCommand';
import { PromoteMemberCommand } from '../../../core/leagues/ports/PromoteMemberCommand';
import { DemoteAdminCommand } from '../../../core/leagues/ports/DemoteAdminCommand';
import { RemoveMemberCommand } from '../../../core/leagues/ports/RemoveMemberCommand';
import { GetLeagueRosterUseCase } from '../../../core/leagues/application/use-cases/GetLeagueRosterUseCase';
import { JoinLeagueUseCase } from '../../../core/leagues/application/use-cases/JoinLeagueUseCase';
import { LeaveLeagueUseCase } from '../../../core/leagues/application/use-cases/LeaveLeagueUseCase';
import { ApproveMembershipRequestUseCase } from '../../../core/leagues/application/use-cases/ApproveMembershipRequestUseCase';
import { RejectMembershipRequestUseCase } from '../../../core/leagues/application/use-cases/RejectMembershipRequestUseCase';
import { PromoteMemberUseCase } from '../../../core/leagues/application/use-cases/PromoteMemberUseCase';
import { DemoteAdminUseCase } from '../../../core/leagues/application/use-cases/DemoteAdminUseCase';
import { RemoveMemberUseCase } from '../../../core/leagues/application/use-cases/RemoveMemberUseCase';
import { LeagueRosterQuery } from '../../../core/leagues/application/ports/LeagueRosterQuery';
import { JoinLeagueCommand } from '../../../core/leagues/application/ports/JoinLeagueCommand';
import { LeaveLeagueCommand } from '../../../core/leagues/application/ports/LeaveLeagueCommand';
import { ApproveMembershipRequestCommand } from '../../../core/leagues/application/ports/ApproveMembershipRequestCommand';
import { RejectMembershipRequestCommand } from '../../../core/leagues/application/ports/RejectMembershipRequestCommand';
import { PromoteMemberCommand } from '../../../core/leagues/application/ports/PromoteMemberCommand';
import { DemoteAdminCommand } from '../../../core/leagues/application/ports/DemoteAdminCommand';
import { RemoveMemberCommand } from '../../../core/leagues/application/ports/RemoveMemberCommand';
describe('League Roster Use Case Orchestration', () => {
let leagueRepository: InMemoryLeagueRepository;
@@ -51,112 +51,516 @@ describe('League Roster Use Case Orchestration', () => {
let removeMemberUseCase: RemoveMemberUseCase;
beforeAll(() => {
// TODO: Initialize In-Memory repositories and event publisher
// leagueRepository = new InMemoryLeagueRepository();
// driverRepository = new InMemoryDriverRepository();
// eventPublisher = new InMemoryEventPublisher();
// getLeagueRosterUseCase = new GetLeagueRosterUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// joinLeagueUseCase = new JoinLeagueUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// leaveLeagueUseCase = new LeaveLeagueUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// approveMembershipRequestUseCase = new ApproveMembershipRequestUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// rejectMembershipRequestUseCase = new RejectMembershipRequestUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// promoteMemberUseCase = new PromoteMemberUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// demoteAdminUseCase = new DemoteAdminUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// removeMemberUseCase = new RemoveMemberUseCase({
// leagueRepository,
// driverRepository,
// eventPublisher,
// });
// Initialize In-Memory repositories and event publisher
leagueRepository = new InMemoryLeagueRepository();
driverRepository = new InMemoryDriverRepository();
eventPublisher = new InMemoryEventPublisher();
getLeagueRosterUseCase = new GetLeagueRosterUseCase(
leagueRepository,
eventPublisher,
);
joinLeagueUseCase = new JoinLeagueUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
leaveLeagueUseCase = new LeaveLeagueUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
approveMembershipRequestUseCase = new ApproveMembershipRequestUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
rejectMembershipRequestUseCase = new RejectMembershipRequestUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
promoteMemberUseCase = new PromoteMemberUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
demoteAdminUseCase = new DemoteAdminUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
removeMemberUseCase = new RemoveMemberUseCase(
leagueRepository,
driverRepository,
eventPublisher,
);
});
beforeEach(() => {
// TODO: Clear all In-Memory repositories before each test
// leagueRepository.clear();
// driverRepository.clear();
// eventPublisher.clear();
// Clear all In-Memory repositories before each test
leagueRepository.clear();
driverRepository.clear();
eventPublisher.clear();
});
describe('GetLeagueRosterUseCase - Success Path', () => {
it('should retrieve complete league roster with all members', async () => {
// TODO: Implement test
// Scenario: League with complete roster
// Given: A league exists with multiple members
// And: The league has owners, admins, and drivers
// And: Each member has join dates and roles
const leagueId = 'league-123';
const ownerId = 'driver-1';
const adminId = 'driver-2';
const driverId = 'driver-3';
// Create league
await leagueRepository.create({
id: leagueId,
name: 'Test League',
description: 'A test league for integration testing',
visibility: 'public',
ownerId,
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
maxDrivers: 20,
approvalRequired: true,
lateJoinAllowed: true,
raceFrequency: 'weekly',
raceDay: 'Saturday',
raceTime: '18:00',
tracks: ['Monza', 'Spa', 'Nürburgring'],
scoringSystem: { points: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1] },
bonusPointsEnabled: true,
penaltiesEnabled: true,
protestsEnabled: true,
appealsEnabled: true,
stewardTeam: ['steward-1', 'steward-2'],
gameType: 'iRacing',
skillLevel: 'Intermediate',
category: 'GT3',
tags: ['competitive', 'weekly-races'],
});
// Add league members
leagueRepository.addLeagueMembers(leagueId, [
{
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
},
{
driverId: adminId,
name: 'Admin Driver',
role: 'admin',
joinDate: new Date('2024-01-15'),
},
{
driverId: driverId,
name: 'Regular Driver',
role: 'member',
joinDate: new Date('2024-02-01'),
},
]);
// Add pending requests
leagueRepository.addPendingRequests(leagueId, [
{
id: 'request-1',
driverId: 'driver-4',
name: 'Pending Driver',
requestDate: new Date('2024-02-15'),
},
]);
// When: GetLeagueRosterUseCase.execute() is called with league ID
const result = await getLeagueRosterUseCase.execute({ leagueId });
// Then: The result should contain all league members
// And: Each member should display their name
// And: Each member should display their role
// And: Each member should display their join date
expect(result).toBeDefined();
expect(result.leagueId).toBe(leagueId);
expect(result.members).toHaveLength(3);
// And: Each member should display their name, role, and join date
expect(result.members[0]).toEqual({
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
});
expect(result.members[1]).toEqual({
driverId: adminId,
name: 'Admin Driver',
role: 'admin',
joinDate: new Date('2024-01-15'),
});
expect(result.members[2]).toEqual({
driverId: driverId,
name: 'Regular Driver',
role: 'member',
joinDate: new Date('2024-02-01'),
});
// And: Pending requests should be included
expect(result.pendingRequests).toHaveLength(1);
expect(result.pendingRequests[0]).toEqual({
requestId: 'request-1',
driverId: 'driver-4',
name: 'Pending Driver',
requestDate: new Date('2024-02-15'),
});
// And: Stats should be calculated
expect(result.stats.adminCount).toBe(2); // owner + admin
expect(result.stats.driverCount).toBe(1); // member
// And: EventPublisher should emit LeagueRosterAccessedEvent
expect(eventPublisher.getLeagueRosterAccessedEventCount()).toBe(1);
const events = eventPublisher.getLeagueRosterAccessedEvents();
expect(events[0].leagueId).toBe(leagueId);
});
it('should retrieve league roster with minimal members', async () => {
// TODO: Implement test
// Scenario: League with minimal roster
// Given: A league exists with only the owner
const leagueId = 'league-minimal';
const ownerId = 'driver-owner';
// Create league
await leagueRepository.create({
id: leagueId,
name: 'Minimal League',
description: 'A league with only the owner',
visibility: 'public',
ownerId,
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
maxDrivers: 10,
approvalRequired: true,
lateJoinAllowed: true,
raceFrequency: 'weekly',
raceDay: 'Saturday',
raceTime: '18:00',
tracks: ['Monza'],
scoringSystem: { points: [25, 18, 15] },
bonusPointsEnabled: true,
penaltiesEnabled: true,
protestsEnabled: true,
appealsEnabled: true,
stewardTeam: ['steward-1'],
gameType: 'iRacing',
skillLevel: 'Intermediate',
category: 'GT3',
tags: ['minimal'],
});
// Add only the owner as a member
leagueRepository.addLeagueMembers(leagueId, [
{
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
},
]);
// When: GetLeagueRosterUseCase.execute() is called with league ID
const result = await getLeagueRosterUseCase.execute({ leagueId });
// Then: The result should contain only the owner
expect(result).toBeDefined();
expect(result.leagueId).toBe(leagueId);
expect(result.members).toHaveLength(1);
// And: The owner should be marked as "Owner"
expect(result.members[0]).toEqual({
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
});
// And: Pending requests should be empty
expect(result.pendingRequests).toHaveLength(0);
// And: Stats should be calculated
expect(result.stats.adminCount).toBe(1); // owner
expect(result.stats.driverCount).toBe(0); // no members
// And: EventPublisher should emit LeagueRosterAccessedEvent
expect(eventPublisher.getLeagueRosterAccessedEventCount()).toBe(1);
const events = eventPublisher.getLeagueRosterAccessedEvents();
expect(events[0].leagueId).toBe(leagueId);
});
it('should retrieve league roster with pending membership requests', async () => {
// TODO: Implement test
// Scenario: League with pending requests
// Given: A league exists with pending membership requests
const leagueId = 'league-pending-requests';
const ownerId = 'driver-owner';
// Create league
await leagueRepository.create({
id: leagueId,
name: 'League with Pending Requests',
description: 'A league with pending membership requests',
visibility: 'public',
ownerId,
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
maxDrivers: 20,
approvalRequired: true,
lateJoinAllowed: true,
raceFrequency: 'weekly',
raceDay: 'Saturday',
raceTime: '18:00',
tracks: ['Monza', 'Spa'],
scoringSystem: { points: [25, 18, 15, 12, 10] },
bonusPointsEnabled: true,
penaltiesEnabled: true,
protestsEnabled: true,
appealsEnabled: true,
stewardTeam: ['steward-1', 'steward-2'],
gameType: 'iRacing',
skillLevel: 'Intermediate',
category: 'GT3',
tags: ['pending-requests'],
});
// Add owner as a member
leagueRepository.addLeagueMembers(leagueId, [
{
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
},
]);
// Add pending requests
leagueRepository.addPendingRequests(leagueId, [
{
id: 'request-1',
driverId: 'driver-2',
name: 'Pending Driver 1',
requestDate: new Date('2024-02-15'),
},
{
id: 'request-2',
driverId: 'driver-3',
name: 'Pending Driver 2',
requestDate: new Date('2024-02-20'),
},
]);
// When: GetLeagueRosterUseCase.execute() is called with league ID
const result = await getLeagueRosterUseCase.execute({ leagueId });
// Then: The result should contain pending requests
expect(result).toBeDefined();
expect(result.leagueId).toBe(leagueId);
expect(result.members).toHaveLength(1);
expect(result.pendingRequests).toHaveLength(2);
// And: Each request should display driver name and request date
expect(result.pendingRequests[0]).toEqual({
requestId: 'request-1',
driverId: 'driver-2',
name: 'Pending Driver 1',
requestDate: new Date('2024-02-15'),
});
expect(result.pendingRequests[1]).toEqual({
requestId: 'request-2',
driverId: 'driver-3',
name: 'Pending Driver 2',
requestDate: new Date('2024-02-20'),
});
// And: Stats should be calculated
expect(result.stats.adminCount).toBe(1); // owner
expect(result.stats.driverCount).toBe(0); // no members
// And: EventPublisher should emit LeagueRosterAccessedEvent
expect(eventPublisher.getLeagueRosterAccessedEventCount()).toBe(1);
const events = eventPublisher.getLeagueRosterAccessedEvents();
expect(events[0].leagueId).toBe(leagueId);
});
it('should retrieve league roster with admin count', async () => {
// TODO: Implement test
// Scenario: League with multiple admins
// Given: A league exists with multiple admins
const leagueId = 'league-admin-count';
const ownerId = 'driver-owner';
const adminId1 = 'driver-admin-1';
const adminId2 = 'driver-admin-2';
const driverId = 'driver-member';
// Create league
await leagueRepository.create({
id: leagueId,
name: 'League with Admins',
description: 'A league with multiple admins',
visibility: 'public',
ownerId,
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
maxDrivers: 20,
approvalRequired: true,
lateJoinAllowed: true,
raceFrequency: 'weekly',
raceDay: 'Saturday',
raceTime: '18:00',
tracks: ['Monza', 'Spa', 'Nürburgring'],
scoringSystem: { points: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1] },
bonusPointsEnabled: true,
penaltiesEnabled: true,
protestsEnabled: true,
appealsEnabled: true,
stewardTeam: ['steward-1', 'steward-2'],
gameType: 'iRacing',
skillLevel: 'Intermediate',
category: 'GT3',
tags: ['admin-count'],
});
// Add league members with multiple admins
leagueRepository.addLeagueMembers(leagueId, [
{
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
},
{
driverId: adminId1,
name: 'Admin Driver 1',
role: 'admin',
joinDate: new Date('2024-01-15'),
},
{
driverId: adminId2,
name: 'Admin Driver 2',
role: 'admin',
joinDate: new Date('2024-01-20'),
},
{
driverId: driverId,
name: 'Regular Driver',
role: 'member',
joinDate: new Date('2024-02-01'),
},
]);
// When: GetLeagueRosterUseCase.execute() is called with league ID
const result = await getLeagueRosterUseCase.execute({ leagueId });
// Then: The result should show admin count
// And: Admin count should be accurate
expect(result).toBeDefined();
expect(result.leagueId).toBe(leagueId);
expect(result.members).toHaveLength(4);
// And: Admin count should be accurate (owner + 2 admins = 3)
expect(result.stats.adminCount).toBe(3);
expect(result.stats.driverCount).toBe(1); // 1 member
// And: EventPublisher should emit LeagueRosterAccessedEvent
expect(eventPublisher.getLeagueRosterAccessedEventCount()).toBe(1);
const events = eventPublisher.getLeagueRosterAccessedEvents();
expect(events[0].leagueId).toBe(leagueId);
});
it('should retrieve league roster with driver count', async () => {
// TODO: Implement test
// Scenario: League with multiple drivers
// Given: A league exists with multiple drivers
const leagueId = 'league-driver-count';
const ownerId = 'driver-owner';
const adminId = 'driver-admin';
const driverId1 = 'driver-member-1';
const driverId2 = 'driver-member-2';
const driverId3 = 'driver-member-3';
// Create league
await leagueRepository.create({
id: leagueId,
name: 'League with Drivers',
description: 'A league with multiple drivers',
visibility: 'public',
ownerId,
status: 'active',
createdAt: new Date(),
updatedAt: new Date(),
maxDrivers: 20,
approvalRequired: true,
lateJoinAllowed: true,
raceFrequency: 'weekly',
raceDay: 'Saturday',
raceTime: '18:00',
tracks: ['Monza', 'Spa', 'Nürburgring'],
scoringSystem: { points: [25, 18, 15, 12, 10, 8, 6, 4, 2, 1] },
bonusPointsEnabled: true,
penaltiesEnabled: true,
protestsEnabled: true,
appealsEnabled: true,
stewardTeam: ['steward-1', 'steward-2'],
gameType: 'iRacing',
skillLevel: 'Intermediate',
category: 'GT3',
tags: ['driver-count'],
});
// Add league members with multiple drivers
leagueRepository.addLeagueMembers(leagueId, [
{
driverId: ownerId,
name: 'Owner Driver',
role: 'owner',
joinDate: new Date('2024-01-01'),
},
{
driverId: adminId,
name: 'Admin Driver',
role: 'admin',
joinDate: new Date('2024-01-15'),
},
{
driverId: driverId1,
name: 'Regular Driver 1',
role: 'member',
joinDate: new Date('2024-02-01'),
},
{
driverId: driverId2,
name: 'Regular Driver 2',
role: 'member',
joinDate: new Date('2024-02-05'),
},
{
driverId: driverId3,
name: 'Regular Driver 3',
role: 'member',
joinDate: new Date('2024-02-10'),
},
]);
// When: GetLeagueRosterUseCase.execute() is called with league ID
const result = await getLeagueRosterUseCase.execute({ leagueId });
// Then: The result should show driver count
// And: Driver count should be accurate
expect(result).toBeDefined();
expect(result.leagueId).toBe(leagueId);
expect(result.members).toHaveLength(5);
// And: Driver count should be accurate (3 members)
expect(result.stats.adminCount).toBe(2); // owner + admin
expect(result.stats.driverCount).toBe(3); // 3 members
// And: EventPublisher should emit LeagueRosterAccessedEvent
expect(eventPublisher.getLeagueRosterAccessedEventCount()).toBe(1);
const events = eventPublisher.getLeagueRosterAccessedEvents();
expect(events[0].leagueId).toBe(leagueId);
});
it('should retrieve league roster with member statistics', async () => {