website refactor

This commit is contained in:
2026-01-16 18:21:06 +01:00
parent 2f53727702
commit 095885544b
146 changed files with 970 additions and 524 deletions

View File

@@ -1,5 +1,4 @@
import type { DomainError } from '@core/shared/errors/DomainError';
import type { CommonDomainErrorKind } from '@core/shared/errors/DomainError';
import type { DomainError, CommonDomainErrorKind } from '@core/shared/errors/DomainError';
export abstract class IdentityDomainError extends Error implements DomainError<CommonDomainErrorKind> {
readonly type = 'domain' as const;

View File

@@ -1,3 +1,4 @@
import { describe, it, expect, beforeEach } from 'vitest';
import { ExternalGameRatingRepository } from './ExternalGameRatingRepository';
import { ExternalGameRatingProfile } from '../entities/ExternalGameRatingProfile';
import { UserId } from '../value-objects/UserId';
@@ -182,11 +183,6 @@ describe('ExternalGameRatingRepository', () => {
await repository.save(profile);
// Update the profile
const updatedProvenance = ExternalRatingProvenance.create({
source: 'iracing',
lastSyncedAt: new Date('2024-01-02'),
verified: true,
});
profile.updateLastSyncedAt(new Date('2024-01-02'));
profile.markVerified();
@@ -290,7 +286,7 @@ describe('ExternalGameRatingRepository', () => {
lastSyncedAt: new Date('2024-01-01'),
verified: false,
});
profile2.updateRatings(profile2.ratings, profile2Provenance);
profile2.updateRatings(new Map(profile2.ratings), profile2Provenance);
await repository.saveMany([profile1, profile2]);

View File

@@ -3,7 +3,6 @@ import { RatingEvent } from '../entities/RatingEvent';
import { RatingEventId } from '../value-objects/RatingEventId';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import { RatingDelta } from '../value-objects/RatingDelta';
import { AdminVoteOutcome } from '../entities/AdminVoteSession';
describe('AdminTrustRatingCalculator', () => {
describe('calculate', () => {
@@ -316,13 +315,12 @@ describe('AdminTrustRatingCalculator', () => {
it('should default to zero for unknown action type', () => {
const input: SystemSignalInput = {
actionType: 'sla_response' as any,
actionType: 'unknown_type' as unknown as SystemSignalInput['actionType'],
details: {},
};
// Override for test
const delta = AdminTrustRatingCalculator.calculateFromSystemSignal(input);
expect(delta.value).toBe(5); // Known type
expect(delta.value).toBe(0);
});
});

View File

@@ -1,6 +1,4 @@
import { RatingEvent } from '../entities/RatingEvent';
import { DrivingReasonCode } from '../value-objects/DrivingReasonCode';
import { RatingDelta } from '../value-objects/RatingDelta';
/**
* Input DTO for driving rating calculation from race facts
@@ -71,7 +69,7 @@ export class DrivingRatingCalculator {
const fieldStrength = facts.results.length > 0
? (facts.results
.filter(r => r.status === 'finished')
.reduce((sum, r) => sum + (r.sof || this.estimateDriverRating(r.userId)), 0) /
.reduce((sum, r) => sum + (r.sof || this.estimateDriverRating()), 0) /
Math.max(1, facts.results.filter(r => r.status === 'finished').length))
: 0;
@@ -297,7 +295,7 @@ export class DrivingRatingCalculator {
* Estimate driver rating for SoF calculation
* This is a placeholder - in real implementation, would query user rating snapshot
*/
private static estimateDriverRating(userId: string): number {
private static estimateDriverRating(): number {
// Default rating for new drivers
return 50;
}

View File

@@ -3,7 +3,7 @@
*/
import { EligibilityEvaluator, RatingData } from './EligibilityEvaluator';
import { EligibilityFilterDto } from '../../application/dtos/EligibilityFilterDto';
import { EligibilityFilterDto } from '../types/Eligibility';
describe('EligibilityEvaluator', () => {
let evaluator: EligibilityEvaluator;

View File

@@ -6,8 +6,13 @@
* Provides explainable results with detailed reasons.
*/
import { EvaluationResultDto, EvaluationReason } from '../../application/dtos/EvaluationResultDto';
import { EligibilityFilterDto, ParsedEligibilityFilter, EligibilityCondition } from '../../application/dtos/EligibilityFilterDto';
import {
EvaluationResultDto,
EvaluationReason,
EligibilityFilterDto,
ParsedEligibilityFilter,
EligibilityCondition
} from '../types/Eligibility';
export interface RatingData {
platform: {

View File

@@ -1,4 +1,4 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { RatingUpdateService } from './RatingUpdateService';
import type { UserRatingRepository } from '../repositories/UserRatingRepository';
import type { RatingEventRepository } from '../repositories/RatingEventRepository';
@@ -10,8 +10,8 @@ import { RatingDelta } from '../value-objects/RatingDelta';
describe('RatingUpdateService - Slice 7 Evolution', () => {
let service: RatingUpdateService;
let userRatingRepository: any;
let ratingEventRepository: any;
let userRatingRepository: { findByUserId: Mock; save: Mock };
let ratingEventRepository: { save: Mock; getAllByUserId: Mock };
beforeEach(() => {
userRatingRepository = {
@@ -24,7 +24,10 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
getAllByUserId: vi.fn(),
};
service = new RatingUpdateService(userRatingRepository, ratingEventRepository);
service = new RatingUpdateService(
userRatingRepository as unknown as UserRatingRepository,
ratingEventRepository as unknown as RatingEventRepository
);
});
describe('recordRaceRatingEvents - Ledger-based approach', () => {
@@ -150,7 +153,7 @@ describe('RatingUpdateService - Slice 7 Evolution', () => {
// Verify DNF penalty event was created
const savedEvents = ratingEventRepository.save.mock.calls.map(call => call[0]);
const hasDnfPenalty = savedEvents.some((event: any) =>
const hasDnfPenalty = savedEvents.some((event: RatingEvent) =>
event.reason.code === 'DRIVING_DNF_PENALTY'
);
expect(hasDnfPenalty).toBe(true);

View File

@@ -0,0 +1,55 @@
/**
* Domain Types: Eligibility
*
* DSL-based eligibility filter and evaluation results.
*/
export interface EligibilityFilterDto {
/**
* DSL expression for eligibility rules
*/
dsl: string;
/**
* Optional context for evaluation
*/
context?: {
userId?: string;
leagueId?: string;
[key: string]: unknown;
};
}
export interface EligibilityCondition {
target: 'platform' | 'external';
dimension?: string;
game?: string;
operator: string;
expected: number | [number, number];
}
export interface ParsedEligibilityFilter {
conditions: EligibilityCondition[];
logicalOperator: 'AND' | 'OR';
}
export interface EvaluationReason {
target: string;
operator: string;
expected: number | [number, number];
actual: number;
failed: boolean;
message?: string;
}
export interface EvaluationResultDto {
eligible: boolean;
reasons: EvaluationReason[];
summary: string;
evaluatedAt: string;
metadata?: {
userId?: string;
filter?: string;
[key: string]: unknown;
};
}

View File

@@ -1,5 +1,6 @@
import { AdminTrustReasonCode } from './AdminTrustReasonCode';
import { AdminTrustReasonCode, type AdminTrustReasonCodeValue } from './AdminTrustReasonCode';
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { describe, it, expect } from 'vitest';
describe('AdminTrustReasonCode', () => {
describe('create', () => {
@@ -71,7 +72,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_VOTE_OUTCOME_NEGATIVE',
];
voteCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isVoteOutcome()).toBe(true);
});
});
@@ -82,7 +83,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_REVERSAL_PENALTY',
];
nonVoteCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isVoteOutcome()).toBe(false);
});
});
@@ -97,7 +98,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
];
systemCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isSystemSignal()).toBe(true);
});
});
@@ -108,7 +109,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_VOTE_OUTCOME_NEGATIVE',
];
nonSystemCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isSystemSignal()).toBe(false);
});
});
@@ -122,7 +123,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_RULE_CLARITY_BONUS',
];
positiveCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isPositive()).toBe(true);
});
});
@@ -134,7 +135,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
];
nonPositiveCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isPositive()).toBe(false);
});
});
@@ -148,7 +149,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_ABUSE_REPORT_PENALTY',
];
negativeCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isNegative()).toBe(true);
});
});
@@ -160,7 +161,7 @@ describe('AdminTrustReasonCode', () => {
'ADMIN_ACTION_RULE_CLARITY_BONUS',
];
nonNegativeCodes.forEach(codeStr => {
const code = AdminTrustReasonCode.fromValue(codeStr as any);
const code = AdminTrustReasonCode.fromValue(codeStr as AdminTrustReasonCodeValue);
expect(code.isNegative()).toBe(false);
});
});

