wip league admin tools
This commit is contained in:
@@ -3,7 +3,9 @@ import {
|
||||
ApproveLeagueJoinRequestUseCase,
|
||||
type ApproveLeagueJoinRequestResult,
|
||||
} from './ApproveLeagueJoinRequestUseCase';
|
||||
import { League } from '../../domain/entities/League';
|
||||
import type { ILeagueMembershipRepository } from '../../domain/repositories/ILeagueMembershipRepository';
|
||||
import type { ILeagueRepository } from '../../domain/repositories/ILeagueRepository';
|
||||
import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort';
|
||||
|
||||
describe('ApproveLeagueJoinRequestUseCase', () => {
|
||||
@@ -11,6 +13,11 @@ describe('ApproveLeagueJoinRequestUseCase', () => {
|
||||
getJoinRequests: Mock;
|
||||
removeJoinRequest: Mock;
|
||||
saveMembership: Mock;
|
||||
getLeagueMembers: Mock;
|
||||
};
|
||||
|
||||
let mockLeagueRepo: {
|
||||
findById: Mock;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -18,33 +25,52 @@ describe('ApproveLeagueJoinRequestUseCase', () => {
|
||||
getJoinRequests: vi.fn(),
|
||||
removeJoinRequest: vi.fn(),
|
||||
saveMembership: vi.fn(),
|
||||
getLeagueMembers: vi.fn(),
|
||||
};
|
||||
|
||||
mockLeagueRepo = {
|
||||
findById: vi.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should approve join request and save membership', async () => {
|
||||
it('approve removes request and adds member', async () => {
|
||||
const output = {
|
||||
present: vi.fn(),
|
||||
};
|
||||
|
||||
const useCase = new ApproveLeagueJoinRequestUseCase(
|
||||
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
|
||||
mockLeagueRepo as unknown as ILeagueRepository,
|
||||
);
|
||||
|
||||
const leagueId = 'league-1';
|
||||
const requestId = 'req-1';
|
||||
const joinRequests = [{ id: requestId, leagueId, driverId: 'driver-1', requestedAt: new Date(), message: 'msg' }];
|
||||
const joinRequestId = 'req-1';
|
||||
const joinRequests = [{ id: joinRequestId, leagueId, driverId: 'driver-1', requestedAt: new Date(), message: 'msg' }];
|
||||
|
||||
mockLeagueRepo.findById.mockResolvedValue(
|
||||
League.create({
|
||||
id: leagueId,
|
||||
name: 'L',
|
||||
description: 'D',
|
||||
ownerId: 'owner-1',
|
||||
settings: { maxDrivers: 32, visibility: 'unranked' },
|
||||
participantCount: 0,
|
||||
}),
|
||||
);
|
||||
|
||||
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([]);
|
||||
mockLeagueMembershipRepo.getJoinRequests.mockResolvedValue(joinRequests);
|
||||
|
||||
const result = await useCase.execute(
|
||||
{ leagueId, requestId },
|
||||
{ leagueId, joinRequestId },
|
||||
output as unknown as UseCaseOutputPort<ApproveLeagueJoinRequestResult>,
|
||||
);
|
||||
|
||||
expect(result.isOk()).toBe(true);
|
||||
expect(result.unwrap()).toBeUndefined();
|
||||
expect(mockLeagueMembershipRepo.removeJoinRequest).toHaveBeenCalledWith(requestId);
|
||||
expect(mockLeagueMembershipRepo.removeJoinRequest).toHaveBeenCalledWith(joinRequestId);
|
||||
expect(mockLeagueMembershipRepo.saveMembership).toHaveBeenCalledTimes(1);
|
||||
|
||||
const savedMembership = (mockLeagueMembershipRepo.saveMembership as Mock).mock.calls[0]?.[0] as unknown as {
|
||||
id: string;
|
||||
leagueId: { toString(): string };
|
||||
@@ -60,26 +86,101 @@ describe('ApproveLeagueJoinRequestUseCase', () => {
|
||||
expect(savedMembership.role.toString()).toBe('member');
|
||||
expect(savedMembership.status.toString()).toBe('active');
|
||||
expect(savedMembership.joinedAt.toDate()).toBeInstanceOf(Date);
|
||||
|
||||
expect(output.present).toHaveBeenCalledWith({ success: true, message: 'Join request approved.' });
|
||||
});
|
||||
|
||||
it('should return error if request not found', async () => {
|
||||
it('approve returns error when request missing', async () => {
|
||||
const output = {
|
||||
present: vi.fn(),
|
||||
};
|
||||
|
||||
const useCase = new ApproveLeagueJoinRequestUseCase(
|
||||
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
|
||||
mockLeagueRepo as unknown as ILeagueRepository,
|
||||
);
|
||||
|
||||
mockLeagueRepo.findById.mockResolvedValue(
|
||||
League.create({
|
||||
id: 'league-1',
|
||||
name: 'L',
|
||||
description: 'D',
|
||||
ownerId: 'owner-1',
|
||||
settings: { maxDrivers: 32, visibility: 'unranked' },
|
||||
participantCount: 0,
|
||||
}),
|
||||
);
|
||||
|
||||
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([]);
|
||||
mockLeagueMembershipRepo.getJoinRequests.mockResolvedValue([]);
|
||||
|
||||
const result = await useCase.execute(
|
||||
{ leagueId: 'league-1', requestId: 'req-1' },
|
||||
{ leagueId: 'league-1', joinRequestId: 'req-1' },
|
||||
output as unknown as UseCaseOutputPort<ApproveLeagueJoinRequestResult>,
|
||||
);
|
||||
|
||||
expect(result.isOk()).toBe(false);
|
||||
expect(result.error!.code).toBe('JOIN_REQUEST_NOT_FOUND');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(mockLeagueMembershipRepo.removeJoinRequest).not.toHaveBeenCalled();
|
||||
expect(mockLeagueMembershipRepo.saveMembership).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('rejects approval when league is at capacity and does not mutate state', async () => {
|
||||
const output = {
|
||||
present: vi.fn(),
|
||||
};
|
||||
|
||||
const useCase = new ApproveLeagueJoinRequestUseCase(
|
||||
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
|
||||
mockLeagueRepo as unknown as ILeagueRepository,
|
||||
);
|
||||
|
||||
const leagueId = 'league-1';
|
||||
const joinRequestId = 'req-1';
|
||||
const joinRequests = [{ id: joinRequestId, leagueId, driverId: 'driver-2', requestedAt: new Date(), message: 'msg' }];
|
||||
|
||||
mockLeagueRepo.findById.mockResolvedValue(
|
||||
League.create({
|
||||
id: leagueId,
|
||||
name: 'L',
|
||||
description: 'D',
|
||||
ownerId: 'owner-1',
|
||||
settings: { maxDrivers: 2, visibility: 'unranked' },
|
||||
participantCount: 2,
|
||||
}),
|
||||
);
|
||||
|
||||
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
|
||||
{
|
||||
id: `${leagueId}:owner-1`,
|
||||
leagueId: { toString: () => leagueId },
|
||||
driverId: { toString: () => 'owner-1' },
|
||||
role: { toString: () => 'owner' },
|
||||
status: { toString: () => 'active' },
|
||||
joinedAt: { toDate: () => new Date() },
|
||||
},
|
||||
{
|
||||
id: `${leagueId}:driver-1`,
|
||||
leagueId: { toString: () => leagueId },
|
||||
driverId: { toString: () => 'driver-1' },
|
||||
role: { toString: () => 'member' },
|
||||
status: { toString: () => 'active' },
|
||||
joinedAt: { toDate: () => new Date() },
|
||||
},
|
||||
]);
|
||||
|
||||
mockLeagueMembershipRepo.getJoinRequests.mockResolvedValue(joinRequests);
|
||||
|
||||
const result = await useCase.execute(
|
||||
{ leagueId, joinRequestId },
|
||||
output as unknown as UseCaseOutputPort<ApproveLeagueJoinRequestResult>,
|
||||
);
|
||||
|
||||
expect(result.isErr()).toBe(true);
|
||||
expect(result.unwrapErr().code).toBe('LEAGUE_AT_CAPACITY');
|
||||
expect(output.present).not.toHaveBeenCalled();
|
||||
expect(mockLeagueMembershipRepo.removeJoinRequest).not.toHaveBeenCalled();
|
||||
expect(mockLeagueMembershipRepo.saveMembership).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user