blockers
This commit is contained in:
@@ -200,7 +200,7 @@ describe('LeagueService', () => {
|
||||
});
|
||||
|
||||
describe('createLeague', () => {
|
||||
it('should call apiClient.create and return CreateLeagueViewModel', async () => {
|
||||
it('should call apiClient.create', async () => {
|
||||
const input: CreateLeagueInputDTO = {
|
||||
name: 'New League',
|
||||
description: 'A new league',
|
||||
@@ -213,31 +213,9 @@ describe('LeagueService', () => {
|
||||
|
||||
mockApiClient.create.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.createLeague(input);
|
||||
await service.createLeague(input);
|
||||
|
||||
expect(mockApiClient.create).toHaveBeenCalledWith(input);
|
||||
expect(result).toBeInstanceOf(CreateLeagueViewModel);
|
||||
expect(result.leagueId).toBe('new-league-id');
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle unsuccessful creation', async () => {
|
||||
const input: CreateLeagueInputDTO = {
|
||||
name: 'New League',
|
||||
description: 'A new league',
|
||||
};
|
||||
|
||||
const mockDto: CreateLeagueOutputDTO = {
|
||||
leagueId: '',
|
||||
success: false,
|
||||
};
|
||||
|
||||
mockApiClient.create.mockResolvedValue(mockDto);
|
||||
|
||||
const result = await service.createLeague(input);
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.successMessage).toBe('Failed to create league.');
|
||||
});
|
||||
|
||||
it('should throw error when apiClient.create fails', async () => {
|
||||
@@ -251,6 +229,52 @@ describe('LeagueService', () => {
|
||||
|
||||
await expect(service.createLeague(input)).rejects.toThrow('API call failed');
|
||||
});
|
||||
|
||||
it('should not call apiClient.create when submitBlocker is blocked', async () => {
|
||||
const input: CreateLeagueInputDTO = {
|
||||
name: 'New League',
|
||||
description: 'A new league',
|
||||
};
|
||||
|
||||
// First call should succeed
|
||||
const mockDto: CreateLeagueOutputDTO = {
|
||||
leagueId: 'new-league-id',
|
||||
success: true,
|
||||
};
|
||||
mockApiClient.create.mockResolvedValue(mockDto);
|
||||
|
||||
await service.createLeague(input); // This should block the submitBlocker
|
||||
|
||||
// Reset mock to check calls
|
||||
mockApiClient.create.mockClear();
|
||||
|
||||
// Second call should not call API
|
||||
await service.createLeague(input);
|
||||
expect(mockApiClient.create).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not call apiClient.create when throttle is active', async () => {
|
||||
const input: CreateLeagueInputDTO = {
|
||||
name: 'New League',
|
||||
description: 'A new league',
|
||||
};
|
||||
|
||||
// First call
|
||||
const mockDto: CreateLeagueOutputDTO = {
|
||||
leagueId: 'new-league-id',
|
||||
success: true,
|
||||
};
|
||||
mockApiClient.create.mockResolvedValue(mockDto);
|
||||
|
||||
await service.createLeague(input); // This blocks throttle for 500ms
|
||||
|
||||
// Reset mock
|
||||
mockApiClient.create.mockClear();
|
||||
|
||||
// Immediate second call should not call API due to throttle
|
||||
await service.createLeague(input);
|
||||
expect(mockApiClient.create).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('removeMember', () => {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { LeagueStandingsViewModel } from "@/lib/view-models/LeagueStandingsViewM
|
||||
import { LeagueStatsViewModel } from "@/lib/view-models/LeagueStatsViewModel";
|
||||
import { LeagueSummaryViewModel } from "@/lib/view-models/LeagueSummaryViewModel";
|
||||
import { RemoveMemberViewModel } from "@/lib/view-models/RemoveMemberViewModel";
|
||||
import { SubmitBlocker, ThrottleBlocker } from "@/lib/blockers";
|
||||
|
||||
|
||||
/**
|
||||
@@ -17,6 +18,9 @@ import { RemoveMemberViewModel } from "@/lib/view-models/RemoveMemberViewModel";
|
||||
* All dependencies are injected via constructor.
|
||||
*/
|
||||
export class LeagueService {
|
||||
private readonly submitBlocker = new SubmitBlocker();
|
||||
private readonly throttle = new ThrottleBlocker(500);
|
||||
|
||||
constructor(
|
||||
private readonly apiClient: LeaguesApiClient
|
||||
) {}
|
||||
@@ -68,12 +72,19 @@ export class LeagueService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new league
|
||||
*/
|
||||
async createLeague(input: CreateLeagueInputDTO): Promise<CreateLeagueViewModel> {
|
||||
const dto = await this.apiClient.create(input);
|
||||
return new CreateLeagueViewModel(dto);
|
||||
}
|
||||
* Create a new league
|
||||
*/
|
||||
async createLeague(input: CreateLeagueInputDTO): Promise<void> {
|
||||
if (!this.submitBlocker.canExecute() || !this.throttle.canExecute()) return;
|
||||
|
||||
this.submitBlocker.block();
|
||||
this.throttle.block();
|
||||
try {
|
||||
await this.apiClient.create(input);
|
||||
} finally {
|
||||
this.submitBlocker.release();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a member from league
|
||||
|
||||
Reference in New Issue
Block a user