View File

@@ -1,5 +1,6 @@
import { DrivingReasonCode } from './DrivingReasonCode';
import { DrivingReasonCode, type DrivingReasonCodeValue } from './DrivingReasonCode';
import { IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { describe, it, expect } from 'vitest';
describe('DrivingReasonCode', () => {
describe('create', () => {
@@ -77,7 +78,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_PACE_RELATIVE_GAIN',
];
performanceCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isPerformance()).toBe(true);
});
});
@@ -89,7 +90,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_SEASON_ATTENDANCE_BONUS',
];
nonPerformanceCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isPerformance()).toBe(false);
});
});
@@ -103,7 +104,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_PENALTY_INVOLVEMENT_PENALTY',
];
cleanDrivingCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isCleanDriving()).toBe(true);
});
});
@@ -115,7 +116,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_SEASON_ATTENDANCE_BONUS',
];
nonCleanDrivingCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isCleanDriving()).toBe(false);
});
});
@@ -131,7 +132,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_SEASON_ATTENDANCE_BONUS',
];
reliabilityCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isReliability()).toBe(true);
});
});
@@ -142,7 +143,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_INCIDENTS_PENALTY',
];
nonReliabilityCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isReliability()).toBe(false);
});
});
@@ -160,7 +161,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_AFK_PENALTY',
];
penaltyCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isPenalty()).toBe(true);
});
});
@@ -172,7 +173,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_SEASON_ATTENDANCE_BONUS',
];
nonPenaltyCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isPenalty()).toBe(false);
});
});
@@ -187,7 +188,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_PACE_RELATIVE_GAIN',
];
bonusCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isBonus()).toBe(true);
});
});
@@ -198,7 +199,7 @@ describe('DrivingReasonCode', () => {
'DRIVING_DNS_PENALTY',
];
nonBonusCodes.forEach(codeStr => {
const code = DrivingReasonCode.fromValue(codeStr as any);
const code = DrivingReasonCode.fromValue(codeStr as DrivingReasonCodeValue);
expect(code.isBonus()).toBe(false);
});
});