Files
gridpilot.gg/tests/integration/rating/rating-persistence-use-cases.integration.test.ts
Marc Mintel 9bb6b228f1
Some checks failed
CI / lint-typecheck (pull_request) Failing after 4m50s
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
integration tests
2026-01-23 23:46:03 +01:00

474 lines
16 KiB
TypeScript

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';
import { RatingTestContext } from './RatingTestContext';
import { SaveRatingUseCase } from '../../../../core/rating/application/use-cases/SaveRatingUseCase';
import { GetRatingUseCase } from '../../../../core/rating/application/use-cases/GetRatingUseCase';
import { GetRatingHistoryUseCase } from '../../../../core/rating/application/use-cases/GetRatingHistoryUseCase';
import { GetRatingTrendUseCase } from '../../../../core/rating/application/use-cases/GetRatingTrendUseCase';
import { Driver } from '../../../../core/racing/domain/entities/Driver';
import { Rating } from '../../../../core/rating/domain/entities/Rating';
describe('Rating Persistence Use Cases', () => {
let context: RatingTestContext;
let saveRatingUseCase: SaveRatingUseCase;
let getRatingUseCase: GetRatingUseCase;
let getRatingHistoryUseCase: GetRatingHistoryUseCase;
let getRatingTrendUseCase: GetRatingTrendUseCase;
beforeAll(() => {
context = RatingTestContext.create();
saveRatingUseCase = new SaveRatingUseCase(
context.ratingRepository,
context.eventPublisher
);
getRatingUseCase = new GetRatingUseCase(
context.ratingRepository
);
getRatingHistoryUseCase = new GetRatingHistoryUseCase(
context.ratingRepository
);
getRatingTrendUseCase = new GetRatingTrendUseCase(
context.ratingRepository
);
});
beforeEach(async () => {
await context.clear();
});
describe('SaveRatingUseCase', () => {
describe('UseCase - Success Path', () => {
it('should save rating successfully', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: A rating to save
const rating = Rating.create({
driverId,
rating: 1500,
components: {
resultsStrength: 80,
consistency: 75,
cleanDriving: 90,
racecraft: 85,
reliability: 95,
teamContribution: 70
},
timestamp: new Date()
});
// When: SaveRatingUseCase.execute() is called
const result = await saveRatingUseCase.execute({ rating });
// Then: The rating should be saved
expect(result.isOk()).toBe(true);
// And: EventPublisher should emit RatingSavedEvent
expect(context.eventPublisher.events).toContainEqual(
expect.objectContaining({ type: 'RatingSavedEvent' })
);
});
it('should update existing rating', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: An initial rating
const initialRating = Rating.create({
driverId,
rating: 1500,
components: {
resultsStrength: 80,
consistency: 75,
cleanDriving: 90,
racecraft: 85,
reliability: 95,
teamContribution: 70
},
timestamp: new Date(Date.now() - 86400000)
});
await context.ratingRepository.save(initialRating);
// Given: An updated rating
const updatedRating = Rating.create({
driverId,
rating: 1550,
components: {
resultsStrength: 85,
consistency: 80,
cleanDriving: 92,
racecraft: 88,
reliability: 96,
teamContribution: 75
},
timestamp: new Date()
});
// When: SaveRatingUseCase.execute() is called
const result = await saveRatingUseCase.execute({ rating: updatedRating });
// Then: The rating should be updated
expect(result.isOk()).toBe(true);
// And: EventPublisher should emit RatingUpdatedEvent
expect(context.eventPublisher.events).toContainEqual(
expect.objectContaining({ type: 'RatingUpdatedEvent' })
);
});
});
describe('UseCase - Error Handling', () => {
it('should handle missing driver', async () => {
// Given: A rating with non-existent driver
const rating = Rating.create({
driverId: 'd999',
rating: 1500,
components: {
resultsStrength: 80,
consistency: 75,
cleanDriving: 90,
racecraft: 85,
reliability: 95,
teamContribution: 70
},
timestamp: new Date()
});
// When: SaveRatingUseCase.execute() is called
const result = await saveRatingUseCase.execute({ rating });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
});
});
describe('GetRatingUseCase', () => {
describe('UseCase - Success Path', () => {
it('should retrieve rating for driver', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: A saved rating
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: GetRatingUseCase.execute() is called
const result = await getRatingUseCase.execute({ driverId });
// Then: The rating should be retrieved
expect(result.isOk()).toBe(true);
const retrievedRating = result.unwrap();
expect(retrievedRating.driverId.toString()).toBe(driverId);
expect(retrievedRating.rating).toBe(1500);
expect(retrievedRating.components.resultsStrength).toBe(80);
});
it('should return latest rating for driver', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: Multiple ratings for the same driver
const oldRating = Rating.create({
driverId,
rating: 1400,
components: {
resultsStrength: 70,
consistency: 65,
cleanDriving: 80,
racecraft: 75,
reliability: 85,
teamContribution: 60
},
timestamp: new Date(Date.now() - 86400000)
});
await context.ratingRepository.save(oldRating);
const newRating = 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(newRating);
// When: GetRatingUseCase.execute() is called
const result = await getRatingUseCase.execute({ driverId });
// Then: The latest rating should be retrieved
expect(result.isOk()).toBe(true);
const retrievedRating = result.unwrap();
expect(retrievedRating.driverId.toString()).toBe(driverId);
expect(retrievedRating.rating).toBe(1500);
expect(retrievedRating.timestamp.getTime()).toBe(newRating.timestamp.getTime());
});
});
describe('UseCase - Error Handling', () => {
it('should handle missing driver', async () => {
// Given: A non-existent driver
const driverId = 'd999';
// When: GetRatingUseCase.execute() is called
const result = await getRatingUseCase.execute({ driverId });
// 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: GetRatingUseCase.execute() is called
const result = await getRatingUseCase.execute({ driverId });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
});
});
describe('GetRatingHistoryUseCase', () => {
describe('UseCase - Success Path', () => {
it('should retrieve rating history for driver', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: Multiple ratings for the same driver
for (let i = 1; i <= 5; i++) {
const rating = Rating.create({
driverId,
rating: 1400 + (i * 20),
components: {
resultsStrength: 70 + (i * 2),
consistency: 65 + (i * 2),
cleanDriving: 80 + (i * 2),
racecraft: 75 + (i * 2),
reliability: 85 + (i * 2),
teamContribution: 60 + (i * 2)
},
timestamp: new Date(Date.now() - (i * 86400000))
});
await context.ratingRepository.save(rating);
}
// When: GetRatingHistoryUseCase.execute() is called
const result = await getRatingHistoryUseCase.execute({ driverId });
// Then: The rating history should be retrieved
expect(result.isOk()).toBe(true);
const history = result.unwrap();
expect(history).toHaveLength(5);
expect(history[0].rating).toBe(1500);
expect(history[4].rating).toBe(1420);
});
it('should retrieve rating history with limit', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: Multiple ratings for the same driver
for (let i = 1; i <= 10; i++) {
const rating = Rating.create({
driverId,
rating: 1400 + (i * 10),
components: {
resultsStrength: 70 + i,
consistency: 65 + i,
cleanDriving: 80 + i,
racecraft: 75 + i,
reliability: 85 + i,
teamContribution: 60 + i
},
timestamp: new Date(Date.now() - (i * 86400000))
});
await context.ratingRepository.save(rating);
}
// When: GetRatingHistoryUseCase.execute() is called with limit
const result = await getRatingHistoryUseCase.execute({ driverId, limit: 5 });
// Then: The rating history should be retrieved with limit
expect(result.isOk()).toBe(true);
const history = result.unwrap();
expect(history).toHaveLength(5);
});
});
describe('UseCase - Error Handling', () => {
it('should handle missing driver', async () => {
// Given: A non-existent driver
const driverId = 'd999';
// When: GetRatingHistoryUseCase.execute() is called
const result = await getRatingHistoryUseCase.execute({ driverId });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
it('should handle driver with no rating history', 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: GetRatingHistoryUseCase.execute() is called
const result = await getRatingHistoryUseCase.execute({ driverId });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
});
});
describe('GetRatingTrendUseCase', () => {
describe('UseCase - Success Path', () => {
it('should retrieve rating trend for driver', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: Multiple ratings for the same driver
for (let i = 1; i <= 5; i++) {
const rating = Rating.create({
driverId,
rating: 1400 + (i * 20),
components: {
resultsStrength: 70 + (i * 2),
consistency: 65 + (i * 2),
cleanDriving: 80 + (i * 2),
racecraft: 75 + (i * 2),
reliability: 85 + (i * 2),
teamContribution: 60 + (i * 2)
},
timestamp: new Date(Date.now() - (i * 86400000))
});
await context.ratingRepository.save(rating);
}
// When: GetRatingTrendUseCase.execute() is called
const result = await getRatingTrendUseCase.execute({ driverId });
// Then: The rating trend should be retrieved
expect(result.isOk()).toBe(true);
const trend = result.unwrap();
expect(trend.driverId.toString()).toBe(driverId);
expect(trend.trend).toBeDefined();
expect(trend.trend.length).toBeGreaterThan(0);
expect(trend.change).toBeGreaterThan(0);
expect(trend.changePercentage).toBeGreaterThan(0);
});
it('should calculate trend over specific period', async () => {
// Given: A driver
const driverId = 'd1';
const driver = Driver.create({ id: driverId, iracingId: '100', name: 'John Doe', country: 'US' });
await context.driverRepository.create(driver);
// Given: Multiple ratings for the same driver
for (let i = 1; i <= 10; i++) {
const rating = Rating.create({
driverId,
rating: 1400 + (i * 10),
components: {
resultsStrength: 70 + i,
consistency: 65 + i,
cleanDriving: 80 + i,
racecraft: 75 + i,
reliability: 85 + i,
teamContribution: 60 + i
},
timestamp: new Date(Date.now() - (i * 86400000))
});
await context.ratingRepository.save(rating);
}
// When: GetRatingTrendUseCase.execute() is called with period
const result = await getRatingTrendUseCase.execute({ driverId, period: 7 });
// Then: The rating trend should be retrieved for the period
expect(result.isOk()).toBe(true);
const trend = result.unwrap();
expect(trend.driverId.toString()).toBe(driverId);
expect(trend.trend.length).toBeLessThanOrEqual(7);
});
});
describe('UseCase - Error Handling', () => {
it('should handle missing driver', async () => {
// Given: A non-existent driver
const driverId = 'd999';
// When: GetRatingTrendUseCase.execute() is called
const result = await getRatingTrendUseCase.execute({ driverId });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
it('should handle driver with insufficient rating history', async () => {
// Given: A driver with only one 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: GetRatingTrendUseCase.execute() is called
const result = await getRatingTrendUseCase.execute({ driverId });
// Then: The result should be an error
expect(result.isErr()).toBe(true);
});
});
});
});