/** * 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(); }); }); });