Some checks failed
CI / lint-typecheck (pull_request) Failing after 4m51s
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
107 lines
3.2 KiB
TypeScript
107 lines
3.2 KiB
TypeScript
import { describe, it, expect, beforeEach } from 'vitest';
|
|
import { DatabaseTestContext, DriverData } from '../DatabaseTestContext';
|
|
|
|
describe('Database Constraints - Concurrent Operations', () => {
|
|
let context: DatabaseTestContext;
|
|
|
|
beforeEach(() => {
|
|
context = DatabaseTestContext.create();
|
|
});
|
|
|
|
it('should handle concurrent team creation attempts safely', async () => {
|
|
// Given: Multiple drivers exist
|
|
const drivers: DriverData[] = await Promise.all(
|
|
Array(5).fill(null).map((_, i) => {
|
|
const driver = {
|
|
id: `driver-${i}`,
|
|
iracingId: `iracing-${i}`,
|
|
name: `Test Driver ${i}`,
|
|
country: 'US',
|
|
joinedAt: new Date(),
|
|
};
|
|
return context.driverRepository.create(driver);
|
|
})
|
|
);
|
|
|
|
// When: Multiple concurrent attempts to create teams with same name
|
|
// We use a small delay to ensure they don't all get the same timestamp
|
|
// if the implementation uses Date.now() for IDs
|
|
const concurrentRequests = drivers.map(async (driver, i) => {
|
|
await new Promise(resolve => setTimeout(resolve, i * 10));
|
|
return context.createTeamUseCase.execute({
|
|
name: 'Concurrent Team',
|
|
tag: 'CT', // Same tag for all to trigger duplicate error
|
|
description: 'Concurrent creation',
|
|
ownerId: driver.id,
|
|
leagues: [],
|
|
});
|
|
});
|
|
|
|
const results = await Promise.all(concurrentRequests);
|
|
|
|
// Then: Exactly one should succeed, others should fail
|
|
const successes = results.filter(r => r.isOk());
|
|
const failures = results.filter(r => r.isErr());
|
|
|
|
// Note: In-memory implementation is synchronous, so concurrent requests
|
|
// actually run sequentially in this test environment.
|
|
expect(successes.length).toBe(1);
|
|
expect(failures.length).toBe(4);
|
|
|
|
// All failures should be duplicate errors
|
|
failures.forEach(result => {
|
|
if (result.isErr()) {
|
|
expect(result.error.code).toBe('DUPLICATE_TEAM');
|
|
}
|
|
});
|
|
});
|
|
|
|
it('should handle concurrent join requests safely', async () => {
|
|
// Given: A driver and team exist
|
|
const driver: DriverData = {
|
|
id: 'driver-123',
|
|
iracingId: '12345',
|
|
name: 'Test Driver',
|
|
country: 'US',
|
|
joinedAt: new Date(),
|
|
};
|
|
await context.driverRepository.create(driver);
|
|
|
|
const team = {
|
|
id: 'team-123',
|
|
name: 'Test Team',
|
|
tag: 'TT',
|
|
description: 'A test team',
|
|
ownerId: 'other-driver',
|
|
leagues: [],
|
|
isRecruiting: false,
|
|
createdAt: new Date(),
|
|
};
|
|
await context.teamRepository.create(team);
|
|
|
|
// When: Multiple concurrent join attempts
|
|
const concurrentJoins = Array(3).fill(null).map(() =>
|
|
context.joinTeamUseCase.execute({
|
|
teamId: team.id,
|
|
driverId: driver.id,
|
|
})
|
|
);
|
|
|
|
const results = await Promise.all(concurrentJoins);
|
|
|
|
// Then: Exactly one should succeed
|
|
const successes = results.filter(r => r.isOk());
|
|
const failures = results.filter(r => r.isErr());
|
|
|
|
expect(successes.length).toBe(1);
|
|
expect(failures.length).toBe(2);
|
|
|
|
// All failures should be already member errors
|
|
failures.forEach(result => {
|
|
if (result.isErr()) {
|
|
expect(result.error.code).toBe('ALREADY_MEMBER');
|
|
}
|
|
});
|
|
});
|
|
});
|