import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; import { RatingTestContext } from './RatingTestContext'; import { CalculateTeamContributionUseCase } from '../../../../core/rating/application/use-cases/CalculateTeamContributionUseCase'; import { GetTeamRatingUseCase } from '../../../../core/rating/application/use-cases/GetTeamRatingUseCase'; import { GetTeamContributionBreakdownUseCase } from '../../../../core/rating/application/use-cases/GetTeamContributionBreakdownUseCase'; import { Driver } from '../../../../core/racing/domain/entities/Driver'; import { Team } from '../../../../core/team/domain/entities/Team'; import { Rating } from '../../../../core/rating/domain/entities/Rating'; describe('Rating Team Contribution Use Cases', () => { let context: RatingTestContext; let calculateTeamContributionUseCase: CalculateTeamContributionUseCase; let getTeamRatingUseCase: GetTeamRatingUseCase; let getTeamContributionBreakdownUseCase: GetTeamContributionBreakdownUseCase; beforeAll(() => { context = RatingTestContext.create(); calculateTeamContributionUseCase = new CalculateTeamContributionUseCase( context.driverRepository, context.ratingRepository, context.eventPublisher ); getTeamRatingUseCase = new GetTeamRatingUseCase( context.driverRepository, context.ratingRepository ); getTeamContributionBreakdownUseCase = new GetTeamContributionBreakdownUseCase( context.driverRepository, context.ratingRepository ); }); beforeEach(async () => { await context.clear(); }); describe('CalculateTeamContributionUseCase', () => { describe('UseCase - Success Path', () => { it('should calculate team contribution for single driver', async () => { // Given: A driver with rating const driverId = 'd1'; const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' }); await context.driverRepository.create(driver); const rating = Rating.create({ driverId, rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }); await context.ratingRepository.save(rating); // When: CalculateTeamContributionUseCase.execute() is called const result = await calculateTeamContributionUseCase.execute({ driverId, teamId: 't1' }); // Then: The team contribution should be calculated expect(result.isOk()).toBe(true); const contribution = result.unwrap(); expect(contribution.driverId.toString()).toBe(driverId); expect(contribution.teamId.toString()).toBe('t1'); expect(contribution.contributionScore).toBeGreaterThan(0); expect(contribution.contributionPercentage).toBeGreaterThan(0); expect(contribution.contributionPercentage).toBeLessThanOrEqual(100); }); it('should calculate team contribution for multiple drivers', async () => { // Given: Multiple drivers with ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }), Driver.create({ id: 'd3', iracingId: '102', name: 'Bob Johnson', country: 'CA' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // Given: Ratings for each driver const ratings = [ Rating.create({ driverId: 'd1', rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }), Rating.create({ driverId: 'd2', rating: 1600, components: { resultsStrength: 85, consistency: 80, cleanDriving: 92, racecraft: 88, reliability: 96, teamContribution: 75 }, timestamp: new Date() }), Rating.create({ driverId: 'd3', rating: 1400, components: { resultsStrength: 75, consistency: 70, cleanDriving: 88, racecraft: 82, reliability: 93, teamContribution: 65 }, timestamp: new Date() }) ]; for (const rating of ratings) { await context.ratingRepository.save(rating); } // When: CalculateTeamContributionUseCase.execute() is called for each driver const contributions = []; for (const driver of drivers) { const result = await calculateTeamContributionUseCase.execute({ driverId: driver.id.toString(), teamId: 't1' }); expect(result.isOk()).toBe(true); contributions.push(result.unwrap()); } // Then: The team contributions should be calculated expect(contributions).toHaveLength(3); expect(contributions[0].contributionScore).toBeGreaterThan(0); expect(contributions[1].contributionScore).toBeGreaterThan(0); expect(contributions[2].contributionScore).toBeGreaterThan(0); }); }); describe('UseCase - Error Handling', () => { it('should handle missing driver', async () => { // Given: A non-existent driver const driverId = 'd999'; // When: CalculateTeamContributionUseCase.execute() is called const result = await calculateTeamContributionUseCase.execute({ driverId, teamId: 't1' }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); it('should handle driver with no rating', async () => { // Given: A driver with no rating const driverId = 'd1'; const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' }); await context.driverRepository.create(driver); // When: CalculateTeamContributionUseCase.execute() is called const result = await calculateTeamContributionUseCase.execute({ driverId, teamId: 't1' }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); }); }); describe('GetTeamRatingUseCase', () => { describe('UseCase - Success Path', () => { it('should retrieve team rating from single driver', async () => { // Given: A driver with rating const driverId = 'd1'; const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' }); await context.driverRepository.create(driver); const rating = Rating.create({ driverId, rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }); await context.ratingRepository.save(rating); // When: GetTeamRatingUseCase.execute() is called const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: [driverId] }); // Then: The team rating should be retrieved expect(result.isOk()).toBe(true); const teamRating = result.unwrap(); expect(teamRating.teamId.toString()).toBe('t1'); expect(teamRating.teamRating).toBe(1500); expect(teamRating.driverRatings).toHaveLength(1); expect(teamRating.driverRatings[0].driverId.toString()).toBe(driverId); expect(teamRating.driverRatings[0].rating).toBe(1500); }); it('should retrieve team rating from multiple drivers', async () => { // Given: Multiple drivers with ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }), Driver.create({ id: 'd3', iracingId: '102', name: 'Bob Johnson', country: 'CA' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // Given: Ratings for each driver const ratings = [ Rating.create({ driverId: 'd1', rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }), Rating.create({ driverId: 'd2', rating: 1600, components: { resultsStrength: 85, consistency: 80, cleanDriving: 92, racecraft: 88, reliability: 96, teamContribution: 75 }, timestamp: new Date() }), Rating.create({ driverId: 'd3', rating: 1400, components: { resultsStrength: 75, consistency: 70, cleanDriving: 88, racecraft: 82, reliability: 93, teamContribution: 65 }, timestamp: new Date() }) ]; for (const rating of ratings) { await context.ratingRepository.save(rating); } // When: GetTeamRatingUseCase.execute() is called const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2', 'd3'] }); // Then: The team rating should be retrieved expect(result.isOk()).toBe(true); const teamRating = result.unwrap(); expect(teamRating.teamId.toString()).toBe('t1'); expect(teamRating.teamRating).toBeGreaterThan(0); expect(teamRating.driverRatings).toHaveLength(3); expect(teamRating.driverRatings[0].rating).toBe(1500); expect(teamRating.driverRatings[1].rating).toBe(1600); expect(teamRating.driverRatings[2].rating).toBe(1400); }); it('should calculate team rating as average of driver ratings', async () => { // Given: Multiple drivers with ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // Given: Ratings for each driver const ratings = [ Rating.create({ driverId: 'd1', rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }), Rating.create({ driverId: 'd2', rating: 1700, components: { resultsStrength: 90, consistency: 85, cleanDriving: 95, racecraft: 90, reliability: 98, teamContribution: 80 }, timestamp: new Date() }) ]; for (const rating of ratings) { await context.ratingRepository.save(rating); } // When: GetTeamRatingUseCase.execute() is called const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2'] }); // Then: The team rating should be the average expect(result.isOk()).toBe(true); const teamRating = result.unwrap(); expect(teamRating.teamRating).toBe(1600); // (1500 + 1700) / 2 }); }); describe('UseCase - Error Handling', () => { it('should handle missing drivers', async () => { // Given: Non-existent drivers // When: GetTeamRatingUseCase.execute() is called const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: ['d999', 'd998'] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); it('should handle drivers with no ratings', async () => { // Given: Drivers with no ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // When: GetTeamRatingUseCase.execute() is called const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2'] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); it('should handle empty driver list', async () => { // When: GetTeamRatingUseCase.execute() is called with empty list const result = await getTeamRatingUseCase.execute({ teamId: 't1', driverIds: [] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); }); }); describe('GetTeamContributionBreakdownUseCase', () => { describe('UseCase - Success Path', () => { it('should retrieve contribution breakdown for single driver', async () => { // Given: A driver with rating const driverId = 'd1'; const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' }); await context.driverRepository.create(driver); const rating = Rating.create({ driverId, rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }); await context.ratingRepository.save(rating); // When: GetTeamContributionBreakdownUseCase.execute() is called const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: [driverId] }); // Then: The contribution breakdown should be retrieved expect(result.isOk()).toBe(true); const breakdown = result.unwrap(); expect(breakdown.teamId.toString()).toBe('t1'); expect(breakdown.breakdown).toHaveLength(1); expect(breakdown.breakdown[0].driverId.toString()).toBe(driverId); expect(breakdown.breakdown[0].rating).toBe(1500); expect(breakdown.breakdown[0].contributionScore).toBeGreaterThan(0); expect(breakdown.breakdown[0].contributionPercentage).toBeGreaterThan(0); expect(breakdown.breakdown[0].contributionPercentage).toBeLessThanOrEqual(100); }); it('should retrieve contribution breakdown for multiple drivers', async () => { // Given: Multiple drivers with ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }), Driver.create({ id: 'd3', iracingId: '102', name: 'Bob Johnson', country: 'CA' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // Given: Ratings for each driver const ratings = [ Rating.create({ driverId: 'd1', rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }), Rating.create({ driverId: 'd2', rating: 1600, components: { resultsStrength: 85, consistency: 80, cleanDriving: 92, racecraft: 88, reliability: 96, teamContribution: 75 }, timestamp: new Date() }), Rating.create({ driverId: 'd3', rating: 1400, components: { resultsStrength: 75, consistency: 70, cleanDriving: 88, racecraft: 82, reliability: 93, teamContribution: 65 }, timestamp: new Date() }) ]; for (const rating of ratings) { await context.ratingRepository.save(rating); } // When: GetTeamContributionBreakdownUseCase.execute() is called const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2', 'd3'] }); // Then: The contribution breakdown should be retrieved expect(result.isOk()).toBe(true); const breakdown = result.unwrap(); expect(breakdown.teamId.toString()).toBe('t1'); expect(breakdown.breakdown).toHaveLength(3); expect(breakdown.breakdown[0].driverId.toString()).toBe('d1'); expect(breakdown.breakdown[1].driverId.toString()).toBe('d2'); expect(breakdown.breakdown[2].driverId.toString()).toBe('d3'); expect(breakdown.breakdown[0].contributionPercentage).toBeGreaterThan(0); expect(breakdown.breakdown[1].contributionPercentage).toBeGreaterThan(0); expect(breakdown.breakdown[2].contributionPercentage).toBeGreaterThan(0); }); it('should calculate contribution percentages correctly', async () => { // Given: Multiple drivers with different ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // Given: Ratings for each driver const ratings = [ Rating.create({ driverId: 'd1', rating: 1500, components: { resultsStrength: 80, consistency: 75, cleanDriving: 90, racecraft: 85, reliability: 95, teamContribution: 70 }, timestamp: new Date() }), Rating.create({ driverId: 'd2', rating: 1700, components: { resultsStrength: 90, consistency: 85, cleanDriving: 95, racecraft: 90, reliability: 98, teamContribution: 80 }, timestamp: new Date() }) ]; for (const rating of ratings) { await context.ratingRepository.save(rating); } // When: GetTeamContributionBreakdownUseCase.execute() is called const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2'] }); // Then: The contribution percentages should be calculated correctly expect(result.isOk()).toBe(true); const breakdown = result.unwrap(); expect(breakdown.breakdown).toHaveLength(2); expect(breakdown.breakdown[0].contributionPercentage + breakdown.breakdown[1].contributionPercentage).toBe(100); }); }); describe('UseCase - Error Handling', () => { it('should handle missing drivers', async () => { // Given: Non-existent drivers // When: GetTeamContributionBreakdownUseCase.execute() is called const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: ['d999', 'd998'] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); it('should handle drivers with no ratings', async () => { // Given: Drivers with no ratings const drivers = [ Driver.create({ id: 'd1', iracingId: '100', name: 'John Doe', country: 'US' }), Driver.create({ id: 'd2', iracingId: '101', name: 'Jane Smith', country: 'UK' }) ]; for (const driver of drivers) { await context.driverRepository.create(driver); } // When: GetTeamContributionBreakdownUseCase.execute() is called const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: ['d1', 'd2'] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); it('should handle empty driver list', async () => { // When: GetTeamContributionBreakdownUseCase.execute() is called with empty list const result = await getTeamContributionBreakdownUseCase.execute({ teamId: 't1', driverIds: [] }); // Then: The result should be an error expect(result.isErr()).toBe(true); }); }); }); });