Files
gridpilot.gg/core/racing/application/MembershipUseCases.test.ts
2025-12-16 13:53:23 +01:00

121 lines
3.7 KiB
TypeScript

import { describe, it, expect, beforeEach } from 'vitest';
import { JoinLeagueUseCase } from '@core/racing/application/use-cases/JoinLeagueUseCase';
import type { ILeagueMembershipRepository } from '@core/racing/domain/repositories/ILeagueMembershipRepository';
class InMemoryLeagueMembershipRepository implements ILeagueMembershipRepository {
private memberships: LeagueMembership[] = [];
async getMembership(leagueId: string, driverId: string): Promise<LeagueMembership | null> {
return (
this.memberships.find(
(m) => m.leagueId === leagueId && m.driverId === driverId,
) || null
);
}
async getActiveMembershipForDriver(driverId: string): Promise<LeagueMembership | null> {
return (
this.memberships.find(
(m) => m.driverId === driverId && m.status === 'active',
) || null
);
}
async getLeagueMembers(leagueId: string): Promise<LeagueMembership[]> {
return this.memberships.filter(
(m) => m.leagueId === leagueId && m.status === 'active',
);
}
async getTeamMembers(leagueId: string): Promise<LeagueMembership[]> {
return this.memberships.filter(
(m) => m.leagueId === leagueId && m.status === 'active',
);
}
async saveMembership(membership: LeagueMembership): Promise<LeagueMembership> {
const existingIndex = this.memberships.findIndex(
(m) => m.leagueId === membership.leagueId && m.driverId === membership.driverId,
);
if (existingIndex >= 0) {
this.memberships[existingIndex] = membership;
} else {
this.memberships.push(membership);
}
return membership;
}
async removeMembership(leagueId: string, driverId: string): Promise<void> {
this.memberships = this.memberships.filter(
(m) => !(m.leagueId === leagueId && m.driverId === driverId),
);
}
async getJoinRequests(): Promise<never> {
throw new Error('Not implemented for this test');
}
async saveJoinRequest(): Promise<never> {
throw new Error('Not implemented for this test');
}
async removeJoinRequest(): Promise<never> {
throw new Error('Not implemented for this test');
}
seedMembership(membership: LeagueMembership): void {
this.memberships.push(membership);
}
getAllMemberships(): LeagueMembership[] {
return [...this.memberships];
}
}
describe('Membership use-cases', () => {
describe('JoinLeagueUseCase', () => {
let repository: InMemoryLeagueMembershipRepository;
let useCase: JoinLeagueUseCase;
beforeEach(() => {
repository = new InMemoryLeagueMembershipRepository();
useCase = new JoinLeagueUseCase(repository);
});
it('creates an active member when driver has no membership', async () => {
const leagueId = 'league-1';
const driverId = 'driver-1';
await useCase.execute({ leagueId, driverId });
const membership = await repository.getMembership(leagueId, driverId);
expect(membership).not.toBeNull();
expect(membership?.leagueId).toBe(leagueId);
expect(membership?.driverId).toBe(driverId);
expect(membership?.role as MembershipRole).toBe('member');
expect(membership?.status as MembershipStatus).toBe('active');
expect(membership?.joinedAt).toBeInstanceOf(Date);
});
it('throws when driver already has membership for league', async () => {
const leagueId = 'league-1';
const driverId = 'driver-1';
repository.seedMembership(LeagueMembership.create({
leagueId,
driverId,
role: 'member',
status: 'active',
joinedAt: new Date('2024-01-01'),
}));
await expect(
useCase.execute({ leagueId, driverId }),
).rejects.toThrow('Already a member or have a pending request');
});
});
});