305 lines
12 KiB
TypeScript
305 lines
12 KiB
TypeScript
/**
|
|
* Integration Test: League Members Data Flow
|
|
*
|
|
* Tests the complete data flow from database to API response for league members:
|
|
* 1. Database query returns correct data
|
|
* 2. Use case processes the data correctly
|
|
* 3. Presenter transforms data to DTOs
|
|
* 4. API returns correct response
|
|
*/
|
|
|
|
import { describe, it, expect, beforeAll, afterAll, beforeEach } from 'vitest';
|
|
import { IntegrationTestHarness, createTestHarness } from '../harness';
|
|
|
|
describe('League Members - Data Flow Integration', () => {
|
|
let harness: IntegrationTestHarness;
|
|
|
|
beforeAll(async () => {
|
|
harness = createTestHarness();
|
|
await harness.beforeAll();
|
|
}, 120000);
|
|
|
|
afterAll(async () => {
|
|
await harness.afterAll();
|
|
}, 30000);
|
|
|
|
beforeEach(async () => {
|
|
await harness.beforeEach();
|
|
});
|
|
|
|
describe('API to View Data Flow', () => {
|
|
it('should return correct members DTO structure from API', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Members Test League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
// Create drivers
|
|
const drivers = await Promise.all([
|
|
factory.createDriver({ name: 'Owner Driver', country: 'US' }),
|
|
factory.createDriver({ name: 'Admin Driver', country: 'UK' }),
|
|
factory.createDriver({ name: 'Member Driver', country: 'CA' }),
|
|
]);
|
|
|
|
// Create league memberships (simulated via database)
|
|
// Note: In real implementation, memberships would be created through the domain
|
|
// For this test, we'll verify the API response structure
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Verify: API response structure
|
|
expect(response).toBeDefined();
|
|
expect(response.memberships).toBeDefined();
|
|
expect(Array.isArray(response.memberships)).toBe(true);
|
|
|
|
// Verify: Each membership has correct DTO structure
|
|
for (const membership of response.memberships) {
|
|
expect(membership).toHaveProperty('driverId');
|
|
expect(membership).toHaveProperty('driver');
|
|
expect(membership).toHaveProperty('role');
|
|
expect(membership).toHaveProperty('status');
|
|
expect(membership).toHaveProperty('joinedAt');
|
|
|
|
// Verify driver DTO structure
|
|
expect(membership.driver).toHaveProperty('id');
|
|
expect(membership.driver).toHaveProperty('iracingId');
|
|
expect(membership.driver).toHaveProperty('name');
|
|
expect(membership.driver).toHaveProperty('country');
|
|
expect(membership.driver).toHaveProperty('joinedAt');
|
|
}
|
|
});
|
|
|
|
it('should return empty members for league with no members', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Empty Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
expect(response.memberships).toEqual([]);
|
|
});
|
|
|
|
it('should handle league with single member', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Single Member League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const driver = await factory.createDriver({ name: 'Solo Member', country: 'US' });
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Should have at least the owner
|
|
expect(response.memberships.length).toBeGreaterThan(0);
|
|
|
|
const soloMember = response.memberships.find(m => m.driver.name === 'Solo Member');
|
|
expect(soloMember).toBeDefined();
|
|
expect(soloMember?.role).toBeDefined();
|
|
expect(soloMember?.status).toBeDefined();
|
|
});
|
|
});
|
|
|
|
describe('End-to-End Data Flow', () => {
|
|
it('should correctly transform member data to DTO', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Transformation Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const drivers = await Promise.all([
|
|
factory.createDriver({ name: 'Owner', country: 'US', iracingId: '1001' }),
|
|
factory.createDriver({ name: 'Admin', country: 'UK', iracingId: '1002' }),
|
|
factory.createDriver({ name: 'Member', country: 'CA', iracingId: '1003' }),
|
|
]);
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Verify all drivers are in the response
|
|
expect(response.memberships.length).toBeGreaterThanOrEqual(3);
|
|
|
|
// Verify each driver has correct data
|
|
for (const driver of drivers) {
|
|
const membership = response.memberships.find(m => m.driver.name === driver.name.toString());
|
|
expect(membership).toBeDefined();
|
|
expect(membership?.driver.id).toBe(driver.id.toString());
|
|
expect(membership?.driver.iracingId).toBe(driver.iracingId);
|
|
expect(membership?.driver.country).toBe(driver.country);
|
|
}
|
|
});
|
|
|
|
it('should handle league with many members', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Many Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
// Create 15 drivers
|
|
const drivers = await Promise.all(
|
|
Array.from({ length: 15 }, (_, i) =>
|
|
factory.createDriver({ name: `Member ${i + 1}`, iracingId: `${2000 + i}` })
|
|
)
|
|
);
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Should have all drivers
|
|
expect(response.memberships.length).toBeGreaterThanOrEqual(15);
|
|
|
|
// All memberships should have correct structure
|
|
for (const membership of response.memberships) {
|
|
expect(membership).toHaveProperty('driverId');
|
|
expect(membership).toHaveProperty('driver');
|
|
expect(membership).toHaveProperty('role');
|
|
expect(membership).toHaveProperty('status');
|
|
expect(membership).toHaveProperty('joinedAt');
|
|
}
|
|
});
|
|
|
|
it('should handle members with different roles', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Roles League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const drivers = await Promise.all([
|
|
factory.createDriver({ name: 'Owner', country: 'US' }),
|
|
factory.createDriver({ name: 'Admin', country: 'UK' }),
|
|
factory.createDriver({ name: 'Member', country: 'CA' }),
|
|
]);
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Should have members with different roles
|
|
const roles = response.memberships.map(m => m.role);
|
|
expect(roles.length).toBeGreaterThan(0);
|
|
|
|
// Verify roles are present
|
|
const hasOwner = roles.some(r => r === 'owner' || r === 'OWNER');
|
|
const hasAdmin = roles.some(r => r === 'admin' || r === 'ADMIN');
|
|
const hasMember = roles.some(r => r === 'member' || r === 'MEMBER');
|
|
|
|
// At least owner should exist
|
|
expect(hasOwner || hasAdmin || hasMember).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Data Consistency', () => {
|
|
it('should maintain data consistency across multiple API calls', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Consistency Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const driver = await factory.createDriver({ name: 'Consistent Member', country: 'DE' });
|
|
|
|
const api = harness.getApi();
|
|
|
|
// Make multiple calls
|
|
const response1 = await api.get(`/leagues/${league.id}/memberships`);
|
|
const response2 = await api.get(`/leagues/${league.id}/memberships`);
|
|
const response3 = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// All responses should be identical
|
|
expect(response1).toEqual(response2);
|
|
expect(response2).toEqual(response3);
|
|
|
|
// Verify data integrity
|
|
expect(response1.memberships.length).toBeGreaterThan(0);
|
|
|
|
const consistentMember = response1.memberships.find(m => m.driver.name === 'Consistent Member');
|
|
expect(consistentMember).toBeDefined();
|
|
expect(consistentMember?.driver.country).toBe('DE');
|
|
});
|
|
|
|
it('should handle edge case: league with many members and complex data', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Complex Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
// Create 20 drivers
|
|
const drivers = await Promise.all(
|
|
Array.from({ length: 20 }, (_, i) =>
|
|
factory.createDriver({
|
|
name: `Complex Member ${i + 1}`,
|
|
iracingId: `${3000 + i}`,
|
|
country: ['US', 'UK', 'CA', 'DE', 'FR'][i % 5]
|
|
})
|
|
)
|
|
);
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Should have all drivers
|
|
expect(response.memberships.length).toBeGreaterThanOrEqual(20);
|
|
|
|
// All memberships should have correct structure
|
|
for (const membership of response.memberships) {
|
|
expect(membership).toHaveProperty('driverId');
|
|
expect(membership).toHaveProperty('driver');
|
|
expect(membership).toHaveProperty('role');
|
|
expect(membership).toHaveProperty('status');
|
|
expect(membership).toHaveProperty('joinedAt');
|
|
|
|
// Verify driver has all required fields
|
|
expect(membership.driver).toHaveProperty('id');
|
|
expect(membership.driver).toHaveProperty('iracingId');
|
|
expect(membership.driver).toHaveProperty('name');
|
|
expect(membership.driver).toHaveProperty('country');
|
|
expect(membership.driver).toHaveProperty('joinedAt');
|
|
}
|
|
|
|
// Verify all drivers are present
|
|
const driverNames = response.memberships.map(m => m.driver.name);
|
|
for (const driver of drivers) {
|
|
expect(driverNames).toContain(driver.name.toString());
|
|
}
|
|
});
|
|
|
|
it('should handle edge case: members with optional fields', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'Optional Fields League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
// Create driver without bio (should be optional)
|
|
const driver = await factory.createDriver({ name: 'Test Member', country: 'US' });
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
expect(response.memberships.length).toBeGreaterThan(0);
|
|
|
|
const testMember = response.memberships.find(m => m.driver.name === 'Test Member');
|
|
expect(testMember).toBeDefined();
|
|
expect(testMember?.driver.bio).toBeUndefined(); // Optional field
|
|
expect(testMember?.driver.name).toBe('Test Member');
|
|
expect(testMember?.driver.country).toBe('US');
|
|
});
|
|
|
|
it('should handle edge case: league with no completed races but has members', async () => {
|
|
const factory = harness.getFactory();
|
|
const league = await factory.createLeague({ name: 'No Races Members League' });
|
|
const season = await factory.createSeason(league.id.toString());
|
|
|
|
const driver = await factory.createDriver({ name: 'Waiting Member', country: 'US' });
|
|
|
|
// Create only scheduled races (no completed races)
|
|
const race = await factory.createRace({
|
|
leagueId: league.id.toString(),
|
|
track: 'Future Track',
|
|
car: 'Future Car',
|
|
scheduledAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
|
|
status: 'scheduled'
|
|
});
|
|
|
|
const api = harness.getApi();
|
|
const response = await api.get(`/leagues/${league.id}/memberships`);
|
|
|
|
// Should still have members even with no completed races
|
|
expect(response.memberships.length).toBeGreaterThan(0);
|
|
|
|
const waitingMember = response.memberships.find(m => m.driver.name === 'Waiting Member');
|
|
expect(waitingMember).toBeDefined();
|
|
});
|
|
});
|
|
}); |