website refactor

This commit is contained in:
2026-01-16 15:20:25 +01:00
parent 7e02fc3ea5
commit 37b1aa626c
325 changed files with 2167 additions and 2782 deletions

View File

@@ -1,9 +1,9 @@
import type { Entity } from '@core/shared/domain';
import { UserId } from '../value-objects/UserId';
import type { Entity } from '@core/shared/domain/Entity';
import { AdminDomainInvariantError, AdminDomainValidationError } from '../errors/AdminDomainError';
import { Email } from '../value-objects/Email';
import { UserId } from '../value-objects/UserId';
import { UserRole } from '../value-objects/UserRole';
import { UserStatus } from '../value-objects/UserStatus';
import { AdminDomainValidationError, AdminDomainInvariantError } from '../errors/AdminDomainError';
export interface AdminUserProps {
id: UserId;

View File

@@ -1,6 +1,6 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { GetAnalyticsMetricsUseCase, type GetAnalyticsMetricsInput } from './GetAnalyticsMetricsUseCase';
import type { Logger } from '@core/shared/application';
describe('GetAnalyticsMetricsUseCase', () => {
let logger: Logger;

View File

@@ -1,6 +1,6 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { GetDashboardDataUseCase } from './GetDashboardDataUseCase';
import type { Logger } from '@core/shared/application';
describe('GetDashboardDataUseCase', () => {
let logger: Logger;

View File

@@ -1,9 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { GetEntityAnalyticsQuery, type GetEntityAnalyticsInput } from './GetEntityAnalyticsQuery';
import type { PageViewRepository } from '../repositories/PageViewRepository';
import type { EngagementRepository } from '@core/analytics/domain/repositories/EngagementRepository';
import type { Logger } from '@core/shared/domain';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { EntityType } from '../../domain/types/PageView';
import { GetEntityAnalyticsQuery, type GetEntityAnalyticsInput } from './GetEntityAnalyticsQuery';
describe('GetEntityAnalyticsQuery', () => {
let pageViewRepository: {

View File

@@ -1,9 +1,8 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { RecordEngagementUseCase, type RecordEngagementInput } from './RecordEngagementUseCase';
import type { EngagementRepository } from '../../domain/repositories/EngagementRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { EngagementEvent } from '../../domain/entities/EngagementEvent';
import type { Logger } from '@core/shared/application';
import type { EngagementAction, EngagementEntityType } from '../../domain/types/EngagementEvent';
import { RecordEngagementUseCase, type RecordEngagementInput } from './RecordEngagementUseCase';
describe('RecordEngagementUseCase', () => {
let engagementRepository: {

View File

@@ -1,8 +1,8 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { RecordPageViewUseCase, type RecordPageViewInput } from './RecordPageViewUseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { PageView } from '../../domain/entities/PageView';
import type { Logger } from '@core/shared/application';
import type { EntityType, VisitorType } from '../../domain/types/PageView';
import { RecordPageViewUseCase, type RecordPageViewInput } from './RecordPageViewUseCase';
describe('RecordPageViewUseCase', () => {
let pageViewRepository: {

View File

@@ -5,15 +5,15 @@
* Pre-calculated metrics for sponsor dashboard and entity analytics.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import type {
AnalyticsSnapshotProps,
AnalyticsMetrics,
SnapshotEntityType,
SnapshotPeriod,
AnalyticsMetrics,
AnalyticsSnapshotProps,
SnapshotEntityType,
SnapshotPeriod,
} from '../types/AnalyticsSnapshot';
export type { SnapshotEntityType, SnapshotPeriod } from '../types/AnalyticsSnapshot';
import { AnalyticsEntityId } from '../value-objects/AnalyticsEntityId';
export type { SnapshotEntityType, SnapshotPeriod } from '../types/AnalyticsSnapshot';
export class AnalyticsSnapshot implements Entity<string> {
readonly id: string;

View File

@@ -5,15 +5,15 @@
* Tracks clicks, downloads, sign-ups, and other engagement actions.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import type {
EngagementAction,
EngagementEntityType,
EngagementEventProps,
EngagementAction,
EngagementEntityType,
EngagementEventProps,
} from '../types/EngagementEvent';
import { AnalyticsEntityId } from '../value-objects/AnalyticsEntityId';
export type { EngagementAction, EngagementEntityType } from '../types/EngagementEvent';
import { AnalyticsEntityId } from '../value-objects/AnalyticsEntityId';
export class EngagementEvent implements Entity<string> {
readonly id: string;

View File

@@ -5,14 +5,14 @@
* Captures visitor interactions with leagues, drivers, teams, races.
*/
import type { Entity } from '@core/shared/domain';
import type { EntityType, VisitorType, PageViewProps } from '../types/PageView';
export type { EntityType, VisitorType } from '../types/PageView';
import type { Entity } from '@core/shared/domain/Entity';
import type { EntityType, PageViewProps, VisitorType } from '../types/PageView';
import { AnalyticsEntityId } from '../value-objects/AnalyticsEntityId';
import { AnalyticsSessionId } from '../value-objects/AnalyticsSessionId';
import { PageViewId } from '../value-objects/PageViewId';
export type { EntityType, VisitorType } from '../types/PageView';
export class PageView implements Entity<string> {
readonly entityType: EntityType;
readonly visitorId: string | undefined;

View File

@@ -1,14 +1,10 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { ForgotPasswordUseCase } from './ForgotPasswordUseCase';
import type { AuthRepository } from '../../domain/repositories/AuthRepository';
import type { MagicLinkRepository } from '../../domain/repositories/MagicLinkRepository';
import type { MagicLinkNotificationPort } from '../../domain/ports/MagicLinkNotificationPort';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { User } from '../../domain/entities/User';
import { UserId } from '../../domain/value-objects/UserId';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
import { UserId } from '../../domain/value-objects/UserId';
import { ForgotPasswordUseCase } from './ForgotPasswordUseCase';
describe('ForgotPasswordUseCase', () => {
let authRepo: {

View File

@@ -1,9 +1,8 @@
import type { Logger } from '@core/shared/domain/Logger';
import { vi, type Mock } from 'vitest';
import { GetCurrentSessionUseCase } from './GetCurrentSessionUseCase';
import { User } from '../../domain/entities/User';
import { UserRepository, StoredUser } from '../../domain/repositories/UserRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { StoredUser } from '../../domain/repositories/UserRepository';
import { GetCurrentSessionUseCase } from './GetCurrentSessionUseCase';
describe('GetCurrentSessionUseCase', () => {
let useCase: GetCurrentSessionUseCase;

View File

@@ -1,8 +1,7 @@
import { User } from '../../domain/entities/User';
import { UserRepository } from '../../domain/repositories/UserRepository';
import { Result } from '@core/shared/domain/Result';
import { Result } from '@/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import { User } from '../../domain/entities/User';
export type GetCurrentSessionInput = {
userId: string;

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { GetCurrentUserSessionUseCase } from './GetCurrentUserSessionUseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { GetCurrentUserSessionUseCase } from './GetCurrentUserSessionUseCase';
describe('GetCurrentUserSessionUseCase', () => {
let sessionPort: {

View File

@@ -1,7 +1,7 @@
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
export type GetCurrentUserSessionInput = void;

View File

@@ -1,12 +1,6 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { GetUserUseCase } from './GetUserUseCase';
import type { UserRepository } from '../../domain/repositories/UserRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { User } from '../../domain/entities/User';
import { UserId } from '../../domain/value-objects/UserId';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
describe('GetUserUseCase', () => {
let userRepo: {

View File

@@ -1,9 +1,8 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { HandleAuthCallbackUseCase } from './HandleAuthCallbackUseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { HandleAuthCallbackUseCase } from './HandleAuthCallbackUseCase';
describe('HandleAuthCallbackUseCase', () => {
let provider: {

View File

@@ -1,8 +1,8 @@
import type { AuthCallbackCommand, AuthenticatedUser, IdentityProviderPort } from '../ports/IdentityProviderPort';
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { AuthCallbackCommand, AuthenticatedUser, IdentityProviderPort } from '../ports/IdentityProviderPort';
import type { AuthSession, IdentitySessionPort } from '../ports/IdentitySessionPort';
export type HandleAuthCallbackInput = AuthCallbackCommand;

View File

@@ -1,13 +1,9 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { LoginUseCase } from './LoginUseCase';
import type { AuthRepository } from '../../domain/repositories/AuthRepository';
import type { PasswordHashingService } from '../../domain/services/PasswordHashingService';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { User } from '../../domain/entities/User';
import { UserId } from '../../domain/value-objects/UserId';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
import { UserId } from '../../domain/value-objects/UserId';
import { LoginUseCase } from './LoginUseCase';
describe('LoginUseCase', () => {
let authRepo: {

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, type Mock, beforeEach } from 'vitest';
import { LoginWithEmailUseCase } from './LoginWithEmailUseCase';
import type { UserRepository } from '../../domain/repositories/UserRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/application';
import { LoginWithEmailUseCase } from './LoginWithEmailUseCase';
// Mock the PasswordHash module
vi.mock('@core/identity/domain/value-objects/PasswordHash', () => ({

View File

@@ -4,12 +4,11 @@
* Authenticates a user with email and password.
*/
import type { UserRepository } from '../../domain/repositories/UserRepository';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import { PasswordHash } from '@/identity/domain/value-objects/PasswordHash';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import { PasswordHash } from '@core/identity/domain/value-objects/PasswordHash';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
export type LoginWithEmailInput = {
email: string;

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { LogoutUseCase } from './LogoutUseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { LogoutUseCase } from './LogoutUseCase';
describe('LogoutUseCase', () => {
let sessionPort: {

View File

@@ -1,14 +1,9 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { ResetPasswordUseCase } from './ResetPasswordUseCase';
import type { AuthRepository } from '../../domain/repositories/AuthRepository';
import type { MagicLinkRepository } from '../../domain/repositories/MagicLinkRepository';
import type { PasswordHashingService } from '../../domain/services/PasswordHashingService';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { User } from '../../domain/entities/User';
import { UserId } from '../../domain/value-objects/UserId';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
import { UserId } from '../../domain/value-objects/UserId';
import { ResetPasswordUseCase } from './ResetPasswordUseCase';
describe('ResetPasswordUseCase', () => {
let authRepo: {

View File

@@ -1,10 +1,6 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { SignupSponsorUseCase } from './SignupSponsorUseCase';
import type { AuthRepository } from '../../domain/repositories/AuthRepository';
import type { CompanyRepository } from '../../domain/repositories/CompanyRepository';
import type { PasswordHashingService } from '../../domain/services/PasswordHashingService';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
describe('SignupSponsorUseCase', () => {
let authRepo: {

View File

@@ -1,11 +1,9 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { SignupUseCase } from './SignupUseCase';
import type { AuthRepository } from '../../domain/repositories/AuthRepository';
import type { PasswordHashingService } from '../../domain/services/PasswordHashingService';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { User } from '../../domain/entities/User';
import { UserId } from '../../domain/value-objects/UserId';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import { UserId } from '../../domain/value-objects/UserId';
import { SignupUseCase } from './SignupUseCase';
describe('SignupUseCase', () => {
let authRepo: {

View File

@@ -1,9 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { SignupWithEmailUseCase } from './SignupWithEmailUseCase';
import type { UserRepository } from '../../domain/repositories/UserRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { SignupWithEmailUseCase } from './SignupWithEmailUseCase';
describe('SignupWithEmailUseCase', () => {
let userRepository: {

View File

@@ -1,15 +1,8 @@
/**
* Signup with Email Use Case
*
* Creates a new user account with email and password.
*/
import type { UserRepository, StoredUser } from '../../domain/repositories/UserRepository';
import type { AuthenticatedUser } from '../ports/IdentityProviderPort';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { StoredUser } from '../../domain/repositories/UserRepository';
import type { AuthenticatedUser } from '../ports/IdentityProviderPort';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
export type SignupWithEmailInput = {
email: string;

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { StartAuthUseCase } from './StartAuthUseCase';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { StartAuthUseCase } from './StartAuthUseCase';
describe('StartAuthUseCase', () => {
let provider: {

View File

@@ -1,7 +1,7 @@
import type { IdentityProviderPort, AuthProvider, StartAuthCommand } from '../ports/IdentityProviderPort';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { AuthProvider, IdentityProviderPort, StartAuthCommand } from '../ports/IdentityProviderPort';
export type StartAuthInput = {
provider: AuthProvider;

View File

@@ -1,8 +1,6 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { CreateAchievementUseCase, type IAchievementRepository } from './CreateAchievementUseCase';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { Achievement } from '@core/identity/domain/entities/Achievement';
describe('CreateAchievementUseCase', () => {
let achievementRepository: {

View File

@@ -1,7 +1,7 @@
import { Achievement, AchievementProps } from '@core/identity/domain/entities/Achievement';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
export interface AchievementRepository {
save(achievement: Achievement): Promise<void>;

View File

@@ -5,7 +5,7 @@
* Achievements are categorized by role (driver, steward, admin) and type.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
export type AchievementCategory = 'driver' | 'steward' | 'admin' | 'community';

View File

@@ -1,5 +1,5 @@
import type { Entity } from '@core/shared/domain';
import { IdentityDomainValidationError, IdentityDomainInvariantError } from '../errors/IdentityDomainError';
import type { Entity, IEntity } from '@core/shared/domain/Entity';
import { IdentityDomainInvariantError, IdentityDomainValidationError } from '../errors/IdentityDomainError';
export interface AdminVote {
voterId: string;

View File

@@ -1,4 +1,4 @@
import { Entity } from '@core/shared/domain';
import { Entity } from '@core/shared/domain/Entity';
import { UserId } from '../value-objects/UserId';
import { GameKey } from '../value-objects/GameKey';
import { ExternalRating } from '../value-objects/ExternalRating';

View File

@@ -1,8 +1,8 @@
import type { Entity } from '@core/shared/domain';
import { RatingEventId } from '../value-objects/RatingEventId';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import type { Entity, IEntity } from '@core/shared/domain/Entity';
import { IdentityDomainInvariantError, IdentityDomainValidationError } from '../errors/IdentityDomainError';
import { RatingDelta } from '../value-objects/RatingDelta';
import { IdentityDomainValidationError, IdentityDomainInvariantError } from '../errors/IdentityDomainError';
import { RatingDimensionKey } from '../value-objects/RatingDimensionKey';
import { RatingEventId } from '../value-objects/RatingEventId';
export interface RatingEventSource {
type: 'race' | 'penalty' | 'vote' | 'adminAction' | 'manualAdjustment';

View File

@@ -4,7 +4,7 @@
* Represents an achievement earned by a specific user.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
export interface UserAchievementProps {
id: string;

View File

@@ -1,15 +1,14 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import {
DeleteMediaUseCase,
type DeleteMediaInput,
type DeleteMediaErrorCode,
} from './DeleteMediaUseCase';
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Media } from '../../domain/entities/Media';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import {
DeleteMediaUseCase,
type DeleteMediaErrorCode,
type DeleteMediaInput,
} from './DeleteMediaUseCase';
describe('DeleteMediaUseCase', () => {
let mediaRepo: {

View File

@@ -4,11 +4,9 @@
* Handles the business logic for deleting media files.
*/
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
export interface DeleteMediaInput {
mediaId: string;

View File

@@ -1,14 +1,13 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import {
GetAvatarUseCase,
type GetAvatarInput,
type GetAvatarErrorCode,
} from './GetAvatarUseCase';
import type { AvatarRepository } from '../../domain/repositories/AvatarRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Avatar } from '../../domain/entities/Avatar';
import {
GetAvatarUseCase,
type GetAvatarErrorCode,
type GetAvatarInput,
} from './GetAvatarUseCase';
describe('GetAvatarUseCase', () => {
let avatarRepo: {

View File

@@ -4,8 +4,6 @@
* Handles the business logic for retrieving a driver's avatar.
*/
import type { AvatarRepository } from '../../domain/repositories/AvatarRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

View File

@@ -1,14 +1,13 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import {
GetMediaUseCase,
type GetMediaInput,
type GetMediaErrorCode,
} from './GetMediaUseCase';
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Media } from '../../domain/entities/Media';
import {
GetMediaUseCase,
type GetMediaErrorCode,
type GetMediaInput,
} from './GetMediaUseCase';
describe('GetMediaUseCase', () => {
let mediaRepo: {

View File

@@ -4,8 +4,6 @@
* Handles the business logic for retrieving media information.
*/
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

View File

@@ -1,16 +1,15 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import {
RequestAvatarGenerationUseCase,
type RequestAvatarGenerationInput,
type RequestAvatarGenerationErrorCode,
type RequestAvatarGenerationResult,
} from './RequestAvatarGenerationUseCase';
import type { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
import type { FaceValidationPort } from '../ports/FaceValidationPort';
import { describe, expect, it, vi, type Mock } from 'vitest';
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
import type { FaceValidationPort } from '../ports/FaceValidationPort';
import {
RequestAvatarGenerationUseCase,
type RequestAvatarGenerationErrorCode,
type RequestAvatarGenerationInput,
type RequestAvatarGenerationResult,
} from './RequestAvatarGenerationUseCase';
vi.mock('uuid', () => ({
v4: () => 'request-1',

View File

@@ -4,15 +4,14 @@
* Handles the business logic for requesting avatar generation from a face photo.
*/
import { Result } from '@/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import type { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
import type { FaceValidationPort } from '../ports/FaceValidationPort';
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
import type { Logger } from '@core/shared/application';
import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationRequest';
import type { RacingSuitColor } from '../../domain/types/AvatarGenerationRequest';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { AvatarGenerationPort } from '../ports/AvatarGenerationPort';
import type { FaceValidationPort } from '../ports/FaceValidationPort';
export interface RequestAvatarGenerationInput {
userId: string;

View File

@@ -1,14 +1,13 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import {
SelectAvatarUseCase,
type SelectAvatarErrorCode,
type SelectAvatarInput,
} from './SelectAvatarUseCase';
import type { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { AvatarGenerationRequest } from '../../domain/entities/AvatarGenerationRequest';
import {
SelectAvatarUseCase,
type SelectAvatarErrorCode,
type SelectAvatarInput,
} from './SelectAvatarUseCase';
describe('SelectAvatarUseCase', () => {
let avatarRepo: { findById: Mock; save: Mock };

View File

@@ -4,8 +4,6 @@
* Handles the business logic for selecting a generated avatar from the options.
*/
import type { AvatarGenerationRepository } from '../../domain/repositories/AvatarGenerationRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';

View File

@@ -1,15 +1,14 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import {
UpdateAvatarUseCase,
type UpdateAvatarErrorCode,
type UpdateAvatarInput,
type UpdateAvatarResult,
} from './UpdateAvatarUseCase';
import type { AvatarRepository } from '../../domain/repositories/AvatarRepository';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Avatar } from '../../domain/entities/Avatar';
import {
UpdateAvatarUseCase,
type UpdateAvatarErrorCode,
type UpdateAvatarInput,
type UpdateAvatarResult,
} from './UpdateAvatarUseCase';
vi.mock('uuid', () => ({
v4: () => 'avatar-1',

View File

@@ -4,13 +4,12 @@
* Handles the business logic for updating a driver's avatar.
*/
import type { Logger } from '@core/shared/application';
import { AvatarId } from '@/media/domain/value-objects/AvatarId';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import { Avatar } from '../../domain/entities/Avatar';
import type { AvatarRepository } from '../../domain/repositories/AvatarRepository';
import { AvatarId } from '../../domain/value-objects/AvatarId';
export interface UpdateAvatarInput {
driverId: string;

View File

@@ -1,18 +1,17 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import { Readable } from 'node:stream';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import {
UploadMediaUseCase,
type MulterFile,
type UploadMediaErrorCode,
type UploadMediaInput,
type UploadMediaResult,
} from './UploadMediaUseCase';
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import { Readable } from 'node:stream';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Media } from '../../domain/entities/Media';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import {
UploadMediaUseCase,
type MulterFile,
type UploadMediaErrorCode,
type UploadMediaInput,
type UploadMediaResult,
} from './UploadMediaUseCase';
vi.mock('uuid', () => ({
v4: () => 'media-1',

View File

@@ -4,13 +4,11 @@
* Handles the business logic for uploading media files.
*/
import type { MediaRepository } from '../../domain/repositories/MediaRepository';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Media } from '../../domain/entities/Media';
import { v4 as uuidv4 } from 'uuid';
import { Media } from '../../domain/entities/Media';
import type { MediaStoragePort } from '../ports/MediaStoragePort';
// Define Multer file type locally since @types/multer is not available
export interface MulterFile {

View File

@@ -4,7 +4,7 @@
* Represents a user's selected avatar.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import { MediaUrl } from '../value-objects/MediaUrl';
export interface AvatarProps {

View File

@@ -4,12 +4,12 @@
* Represents a request to generate a racing avatar from a face photo.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import type {
AvatarGenerationRequestProps,
AvatarGenerationStatus,
AvatarStyle,
RacingSuitColor,
AvatarGenerationRequestProps,
AvatarGenerationStatus,
AvatarStyle,
RacingSuitColor,
} from '../types/AvatarGenerationRequest';
import { MediaUrl } from '../value-objects/MediaUrl';

View File

@@ -4,7 +4,7 @@
* Represents a media file (image, video, etc.) stored in the system.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import { MediaUrl } from '../value-objects/MediaUrl';
export type MediaType = 'image' | 'video' | 'document';

View File

@@ -4,7 +4,7 @@
* Retrieves all notifications for a recipient.
*/
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Notification } from '../../domain/entities/Notification';
@@ -23,7 +23,7 @@ export type GetAllNotificationsErrorCode = 'REPOSITORY_ERROR';
export class GetAllNotificationsUseCase {
constructor(
private readonly notificationRepository: INotificationRepository,
private readonly notificationRepository: NotificationRepository,
private readonly logger: Logger,
) {}

View File

@@ -1,13 +1,12 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import {
GetUnreadNotificationsUseCase,
type GetUnreadNotificationsInput,
} from './GetUnreadNotificationsUseCase';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Notification } from '../../domain/entities/Notification';
import {
GetUnreadNotificationsUseCase,
type GetUnreadNotificationsInput,
} from './GetUnreadNotificationsUseCase';
interface NotificationRepositoryMock {
findUnreadByRecipientId: Mock;

View File

@@ -4,15 +4,12 @@
* Retrieves unread notifications for a recipient.
*/
import type { Logger } from '@core/shared/application';
import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Notification } from '../../domain/entities/Notification';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
export type GetUnreadNotificationsInput = {
recipientId: string;
};
export interface GetUnreadNotificationsResult {
notifications: Notification[];
@@ -23,7 +20,7 @@ export type GetUnreadNotificationsErrorCode = 'REPOSITORY_ERROR';
export class GetUnreadNotificationsUseCase {
constructor(
private readonly notificationRepository: INotificationRepository,
private readonly notificationRepository: NotificationRepository,
private readonly logger: Logger,
) {}

View File

@@ -1,17 +1,16 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import {
MarkNotificationReadUseCase,
type MarkNotificationReadCommand,
MarkAllNotificationsReadUseCase,
type MarkAllNotificationsReadInput,
DismissNotificationUseCase,
type DismissNotificationCommand,
} from './MarkNotificationReadUseCase';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import { Notification } from '../../domain/entities/Notification';
import {
DismissNotificationUseCase,
MarkAllNotificationsReadUseCase,
MarkNotificationReadUseCase,
type DismissNotificationCommand,
type MarkAllNotificationsReadInput,
type MarkNotificationReadCommand,
} from './MarkNotificationReadUseCase';
interface NotificationRepositoryMock {
findById: Mock;

View File

@@ -4,15 +4,11 @@
* Marks a notification as read.
*/
import type { Logger } from '@core/shared/application';
import { NotificationRepository } from '@/notifications/domain/repositories/NotificationRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
export interface MarkNotificationReadCommand {
notificationId: string;
recipientId: string; // For validation
}
export interface MarkNotificationReadResult {
notificationId: string;
@@ -27,7 +23,7 @@ export type MarkNotificationReadErrorCode =
export class MarkNotificationReadUseCase {
constructor(
private readonly notificationRepository: INotificationRepository,
private readonly notificationRepository: NotificationRepository,
private readonly logger: Logger,
) {}

View File

@@ -1,21 +1,20 @@
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock, beforeEach } from 'vitest';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import type { ChannelPreference, NotificationPreference, TypePreference } from '../../domain/entities/NotificationPreference';
import type { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
import type { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
import {
GetNotificationPreferencesQuery,
SetDigestModeUseCase,
UpdateChannelPreferenceUseCase,
UpdateQuietHoursUseCase,
UpdateTypePreferenceUseCase,
type GetNotificationPreferencesInput,
type SetDigestModeCommand,
type UpdateChannelPreferenceCommand,
type UpdateQuietHoursCommand,
type UpdateTypePreferenceCommand,
GetNotificationPreferencesQuery,
SetDigestModeUseCase,
UpdateChannelPreferenceUseCase,
UpdateQuietHoursUseCase,
UpdateTypePreferenceUseCase,
type GetNotificationPreferencesInput,
type SetDigestModeCommand,
type UpdateChannelPreferenceCommand,
type UpdateQuietHoursCommand,
type UpdateTypePreferenceCommand,
} from './NotificationPreferencesUseCases';
describe('NotificationPreferencesUseCases', () => {

View File

@@ -4,13 +4,13 @@
* Manages user notification preferences.
*/
import type { Logger } from '@core/shared/application';
import { NotificationPreferenceRepository } from '@/notifications/domain/repositories/NotificationPreferenceRepository';
import { NotificationChannel, NotificationType } from '@/notifications/domain/types/NotificationTypes';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { NotificationPreference } from '../../domain/entities/NotificationPreference';
import type { ChannelPreference, TypePreference } from '../../domain/entities/NotificationPreference';
import type { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
import type { NotificationType, NotificationChannel } from '../../domain/types/NotificationTypes';
import { NotificationPreference } from '../../domain/entities/NotificationPreference';
/**
* Query: GetNotificationPreferencesQuery
@@ -27,7 +27,7 @@ export type GetNotificationPreferencesErrorCode = 'REPOSITORY_ERROR';
export class GetNotificationPreferencesQuery {
constructor(
private readonly preferenceRepository: INotificationPreferenceRepository,
private readonly preferenceRepository: NotificationPreferenceRepository,
private readonly logger: Logger,
) {}

View File

@@ -1,19 +1,17 @@
import { describe, it, expect, vi, type Mock } from 'vitest';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { describe, expect, it, vi, type Mock } from 'vitest';
import {
SendNotificationUseCase,
type SendNotificationCommand,
type SendNotificationErrorCode,
type SendNotificationResult,
SendNotificationUseCase,
type SendNotificationCommand,
type SendNotificationErrorCode,
type SendNotificationResult,
} from './SendNotificationUseCase';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
import type { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
import type { NotificationGatewayRegistry } from '../ports/NotificationGateway';
import type { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
import type { NotificationGatewayRegistry } from '../ports/NotificationGateway';
vi.mock('uuid', () => ({
v4: () => 'notif-1',

View File

@@ -5,15 +5,13 @@
* based on their preferences.
*/
import type { Logger } from '@core/shared/application';
import { NotificationChannel, NotificationType } from '@/notifications/domain/types/NotificationTypes';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuid } from 'uuid';
import type { NotificationData } from '../../domain/entities/Notification';
import { Notification } from '../../domain/entities/Notification';
import type { NotificationPreferenceRepository } from '../../domain/repositories/NotificationPreferenceRepository';
import type { NotificationRepository } from '../../domain/repositories/NotificationRepository';
import type { NotificationChannel, NotificationType } from '../../domain/types/NotificationTypes';
import type { NotificationDeliveryResult, NotificationGatewayRegistry } from '../ports/NotificationGateway';
export interface SendNotificationCommand {

View File

@@ -5,11 +5,11 @@
* Immutable entity with factory methods and domain validation.
*/
import type { Entity } from '@core/shared/domain';
import type { Entity } from '@core/shared/domain/Entity';
import { NotificationDomainError } from '../errors/NotificationDomainError';
import { NotificationId } from '../value-objects/NotificationId';
import type { NotificationType, NotificationChannel } from '../types/NotificationTypes';
import type { NotificationChannel, NotificationType } from '../types/NotificationTypes';
export type NotificationStatus = 'unread' | 'read' | 'dismissed' | 'action_required';

View File

@@ -4,9 +4,9 @@
* Represents a user's notification preferences for different channels and types.
*/
import type { Entity } from '@core/shared/domain';
import type { NotificationType, NotificationChannel } from '../types/NotificationTypes';
import type { Entity } from '@core/shared/domain/Entity';
import { NotificationDomainError } from '../errors/NotificationDomainError';
import type { NotificationChannel, NotificationType } from '../types/NotificationTypes';
import { QuietHours } from '../value-objects/QuietHours';
export interface ChannelPreference {

View File

@@ -1,14 +1,9 @@
import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { WalletRepository } from '@core/payments/domain/repositories/WalletRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { LeagueWallet } from '../../domain/entities/league-wallet/LeagueWallet';
import { Season } from '../../domain/entities/season/Season';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import type { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
import type { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import { Money } from '../../domain/value-objects/Money';
import { AcceptSponsorshipRequestUseCase } from './AcceptSponsorshipRequestUseCase';

View File

@@ -6,15 +6,9 @@
*/
import type { NotificationService } from '@core/notifications/application/ports/NotificationService';
import type { WalletRepository } from '@core/payments/domain/repositories/WalletRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SeasonSponsorship } from '../../domain/entities/season/SeasonSponsorship';
import type { LeagueWalletRepository } from '../../domain/repositories/LeagueWalletRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { SeasonSponsorshipRepository } from '../../domain/repositories/SeasonSponsorshipRepository';
import type { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
export interface AcceptSponsorshipRequestInput {
requestId: string;

View File

@@ -1,10 +1,7 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { ApplyForSponsorshipUseCase } from './ApplyForSponsorshipUseCase';
import type { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import type { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import type { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { Money } from '../../domain/value-objects/Money';
import { ApplyForSponsorshipUseCase } from './ApplyForSponsorshipUseCase';
describe('ApplyForSponsorshipUseCase', () => {
let mockSponsorshipRequestRepo: {

View File

@@ -5,14 +5,11 @@
* (driver, team, race, or season/league).
*/
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import type { SponsorshipRequestRepository } from '../../domain/repositories/SponsorshipRequestRepository';
import type { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import type { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import { Money, isCurrency } from '../../domain/value-objects/Money';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { Result } from '@/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { SponsorshipRequest } from '../../domain/entities/SponsorshipRequest';
import { Money, isCurrency } from '../../domain/value-objects/Money';
export interface ApplyForSponsorshipInput {
sponsorId: string;

View File

@@ -1,9 +1,6 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { ApplyPenaltyUseCase, type ApplyPenaltyResult } from './ApplyPenaltyUseCase';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { ApplyPenaltyUseCase } from './ApplyPenaltyUseCase';
describe('ApplyPenaltyUseCase', () => {
let mockPenaltyRepo: {
create: Mock;
@@ -46,15 +43,11 @@ describe('ApplyPenaltyUseCase', () => {
});
it('should return error when race does not exist', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue(null);
@@ -68,19 +61,15 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(false);
expect(result.error!.code).toBe('RACE_NOT_FOUND');
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
});
it('should return error when steward does not have authority', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -102,19 +91,15 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(false);
expect(result.error!.code).toBe('INSUFFICIENT_AUTHORITY');
expect(result.unwrapErr().code).toBe('INSUFFICIENT_AUTHORITY');
});
it('should return error when protest does not exist', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -138,19 +123,15 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(false);
expect(result.error!.code).toBe('PROTEST_NOT_FOUND');
expect(result.unwrapErr().code).toBe('PROTEST_NOT_FOUND');
});
it('should return error when protest is not upheld', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -174,19 +155,15 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(false);
expect(result.error!.code).toBe('PROTEST_NOT_UPHELD');
expect(result.unwrapErr().code).toBe('PROTEST_NOT_UPHELD');
});
it('should return error when protest is not for this race', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -210,19 +187,15 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(false);
expect(result.error!.code).toBe('PROTEST_NOT_FOR_RACE');
expect(result.unwrapErr().code).toBe('PROTEST_NOT_FOR_RACE');
});
it('should create penalty and return result on success', async () => {
const output: { present: Mock } = {
present: vi.fn(),
};
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as unknown as IPenaltyRepository,
mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository,
mockLogger as unknown as Logger);
const useCase = new ApplyPenaltyUseCase(mockPenaltyRepo as any,
mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any,
mockLogger as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -246,22 +219,11 @@ describe('ApplyPenaltyUseCase', () => {
});
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = (expect(presented).toEqual({ penaltyId: expect.any(String) });
const presented = result.unwrap();
expect(presented.penaltyId).toBeDefined();
expect(mockPenaltyRepo.create).toHaveBeenCalledTimes(1);
const createdPenalty = (mockPenaltyRepo.create as Mock).mock.calls[0]?.[0] as unknown as {
leagueId: unknown;
raceId: unknown;
driverId: unknown;
type: string;
value?: number;
reason: string;
issuedBy: unknown;
status: unknown;
notes?: string;
};
const createdPenalty = (mockPenaltyRepo.create as Mock).mock.calls[0]?.[0] as any;
type ToStringable = { toString(): string };
const asString = (value: unknown): string => {
@@ -287,4 +249,4 @@ describe('ApplyPenaltyUseCase', () => {
expect(asString(createdPenalty.status)).toBe('pending');
expect(createdPenalty.notes).toBe('Test notes');
});
});
});

View File

@@ -5,15 +5,11 @@
* The penalty can be standalone or linked to an upheld protest.
*/
import { Penalty } from '../../domain/entities/penalty/Penalty';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import type { ProtestRepository } from '../../domain/repositories/ProtestRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import { randomUUID } from 'crypto';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import { Result } from '@/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { randomUUID } from 'crypto';
import { Penalty } from '../../domain/entities/penalty/Penalty';
export interface ApplyPenaltyInput {
raceId: string;

View File

@@ -1,9 +1,8 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { CancelRaceUseCase, type CancelRaceResult } from './CancelRaceUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/application';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { Race } from '../../domain/entities/Race';
import { SessionType } from '../../domain/value-objects/SessionType';
import { CancelRaceUseCase } from './CancelRaceUseCase';
describe('CancelRaceUseCase', () => {
let useCase: CancelRaceUseCase;
let raceRepository: {
@@ -16,6 +15,7 @@ describe('CancelRaceUseCase', () => {
info: Mock;
error: Mock;
};
beforeEach(() => {
raceRepository = {
findById: vi.fn(),
@@ -27,8 +27,8 @@ describe('CancelRaceUseCase', () => {
info: vi.fn(),
error: vi.fn(),
};
useCase = new CancelRaceUseCase(raceRepository as unknown as IRaceRepository,
logger as unknown as Logger);
useCase = new CancelRaceUseCase(raceRepository as any,
logger as any);
});
it('should cancel race successfully', async () => {
@@ -48,14 +48,14 @@ describe('CancelRaceUseCase', () => {
const result = await useCase.execute({ raceId, cancelledById: 'admin-1' });
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
expect(raceRepository.findById).toHaveBeenCalledWith(raceId);
expect(raceRepository.update).toHaveBeenCalledTimes(1);
const updatedRace = (raceRepository.update as Mock).mock.calls[0]?.[0] as Race;
expect(updatedRace.id).toBe(raceId);
expect(updatedRace.status.toString()).toBe('cancelled');
const presented = (expect(presented.race.id).toBe(raceId);
expect(presented.race.id).toBe(raceId);
expect(presented.race.status.toString()).toBe('cancelled');
});
@@ -67,7 +67,7 @@ describe('CancelRaceUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
});
});
it('should return domain error if race is already cancelled', async () => {
const raceId = 'race-1';
@@ -91,7 +91,7 @@ describe('CancelRaceUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toContain('already cancelled');
}
});
});
it('should return domain error if race is completed', async () => {
const raceId = 'race-1';
@@ -115,5 +115,5 @@ describe('CancelRaceUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toContain('completed race');
}
});
});
});
});

View File

@@ -1,5 +1,3 @@
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Race } from '../../domain/entities/Race';

View File

@@ -1,13 +1,9 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { CloseRaceEventStewardingUseCase, type CloseRaceEventStewardingResult } from './CloseRaceEventStewardingUseCase';
import type { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import type { DomainEventPublisher } from '@core/shared/domain/DomainEvent';
import type { Logger } from '@core/shared/application';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { RaceEvent } from '../../domain/entities/RaceEvent';
import { Session } from '../../domain/entities/Session';
import { SessionType } from '../../domain/value-objects/SessionType';
import { CloseRaceEventStewardingUseCase } from './CloseRaceEventStewardingUseCase';
describe('CloseRaceEventStewardingUseCase', () => {
let useCase: CloseRaceEventStewardingUseCase;
let raceEventRepository: {
@@ -27,6 +23,7 @@ describe('CloseRaceEventStewardingUseCase', () => {
let logger: {
error: Mock;
};
beforeEach(() => {
raceEventRepository = {
findAwaitingStewardingClose: vi.fn(),
@@ -45,11 +42,11 @@ describe('CloseRaceEventStewardingUseCase', () => {
logger = {
error: vi.fn(),
};
useCase = new CloseRaceEventStewardingUseCase(logger as unknown as Logger,
raceEventRepository as unknown as IRaceEventRepository,
raceRegistrationRepository as unknown as IRaceRegistrationRepository,
penaltyRepository as unknown as IPenaltyRepository,
domainEventPublisher as unknown as DomainEventPublisher);
useCase = new CloseRaceEventStewardingUseCase(logger as any,
raceEventRepository as any,
raceRegistrationRepository as any,
penaltyRepository as any,
domainEventPublisher as any);
});
it('should close stewarding for expired events successfully', async () => {
@@ -82,33 +79,27 @@ describe('CloseRaceEventStewardingUseCase', () => {
const result = await useCase.execute({ raceId: 'event-1', closedById: 'admin-1' });
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
expect(raceEventRepository.findAwaitingStewardingClose).toHaveBeenCalled();
expect(raceEventRepository.update).toHaveBeenCalledWith(
expect.objectContaining({ status: 'closed' })
);
expect(domainEventPublisher.publish).toHaveBeenCalled();
const presentedRace = (status?: unknown;
};
const presentedId =
presentedRace?.id && typeof presentedRace.id === 'object' && typeof presentedRace.id.toString === 'function'
? presentedRace.id.toString()
: presentedRace?.id;
expect(presentedId).toBe('event-1');
expect(presentedRace?.status).toBe('closed');
expect(presented.race.id).toBe('event-1');
expect(presented.race.status).toBe('closed');
});
it('should handle no expired events', async () => {
it('should return error when no expired events', async () => {
raceEventRepository.findAwaitingStewardingClose.mockResolvedValue([]);
const result = await useCase.execute({ raceId: 'event-1', closedById: 'admin-1' });
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
expect(raceEventRepository.update).not.toHaveBeenCalled();
expect(domainEventPublisher.publish).not.toHaveBeenCalled();
});
});
it('should return error when repository throws', async () => {
raceEventRepository.findAwaitingStewardingClose.mockRejectedValue(new Error('DB error'));
@@ -121,5 +112,5 @@ describe('CloseRaceEventStewardingUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toContain('DB error');
}
});
});
});
});

View File

@@ -1,12 +1,9 @@
import type { Logger } from '@core/shared/application';
import type { RaceEventRepository } from '../../domain/repositories/RaceEventRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { PenaltyRepository } from '../../domain/repositories/PenaltyRepository';
import type { DomainEventPublisher } from '@core/shared/domain/DomainEvent';
import { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
import { DomainEventPublisher } from '@/shared/domain/DomainEvent';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceEvent } from '../../domain/entities/RaceEvent';
import { RaceEventStewardingClosedEvent } from '../../domain/events/RaceEventStewardingClosed';
export type CloseRaceEventStewardingInput = {
raceId: string;

View File

@@ -4,6 +4,7 @@ import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { RaceRegistrationRepository } from '../../domain/repositories/RaceRegistrationRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
import type { StandingRepository } from '../../domain/repositories/StandingRepository';
describe('CompleteRaceUseCase', () => {
let useCase: CompleteRaceUseCase;
let raceRepository: {
@@ -21,7 +22,6 @@ describe('CompleteRaceUseCase', () => {
save: Mock;
};
let getDriverRating: Mock;
let output: { present: Mock };
beforeEach(() => {
raceRepository = {
@@ -39,11 +39,10 @@ describe('CompleteRaceUseCase', () => {
save: vi.fn(),
};
getDriverRating = vi.fn();
output = { present: vi.fn() };
useCase = new CompleteRaceUseCase(raceRepository as unknown as IRaceRepository,
raceRegistrationRepository as unknown as IRaceRegistrationRepository,
resultRepository as unknown as IResultRepository,
standingRepository as unknown as IStandingRepository,
useCase = new CompleteRaceUseCase(raceRepository as any,
raceRegistrationRepository as any,
resultRepository as any,
standingRepository as any,
getDriverRating);
});
@@ -73,7 +72,8 @@ describe('CompleteRaceUseCase', () => {
const result = await useCase.execute(command);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
expect(presented.raceId).toBe('race-1');
expect(raceRepository.findById).toHaveBeenCalledWith('race-1');
expect(raceRegistrationRepository.getRegisteredDrivers).toHaveBeenCalledWith('race-1');
expect(getDriverRating).toHaveBeenCalledTimes(2);
@@ -81,7 +81,7 @@ describe('CompleteRaceUseCase', () => {
expect(standingRepository.save).toHaveBeenCalledTimes(2);
expect(mockRace.complete).toHaveBeenCalled();
expect(raceRepository.update).toHaveBeenCalledWith({ id: 'race-1', status: 'completed' });
});
});
it('should return error when race does not exist', async () => {
const command: CompleteRaceInput = {
@@ -94,7 +94,7 @@ describe('CompleteRaceUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('RACE_NOT_FOUND');
});
});
it('should return error when no registered drivers', async () => {
const command: CompleteRaceInput = {
@@ -114,7 +114,7 @@ describe('CompleteRaceUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('NO_REGISTERED_DRIVERS');
});
});
it('should return error when repository throws', async () => {
const command: CompleteRaceInput = {
@@ -138,5 +138,5 @@ describe('CompleteRaceUseCase', () => {
const error = result.unwrapErr();
expect(error.code).toBe('REPOSITORY_ERROR');
expect(error.details?.message).toBe('DB error');
});
});
});
});

View File

@@ -1,19 +1,10 @@
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { Race } from '../../domain/entities/Race';
import type { Season } from '../../domain/entities/season/Season';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
export type CreateLeagueSeasonScheduleRaceInput = {
leagueId: string;
seasonId: string;
track: string;
car: string;
scheduledAt: Date;
};
export type CreateLeagueSeasonScheduleRaceResult = {
raceId: string;
@@ -96,9 +87,7 @@ export class CreateLeagueSeasonScheduleRaceUseCase {
}
}
private isWithinSeasonWindow(_season: Season, _scheduledAt: Date): boolean {
// Implementation would check if scheduledAt is within season's schedule window
// For now, return true as a placeholder
return true;
private isWithinSeasonWindow(season: Season, scheduledAt: Date): boolean {
return scheduledAt >= season.startDate && scheduledAt <= season.endDate;
}
}

View File

@@ -1,12 +1,8 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import {
CreateLeagueWithSeasonAndScoringUseCase,
type CreateLeagueWithSeasonAndScoringCommand,
type CreateLeagueWithSeasonAndScoringResult,
CreateLeagueWithSeasonAndScoringUseCase,
type CreateLeagueWithSeasonAndScoringCommand
} from './CreateLeagueWithSeasonAndScoringUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
let useCase: CreateLeagueWithSeasonAndScoringUseCase;
@@ -26,7 +22,6 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
warn: Mock;
error: Mock;
};
let output: { present: Mock } ;
beforeEach(() => {
leagueRepository = {
@@ -45,12 +40,11 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
warn: vi.fn(),
error: vi.fn(),
};
output = { present: vi.fn() } as unknown as typeof output;
useCase = new CreateLeagueWithSeasonAndScoringUseCase(leagueRepository as unknown as ILeagueRepository,
seasonRepository as unknown as ISeasonRepository,
leagueScoringConfigRepository as unknown as ILeagueScoringConfigRepository,
useCase = new CreateLeagueWithSeasonAndScoringUseCase(leagueRepository as any,
seasonRepository as any,
leagueScoringConfigRepository as any,
getLeagueScoringPresetById,
logger as unknown as Logger);
logger as any);
});
it('should create league, season, and scoring successfully', async () => {
@@ -82,11 +76,11 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
const result = await useCase.execute(command);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = (expect(presented?.league.id.toString()).toBeDefined();
expect(presented?.season.id).toBeDefined();
expect(presented?.scoringConfig.seasonId.toString()).toBe(presented?.season.id);
expect(presented.league.id).toBeDefined();
expect(presented.season.id).toBeDefined();
expect(presented.scoringConfig.seasonId.toString()).toBe(presented.season.id);
expect(leagueRepository.create).toHaveBeenCalledTimes(1);
expect(seasonRepository.create).toHaveBeenCalledTimes(1);
expect(leagueScoringConfigRepository.save).toHaveBeenCalledTimes(1);
@@ -113,7 +107,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('League name is required');
}
});
});
it('should return error when ownerId is empty', async () => {
const command = {
@@ -136,7 +130,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('League ownerId is required');
}
});
});
it('should return error when gameId is empty', async () => {
const command = {
@@ -159,7 +153,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('gameId is required');
}
});
});
it('should return error when visibility is missing', async () => {
const command: Partial<CreateLeagueWithSeasonAndScoringCommand> = {
@@ -180,7 +174,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('visibility is required');
}
});
});
it('should return error when maxDrivers is invalid', async () => {
const command = {
@@ -204,7 +198,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('maxDrivers must be greater than 0 when provided');
}
});
});
it('should return error when ranked league has insufficient drivers', async () => {
const command = {
@@ -228,7 +222,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toContain('Ranked leagues require at least 10 drivers');
}
});
});
it('should return error when scoring preset is unknown', async () => {
const command = {
@@ -254,7 +248,7 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('Unknown scoring preset: unknown-preset');
}
});
});
it('should return error when repository throws', async () => {
const command = {
@@ -285,5 +279,5 @@ describe('CreateLeagueWithSeasonAndScoringUseCase', () => {
if ('details' in err && err.details && typeof err.details === 'object' && 'message' in err.details) {
expect(err.details.message).toBe('DB error');
}
});
});
});
});

View File

@@ -1,21 +1,18 @@
import { v4 as uuidv4 } from 'uuid';
import { League } from '../../domain/entities/League';
import { Season } from '../../domain/entities/season/Season';
import { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
import type { LeagueScoringConfigRepository } from '../../domain/repositories/LeagueScoringConfigRepository';
import type { Logger } from '@core/shared/application';
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
import type { SessionType } from '../../domain/types/SessionType';
import type { BonusRule } from '../../domain/types/BonusRule';
import { PointsTable } from '../../domain/value-objects/PointsTable';
import {
LeagueVisibility,
MIN_RANKED_LEAGUE_DRIVERS,
} from '../../domain/value-objects/LeagueVisibility';
import { League } from '@/racing/domain/entities/League';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import { LeagueScoringConfig } from '../../domain/entities/LeagueScoringConfig';
import { Season } from '../../domain/entities/season/Season';
import type { BonusRule } from '../../domain/types/BonusRule';
import type { ChampionshipConfig } from '../../domain/types/ChampionshipConfig';
import type { SessionType } from '../../domain/types/SessionType';
import {
LeagueVisibility,
MIN_RANKED_LEAGUE_DRIVERS,
} from '../../domain/value-objects/LeagueVisibility';
import { PointsTable } from '../../domain/value-objects/PointsTable';
export type CreateLeagueWithSeasonAndScoringCommand = {
name: string;

View File

@@ -1,5 +1,4 @@
import { describe, it, expect, vi, Mock } from 'vitest';
import { describe, it, expect, vi, Mock, beforeEach } from 'vitest';
import { Season } from '@core/racing/domain/entities/season/Season';
import type { SeasonRepository } from '@core/racing/domain/repositories/SeasonRepository';
import type { LeagueRepository } from '@core/racing/domain/repositories/LeagueRepository';
@@ -75,7 +74,7 @@ type CreateSeasonErrorCode = ApplicationErrorCode<'LEAGUE_NOT_FOUND' | 'VALIDATI
describe('CreateSeasonForLeagueUseCase', () => {
const mockLeagueFindById = vi.fn();
const mockLeagueRepo: ILeagueRepository = {
const mockLeagueRepo: any = {
findById: mockLeagueFindById,
findAll: vi.fn(),
findByOwnerId: vi.fn(),
@@ -88,7 +87,7 @@ describe('CreateSeasonForLeagueUseCase', () => {
const mockSeasonFindById = vi.fn();
const mockSeasonAdd = vi.fn();
const mockSeasonRepo: ISeasonRepository = {
const mockSeasonRepo: any = {
findById: mockSeasonFindById,
findByLeagueId: vi.fn(),
create: vi.fn(),
@@ -98,11 +97,8 @@ describe('CreateSeasonForLeagueUseCase', () => {
listActiveByLeague: vi.fn(),
};
let output: { present: Mock } ;
beforeEach(() => {
vi.clearAllMocks();
output = { present: vi.fn() } as unknown as typeof output;
});
it('creates a planned Season for an existing league with config-derived props', async () => {
@@ -125,8 +121,6 @@ describe('CreateSeasonForLeagueUseCase', () => {
strategy: 'dropWorstN',
n: 2,
},
// Intentionally omit seasonStartDate / raceStartTime to avoid schedule derivation,
// focusing this test on scoring/drop/stewarding/maxDrivers mapping.
timings: {
qualifyingMinutes: 10,
mainRaceMinutes: 30,
@@ -141,13 +135,13 @@ describe('CreateSeasonForLeagueUseCase', () => {
config,
};
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
const result = await useCase.execute(command);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = (expect(presented?.season).toBeInstanceOf(Season);
expect(presented?.league.id).toBe('league-1');
expect(presented.season).toBeInstanceOf(Season);
expect(presented.league.id).toBe('league-1');
});
it('clones configuration from a source season when sourceSeasonId is provided', async () => {
@@ -172,15 +166,15 @@ describe('CreateSeasonForLeagueUseCase', () => {
sourceSeasonId: 'source-season',
};
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
const result = await useCase.execute(command);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = (expect(presented?.season.maxDrivers).toBe(40);
expect(presented.season.maxDrivers).toBe(40);
});
it('returns error when league not found and does not call output', async () => {
it('returns error when league not found', async () => {
mockLeagueFindById.mockResolvedValue(null);
const useCase = new CreateSeasonForLeagueUseCase(mockLeagueRepo, mockSeasonRepo);
@@ -191,15 +185,15 @@ describe('CreateSeasonForLeagueUseCase', () => {
gameId: 'iracing',
};
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
const result = await useCase.execute(command);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr();
expect(error.code).toBe('LEAGUE_NOT_FOUND');
expect(error.details?.message).toBe('League not found: missing-league');
});
});
it('returns validation error when source season is missing and does not call output', async () => {
it('returns validation error when source season is missing', async () => {
mockLeagueFindById.mockResolvedValue({ id: 'league-1' });
mockSeasonFindById.mockResolvedValue(undefined);
@@ -212,11 +206,11 @@ describe('CreateSeasonForLeagueUseCase', () => {
sourceSeasonId: 'missing-source',
};
const result: Result<void, CreateSeasonErrorCode> = await useCase.execute(command);
const result = await useCase.execute(command);
expect(result.isErr()).toBe(true);
const error = result.unwrapErr();
expect(error.code).toBe('VALIDATION_ERROR');
expect(error.details?.message).toBe('Source Season not found: missing-source');
});
})
});
});

View File

@@ -1,7 +1,6 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import { CreateSponsorUseCase, type CreateSponsorInput } from './CreateSponsorUseCase';
import type { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import type { Logger } from '@core/shared/application';
describe('CreateSponsorUseCase', () => {
let useCase: CreateSponsorUseCase;

View File

@@ -3,12 +3,11 @@
*
* Creates a new sponsor.
*/
import { ApplicationErrorCode } from '@/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import { v4 as uuidv4 } from 'uuid';
import { Sponsor } from '../../domain/entities/sponsor/Sponsor';
import type { SponsorRepository } from '../../domain/repositories/SponsorRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
export interface CreateSponsorInput {
name: string;

View File

@@ -1,12 +1,9 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import {
CreateTeamUseCase,
type CreateTeamInput,
type CreateTeamResult,
CreateTeamUseCase,
type CreateTeamInput
} from './CreateTeamUseCase';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/application';
describe('CreateTeamUseCase', () => {
let useCase: CreateTeamUseCase;
let teamRepository: {
@@ -22,7 +19,6 @@ describe('CreateTeamUseCase', () => {
warn: Mock;
error: Mock;
};
let output: { present: Mock };
beforeEach(() => {
teamRepository = {
@@ -38,10 +34,9 @@ describe('CreateTeamUseCase', () => {
warn: vi.fn(),
error: vi.fn(),
};
output = { present: vi.fn() };
useCase = new CreateTeamUseCase(teamRepository as unknown as ITeamRepository,
membershipRepository as unknown as ITeamMembershipRepository,
logger as unknown as Logger);
useCase = new CreateTeamUseCase(teamRepository as any,
membershipRepository as any,
logger as any);
});
it('should create team successfully', async () => {
@@ -69,10 +64,11 @@ describe('CreateTeamUseCase', () => {
const result = await useCase.execute(command);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
expect(presented.team.id).toBe('team-uuid');
expect(teamRepository.create).toHaveBeenCalledTimes(1);
expect(membershipRepository.saveMembership).toHaveBeenCalledTimes(1);
});
});
it('should return error when driver already belongs to a team', async () => {
const command: CreateTeamInput = {
@@ -100,7 +96,7 @@ describe('CreateTeamUseCase', () => {
);
expect(teamRepository.create).not.toHaveBeenCalled();
expect(membershipRepository.saveMembership).not.toHaveBeenCalled();
});
});
it('should return error when repository throws', async () => {
const command: CreateTeamInput = {
@@ -119,5 +115,5 @@ describe('CreateTeamUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
expect(result.unwrapErr().details?.message).toBe('DB error');
});
});
});
});

View File

@@ -3,18 +3,16 @@
*
* Creates a new team.
*/
import { Result } from '@/shared/domain/Result';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { v4 as uuidv4 } from 'uuid';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import { Team } from '../../domain/entities/Team';
import type {
TeamMembership,
TeamMembershipStatus,
TeamRole,
TeamMembership,
TeamMembershipStatus,
TeamRole,
} from '../../domain/types/TeamMembership';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
export interface CreateTeamInput {
name: string;
tag: string;

View File

@@ -1,15 +1,8 @@
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { SeasonRepository } from '../../domain/repositories/SeasonRepository';
export type DeleteLeagueSeasonScheduleRaceInput = {
leagueId: string;
seasonId: string;
raceId: string;
};
export type DeleteLeagueSeasonScheduleRaceResult = {
success: true;

View File

@@ -15,6 +15,7 @@ describe('FileProtestUseCase', () => {
let mockLeagueMembershipRepo: {
getLeagueMembers: Mock;
};
beforeEach(() => {
mockProtestRepo = {
create: vi.fn(),
@@ -25,12 +26,12 @@ describe('FileProtestUseCase', () => {
mockLeagueMembershipRepo = {
getLeagueMembers: vi.fn(),
};
});
});
it('should return error when race does not exist', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository);
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
mockRaceRepo.findById.mockResolvedValue(null);
@@ -45,12 +46,12 @@ describe('FileProtestUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<FileProtestErrorCode, { message: string }>;
expect(err.code).toBe('RACE_NOT_FOUND');
expect(err.details?.message).toBe('Race not found');
});
});
it('should return error when protesting against self', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository);
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
@@ -65,12 +66,12 @@ describe('FileProtestUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<FileProtestErrorCode, { message: string }>;
expect(err.code).toBe('SELF_PROTEST');
expect(err.details?.message).toBe('Cannot file a protest against yourself');
});
});
it('should return error when protesting driver is not an active member', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository);
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
@@ -88,12 +89,12 @@ describe('FileProtestUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<FileProtestErrorCode, { message: string }>;
expect(err.code).toBe('NOT_MEMBER');
expect(err.details?.message).toBe('Protesting driver is not an active member of this league');
});
});
it('should create protest and return protestId on success', async () => {
const useCase = new FileProtestUseCase(mockProtestRepo as unknown as IProtestRepository,
mockRaceRepo as unknown as IRaceRepository,
mockLeagueMembershipRepo as unknown as ILeagueMembershipRepository);
const useCase = new FileProtestUseCase(mockProtestRepo as any,
mockRaceRepo as any,
mockLeagueMembershipRepo as any);
mockRaceRepo.findById.mockResolvedValue({ id: 'race1', leagueId: 'league1' });
mockLeagueMembershipRepo.getLeagueMembers.mockResolvedValue([
@@ -111,21 +112,9 @@ describe('FileProtestUseCase', () => {
} as FileProtestInput);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
expect(mockProtestRepo.create).toHaveBeenCalledTimes(1);
const created = (mockProtestRepo.create as unknown as Mock).mock.calls[0]?.[0] as unknown as {
raceId: { toString(): string };
protestingDriverId: { toString(): string };
accusedDriverId: { toString(): string };
comment?: string;
proofVideoUrl: { toString(): string };
status: { toString(): string };
incident: {
lap: { toNumber(): number };
description: { toString(): string };
timeInRace?: unknown;
};
};
const created = (mockProtestRepo.create as unknown as Mock).mock.calls[0]?.[0] as any;
expect(created.raceId.toString()).toBe('race1');
expect(created.protestingDriverId.toString()).toBe('driver1');
@@ -136,16 +125,14 @@ describe('FileProtestUseCase', () => {
expect(created.incident.lap.toNumber()).toBe(5);
expect(created.incident.description.toString()).toBe('Collision');
expect(created.incident.timeInRace).toBeUndefined();
const presented = (expect(presented.protest.raceId.toString()).toBe('race1');
expect(presented.protest.raceId.toString()).toBe('race1');
expect(presented.protest.protestingDriverId.toString()).toBe('driver1');
expect(presented.protest.accusedDriverId.toString()).toBe('driver2');
expect(presented.protest.incident.lap.toNumber()).toBe(5);
expect(presented.protest.incident.description.toString()).toBe('Collision');
expect(presented.protest.incident.timeInRace).toBeUndefined();
expect(presented.protest.comment).toBe('Test comment');
expect(presented.protest.proofVideoUrl).toBeDefined();
expect(presented.protest.proofVideoUrl!.toString()).toBe('http://example.com/video');
});
});
});

View File

@@ -16,23 +16,23 @@ describe('GetAllLeaguesWithCapacityAndScoringUseCase', () => {
let mockSeasonRepo: { findByLeagueId: Mock };
let mockScoringConfigRepo: { findBySeasonId: Mock };
let mockGameRepo: { findById: Mock };
beforeEach(() => {
mockLeagueRepo = { findAll: vi.fn() };
mockMembershipRepo = { getLeagueMembers: vi.fn() };
mockSeasonRepo = { findByLeagueId: vi.fn() };
mockScoringConfigRepo = { findBySeasonId: vi.fn() };
mockGameRepo = { findById: vi.fn() };
output = { present: vi.fn() } as unknown as typeof output;
});
it('should return enriched leagues with capacity and scoring', async () => {
const useCase = new GetAllLeaguesWithCapacityAndScoringUseCase(mockLeagueRepo as unknown as ILeagueRepository,
mockMembershipRepo as unknown as ILeagueMembershipRepository,
mockSeasonRepo as unknown as ISeasonRepository,
mockScoringConfigRepo as unknown as ILeagueScoringConfigRepository,
mockGameRepo as unknown as IGameRepository,
{ getPresetById: vi.fn().mockReturnValue({ id: 'preset1', name: 'Default' }) },
output,
const useCase = new GetAllLeaguesWithCapacityAndScoringUseCase(
mockLeagueRepo as any,
mockMembershipRepo as any,
mockSeasonRepo as any,
mockScoringConfigRepo as any,
mockGameRepo as any,
{ getPresetById: vi.fn().mockReturnValue({ id: 'preset1', name: 'Default' }) }
);
const league = { id: 'league1', name: 'Test League', settings: { maxDrivers: 30 } };
@@ -53,19 +53,18 @@ describe('GetAllLeaguesWithCapacityAndScoringUseCase', () => {
const result = await useCase.execute({} as GetAllLeaguesWithCapacityAndScoringInput);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented =
expect(presented?.leagues).toHaveLength(1);
expect(presented.leagues).toHaveLength(1);
const [summary] = presented?.leagues ?? [];
const summary = presented.leagues[0];
expect(summary?.league).toEqual(league);
expect(summary?.currentDrivers).toBe(2);
expect(summary?.maxDrivers).toBe(30);
expect(summary?.season).toEqual(season);
expect(summary?.scoringConfig).toEqual(scoringConfig);
expect(summary?.game).toEqual(game);
expect(summary?.preset).toEqual({ id: 'preset1', name: 'Default' });
expect(summary.league).toEqual(league);
expect(summary.currentDrivers).toBe(2);
expect(summary.maxDrivers).toBe(30);
expect(summary.season).toEqual(season);
expect(summary.scoringConfig).toEqual(scoringConfig);
expect(summary.game).toEqual(game);
expect(summary.preset).toEqual({ id: 'preset1', name: 'Default' });
});
});

View File

@@ -1,18 +1,15 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
GetAllRacesPageDataUseCase,
type GetAllRacesPageDataResult,
type GetAllRacesPageDataInput,
} from './GetAllRacesPageDataUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { Logger } from '@core/shared/application';
import { Race } from '../../domain/entities/Race';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import {
GetAllRacesPageDataUseCase,
type GetAllRacesPageDataInput
} from './GetAllRacesPageDataUseCase';
describe('GetAllRacesPageDataUseCase', () => {
const mockRaceFindAll = vi.fn();
const mockRaceRepo: IRaceRepository = {
const mockRaceRepo: any = {
findById: vi.fn(),
findAll: mockRaceFindAll,
findByLeagueId: vi.fn(),
@@ -27,7 +24,7 @@ describe('GetAllRacesPageDataUseCase', () => {
};
const mockLeagueFindAll = vi.fn();
const mockLeagueRepo: ILeagueRepository = {
const mockLeagueRepo: any = {
findById: vi.fn(),
findAll: mockLeagueFindAll,
findByOwnerId: vi.fn(),
@@ -47,9 +44,9 @@ describe('GetAllRacesPageDataUseCase', () => {
beforeEach(() => {
vi.clearAllMocks();
});
});
it('should present races and filters data', async () => {
it('should return races and filters data', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
@@ -95,8 +92,8 @@ describe('GetAllRacesPageDataUseCase', () => {
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = expect(presented.races).toEqual([
const presented = result.unwrap();
expect(presented.races).toEqual([
{
id: 'race2',
track: 'Track B',
@@ -134,7 +131,7 @@ describe('GetAllRacesPageDataUseCase', () => {
});
});
it('should present empty result when no races or leagues', async () => {
it('should return empty result when no races or leagues', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
@@ -145,8 +142,8 @@ describe('GetAllRacesPageDataUseCase', () => {
const result = await useCase.execute({});
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = expect(presented.races).toEqual([]);
const presented = result.unwrap();
expect(presented.races).toEqual([]);
expect(presented.filters).toEqual({
statuses: [
{ value: 'all', label: 'All Statuses' },
@@ -159,7 +156,7 @@ describe('GetAllRacesPageDataUseCase', () => {
});
});
it('should return error when repository throws and not present data', async () => {
it('should return error when repository throws', async () => {
const useCase = new GetAllRacesPageDataUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
@@ -173,5 +170,5 @@ describe('GetAllRacesPageDataUseCase', () => {
const err = result.unwrapErr();
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('Repository error');
});
});
});

View File

@@ -1,6 +1,3 @@
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { RaceStatusValue } from '../../domain/entities/Race';

View File

@@ -1,18 +1,17 @@
import { describe, it, expect, beforeEach, vi } from 'vitest';
import {
GetAllRacesUseCase,
type GetAllRacesResult,
type GetAllRacesInput,
} from './GetAllRacesUseCase';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { Logger } from '@core/shared/application';
import { Race } from '../../domain/entities/Race';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { League } from '../../domain/entities/League';
import { Race } from '../../domain/entities/Race';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import {
GetAllRacesUseCase,
type GetAllRacesInput
} from './GetAllRacesUseCase';
describe('GetAllRacesUseCase', () => {
const mockRaceFindAll = vi.fn();
const mockRaceRepo: IRaceRepository = {
const mockRaceRepo: RaceRepository = {
findById: vi.fn(),
findAll: mockRaceFindAll,
findByLeagueId: vi.fn(),
@@ -27,7 +26,7 @@ describe('GetAllRacesUseCase', () => {
};
const mockLeagueFindAll = vi.fn();
const mockLeagueRepo: ILeagueRepository = {
const mockLeagueRepo: LeagueRepository = {
findById: vi.fn(),
findAll: mockLeagueFindAll,
findByOwnerId: vi.fn(),
@@ -47,13 +46,12 @@ describe('GetAllRacesUseCase', () => {
beforeEach(() => {
vi.clearAllMocks();
});
});
it('should present domain races and leagues data', async () => {
it('should return domain races and leagues data', async () => {
const useCase = new GetAllRacesUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
useCase.setOutput(output);
const race1 = Race.create({
id: 'race1',
@@ -95,17 +93,16 @@ describe('GetAllRacesUseCase', () => {
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = expect(presented.totalCount).toBe(2);
const presented = result.unwrap();
expect(presented.totalCount).toBe(2);
expect(presented.races).toEqual([race1, race2]);
expect(presented.leagues).toEqual([league1, league2]);
});
it('should present empty result when no races or leagues', async () => {
it('should return empty result when no races or leagues', async () => {
const useCase = new GetAllRacesUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
useCase.setOutput(output);
mockRaceFindAll.mockResolvedValue([]);
mockLeagueFindAll.mockResolvedValue([]);
@@ -113,13 +110,13 @@ describe('GetAllRacesUseCase', () => {
const result = await useCase.execute({});
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = expect(presented.totalCount).toBe(0);
const presented = result.unwrap();
expect(presented.totalCount).toBe(0);
expect(presented.races).toEqual([]);
expect(presented.leagues).toEqual([]);
});
it('should return error when repository throws and not present data', async () => {
it('should return error when repository throws', async () => {
const useCase = new GetAllRacesUseCase(mockRaceRepo,
mockLeagueRepo,
mockLogger);
@@ -133,5 +130,5 @@ describe('GetAllRacesUseCase', () => {
const err = result.unwrapErr();
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('Repository error');
});
});
});

View File

@@ -1,10 +1,7 @@
import type { RaceRepository } from '../../domain/repositories/RaceRepository';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { Race } from '../../domain/entities/Race';
import type { League } from '../../domain/entities/League';
import type { Race } from '../../domain/entities/Race';
export type GetAllRacesInput = {};

View File

@@ -1,13 +1,13 @@
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { GetAllTeamsUseCase, type GetAllTeamsInput, type GetAllTeamsResult } from './GetAllTeamsUseCase';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamStatsRepository } from '../../domain/repositories/TeamStatsRepository';
import type { ResultRepository } from '../../domain/repositories/ResultRepository';
import type { Logger } from '@core/shared/application';
import { GetAllTeamsUseCase, type GetAllTeamsInput } from './GetAllTeamsUseCase';
describe('GetAllTeamsUseCase', () => {
const mockTeamFindAll = vi.fn();
const mockTeamRepo: ITeamRepository = {
const mockTeamRepo: TeamRepository = {
findById: vi.fn(),
findAll: mockTeamFindAll,
findByLeagueId: vi.fn(),
@@ -18,7 +18,7 @@ describe('GetAllTeamsUseCase', () => {
};
const mockTeamMembershipCountByTeamId = vi.fn();
const mockTeamMembershipRepo: ITeamMembershipRepository = {
const mockTeamMembershipRepo: TeamMembershipRepository = {
getMembership: vi.fn(),
getActiveMembershipForDriver: vi.fn(),
getTeamMembers: vi.fn(),
@@ -30,28 +30,13 @@ describe('GetAllTeamsUseCase', () => {
removeJoinRequest: vi.fn(),
};
const mockTeamStatsRepo: ITeamStatsRepository = {
const mockTeamStatsRepo: TeamStatsRepository = {
getTeamStats: vi.fn(),
saveTeamStats: vi.fn(),
getAllStats: vi.fn(),
clear: vi.fn(),
};
const mockResultRepo: IResultRepository = {
findAll: vi.fn(),
findById: vi.fn(),
findByRaceId: vi.fn(),
findByDriverId: vi.fn(),
findByDriverIdAndLeagueId: vi.fn(),
create: vi.fn(),
createMany: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
deleteByRaceId: vi.fn(),
exists: vi.fn(),
existsByRaceId: vi.fn(),
};
const mockLogger: Logger = {
debug: vi.fn(),
info: vi.fn(),
@@ -61,20 +46,19 @@ describe('GetAllTeamsUseCase', () => {
beforeEach(() => {
vi.clearAllMocks();
});
});
it('should return teams data', async () => {
const useCase = new GetAllTeamsUseCase(mockTeamRepo,
mockTeamMembershipRepo,
mockTeamStatsRepo,
mockResultRepo,
mockLogger);
const team1 = {
id: 'team1',
name: { props: 'Team One' },
tag: { props: 'TO' },
description: { props: 'Description One' },
id: { toString: () => 'team1' },
name: { toString: () => 'Team One' },
tag: { toString: () => 'TO' },
description: { toString: () => 'Description One' },
ownerId: { toString: () => 'owner1' },
leagues: [{ toString: () => 'league1' }],
createdAt: { toDate: () => new Date('2023-01-01T00:00:00Z') },
@@ -83,10 +67,10 @@ describe('GetAllTeamsUseCase', () => {
isRecruiting: false,
};
const team2 = {
id: 'team2',
name: { props: 'Team Two' },
tag: { props: 'TT' },
description: { props: 'Description Two' },
id: { toString: () => 'team2' },
name: { toString: () => 'Team Two' },
tag: { toString: () => 'TT' },
description: { toString: () => 'Description Two' },
ownerId: { toString: () => 'owner2' },
leagues: [{ toString: () => 'league2' }],
createdAt: { toDate: () => new Date('2023-01-02T00:00:00Z') },
@@ -98,7 +82,6 @@ describe('GetAllTeamsUseCase', () => {
mockTeamFindAll.mockResolvedValue([team1, team2]);
mockTeamMembershipCountByTeamId.mockImplementation((id: string) => Promise.resolve(id === 'team1' ? 5 : 3));
// Provide precomputed stats so the use case doesn't compute from results.
(mockTeamStatsRepo.getTeamStats as unknown as Mock).mockImplementation((teamId: string) =>
Promise.resolve(
teamId === 'team1'
@@ -126,62 +109,21 @@ describe('GetAllTeamsUseCase', () => {
const result = await useCase.execute({} as GetAllTeamsInput);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = expect(presented).toEqual({
teams: [
{
id: 'team1',
name: 'Team One',
tag: 'TO',
description: 'Description One',
ownerId: 'owner1',
leagues: ['league1'],
createdAt: new Date('2023-01-01T00:00:00Z'),
memberCount: 5,
totalWins: 2,
totalRaces: 10,
performanceLevel: 'intermediate',
specialization: 'mixed',
region: 'EU',
languages: ['en'],
logoRef: team1.logoRef,
logoUrl: null,
rating: 1200,
category: undefined,
isRecruiting: false,
},
{
id: 'team2',
name: 'Team Two',
tag: 'TT',
description: 'Description Two',
ownerId: 'owner2',
leagues: ['league2'],
createdAt: new Date('2023-01-02T00:00:00Z'),
memberCount: 3,
totalWins: 5,
totalRaces: 20,
performanceLevel: 'advanced',
specialization: 'mixed',
region: 'US',
languages: ['en', 'de'],
logoRef: team2.logoRef,
logoUrl: null,
rating: 1400,
category: undefined,
isRecruiting: true,
},
],
totalCount: 2,
});
expect(presented.totalCount).toBe(2);
expect(presented.teams[0].team).toBe(team1);
expect(presented.teams[0].memberCount).toBe(5);
expect(presented.teams[0].rating).toBe(1200);
expect(presented.teams[1].team).toBe(team2);
expect(presented.teams[1].memberCount).toBe(3);
expect(presented.teams[1].rating).toBe(1400);
});
it('should return empty result when no teams', async () => {
const useCase = new GetAllTeamsUseCase(mockTeamRepo,
mockTeamMembershipRepo,
mockTeamStatsRepo,
mockResultRepo,
mockLogger);
mockTeamFindAll.mockResolvedValue([]);
@@ -189,19 +131,16 @@ describe('GetAllTeamsUseCase', () => {
const result = await useCase.execute({} as GetAllTeamsInput);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = expect(presented).toEqual({
teams: [],
totalCount: 0,
});
expect(presented.teams).toEqual([]);
expect(presented.totalCount).toBe(0);
});
it('should return error when repository throws', async () => {
const useCase = new GetAllTeamsUseCase(mockTeamRepo,
mockTeamMembershipRepo,
mockTeamStatsRepo,
mockResultRepo,
mockLogger);
const error = new Error('Repository error');
@@ -214,6 +153,5 @@ describe('GetAllTeamsUseCase', () => {
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('Repository error');
});
});
});

View File

@@ -1,10 +1,7 @@
import { Team } from '@/racing/domain/entities/Team';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamStatsRepository } from '../../domain/repositories/TeamStatsRepository';
import type { Team } from '../../domain/entities/Team';
export interface GetAllTeamsInput {}

View File

@@ -1,8 +1,7 @@
import { describe, it, expect, vi } from 'vitest';
import { GetDriverLiveriesUseCase, type GetDriverLiveriesInput } from './GetDriverLiveriesUseCase';
import type { LiveryRepository } from '../../domain/repositories/LiveryRepository';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi } from 'vitest';
import type { DriverLivery } from '../../domain/entities/DriverLivery';
import type { Logger } from '@core/shared/application';
import { GetDriverLiveriesUseCase, type GetDriverLiveriesInput } from './GetDriverLiveriesUseCase';
describe('GetDriverLiveriesUseCase', () => {
const mockLiveryRepository: ILiveryRepository = {

View File

@@ -4,12 +4,11 @@
* Retrieves all liveries for a specific driver.
*/
import { Result } from '@core/shared/domain/Result';
import { DriverLivery } from '@/racing/domain/entities/DriverLivery';
import { UseCase } from '@core/shared/application/UseCase';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { LiveryRepository } from '../../domain/repositories/LiveryRepository';
import type { DriverLivery } from '../../domain/entities/DriverLivery';
export interface GetDriverLiveriesInput {
driverId: string;

View File

@@ -1,43 +1,42 @@
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import { GetDriverTeamUseCase, type GetDriverTeamInput, type GetDriverTeamResult } from './GetDriverTeamUseCase';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { GetDriverTeamUseCase, type GetDriverTeamInput } from './GetDriverTeamUseCase';
describe('GetDriverTeamUseCase', () => {
const mockFindById = vi.fn();
const mockGetActiveMembershipForDriver = vi.fn();
const mockTeamRepo: ITeamRepository = {
findById: mockFindById,
findAll: vi.fn(),
findByLeagueId: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
exists: vi.fn(),
};
const mockMembershipRepo: ITeamMembershipRepository = {
getActiveMembershipForDriver: mockGetActiveMembershipForDriver,
getMembership: vi.fn(),
getTeamMembers: vi.fn(),
saveMembership: vi.fn(),
removeMembership: vi.fn(),
getJoinRequests: vi.fn(),
countByTeamId: vi.fn(),
saveJoinRequest: vi.fn(),
removeJoinRequest: vi.fn(),
};
const mockLogger: Logger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
let mockTeamRepo: any;
let mockMembershipRepo: any;
let mockLogger: Logger;
beforeEach(() => {
vi.clearAllMocks();
});
mockTeamRepo = {
findById: vi.fn(),
findAll: vi.fn(),
findByLeagueId: vi.fn(),
create: vi.fn(),
update: vi.fn(),
delete: vi.fn(),
exists: vi.fn(),
};
mockMembershipRepo = {
getActiveMembershipForDriver: vi.fn(),
getMembership: vi.fn(),
getTeamMembers: vi.fn(),
saveMembership: vi.fn(),
removeMembership: vi.fn(),
getJoinRequests: vi.fn(),
countByTeamId: vi.fn(),
saveJoinRequest: vi.fn(),
removeJoinRequest: vi.fn(),
};
mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
});
it('should return driver team data when membership and team exist', async () => {
const useCase = new GetDriverTeamUseCase(mockTeamRepo,
@@ -48,15 +47,15 @@ describe('GetDriverTeamUseCase', () => {
const membership = { id: 'membership1', driverId, teamId: 'team1' };
const team = { id: 'team1', name: 'Team One' };
mockGetActiveMembershipForDriver.mockResolvedValue(membership);
mockFindById.mockResolvedValue(team);
mockMembershipRepo.getActiveMembershipForDriver.mockResolvedValue(membership);
mockTeamRepo.findById.mockResolvedValue(team);
const input: GetDriverTeamInput = { driverId };
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const [[presented]] = (expect(presented.driverId).toBe(driverId);
const presented = result.unwrap();
expect(presented.driverId).toBe(driverId);
expect(presented.team).toBe(team);
expect(presented.membership).toBe(membership);
});
@@ -68,7 +67,7 @@ describe('GetDriverTeamUseCase', () => {
const driverId = 'driver1';
mockGetActiveMembershipForDriver.mockResolvedValue(null);
mockMembershipRepo.getActiveMembershipForDriver.mockResolvedValue(null);
const input: GetDriverTeamInput = { driverId };
const result = await useCase.execute(input);
@@ -76,7 +75,7 @@ describe('GetDriverTeamUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('MEMBERSHIP_NOT_FOUND');
expect(result.unwrapErr().details.message).toBe('No active membership found for driver driver1');
});
});
it('should return error when team not found', async () => {
const useCase = new GetDriverTeamUseCase(mockTeamRepo,
@@ -86,8 +85,8 @@ describe('GetDriverTeamUseCase', () => {
const driverId = 'driver1';
const membership = { id: 'membership1', driverId, teamId: 'team1' };
mockGetActiveMembershipForDriver.mockResolvedValue(membership);
mockFindById.mockResolvedValue(null);
mockMembershipRepo.getActiveMembershipForDriver.mockResolvedValue(membership);
mockTeamRepo.findById.mockResolvedValue(null);
const input: GetDriverTeamInput = { driverId };
const result = await useCase.execute(input);
@@ -95,7 +94,7 @@ describe('GetDriverTeamUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('TEAM_NOT_FOUND');
expect(result.unwrapErr().details.message).toBe('Team not found for teamId team1');
});
});
it('should return error when repository throws', async () => {
const useCase = new GetDriverTeamUseCase(mockTeamRepo,
@@ -105,7 +104,7 @@ describe('GetDriverTeamUseCase', () => {
const driverId = 'driver1';
const error = new Error('Repository error');
mockGetActiveMembershipForDriver.mockRejectedValue(error);
mockMembershipRepo.getActiveMembershipForDriver.mockRejectedValue(error);
const input: GetDriverTeamInput = { driverId };
const result = await useCase.execute(input);
@@ -113,5 +112,5 @@ describe('GetDriverTeamUseCase', () => {
expect(result.isErr()).toBe(true);
expect(result.unwrapErr().code).toBe('REPOSITORY_ERROR');
expect(result.unwrapErr().details.message).toBe('Repository error');
});
});
});
});

View File

@@ -1,10 +1,8 @@
import { TeamMembership } from '@/racing/domain/types/TeamMembership';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import type { Team } from '../../domain/entities/Team';
import type { TeamRepository } from '../../domain/repositories/TeamRepository';
import type { TeamMembershipRepository } from '../../domain/repositories/TeamMembershipRepository';
import type { TeamMembership } from '../../domain/types/TeamMembership';
export interface GetDriverTeamInput {
driverId: string;

View File

@@ -1,13 +1,9 @@
import { describe, it, expect, vi } from 'vitest';
import type { Logger } from '@core/shared/domain/Logger';
import { describe, expect, it, vi } from 'vitest';
import {
GetDriversLeaderboardUseCase,
type GetDriversLeaderboardInput,
type GetDriversLeaderboardResult
GetDriversLeaderboardUseCase,
type GetDriversLeaderboardInput
} from './GetDriversLeaderboardUseCase';
import type { DriverRepository } from '../../domain/repositories/DriverRepository';
import type { RankingUseCase } from './RankingUseCase';
import type { DriverStatsUseCase } from './DriverStatsUseCase';
import type { Logger } from '@core/shared/application';
describe('GetDriversLeaderboardUseCase', () => {
const mockDriverFindAll = vi.fn();

View File

@@ -1,25 +1,18 @@
import { describe, it, expect, beforeEach, vi, Mock } from 'vitest';
import {
GetEntitySponsorshipPricingUseCase,
type GetEntitySponsorshipPricingInput,
type GetEntitySponsorshipPricingResult,
} from './GetEntitySponsorshipPricingUseCase';
import type { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { beforeEach, describe, expect, it, Mock, vi } from 'vitest';
import {
GetEntitySponsorshipPricingUseCase,
type GetEntitySponsorshipPricingInput
} from './GetEntitySponsorshipPricingUseCase';
describe('GetEntitySponsorshipPricingUseCase', () => {
let mockSponsorshipPricingRepo: ISponsorshipPricingRepository;
let mockSponsorshipPricingRepo: any;
let mockLogger: Logger;
let mockFindByEntity: Mock;
let mockFindPendingByEntity: Mock;
let mockFindBySeasonId: Mock;
};
beforeEach(() => {
mockFindByEntity = vi.fn();
mockFindPendingByEntity = vi.fn();
mockFindBySeasonId = vi.fn();
mockSponsorshipPricingRepo = {
findByEntity: mockFindByEntity,
findAll: vi.fn(),
@@ -29,18 +22,17 @@ describe('GetEntitySponsorshipPricingUseCase', () => {
save: vi.fn(),
exists: vi.fn(),
findAcceptingApplications: vi.fn(),
} as ISponsorshipPricingRepository;
};
mockLogger = {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
};
output = { present: vi.fn() } as unknown as typeof output;
});
it('should return PRICING_NOT_CONFIGURED when no pricing found', async () => {
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as any,
mockLogger);
const dto: GetEntitySponsorshipPricingInput = {
@@ -59,10 +51,10 @@ describe('GetEntitySponsorshipPricingUseCase', () => {
>;
expect(err.code).toBe('PRICING_NOT_CONFIGURED');
expect(err.details.message).toContain('No sponsorship pricing configured');
});
});
it('should return pricing data when found', async () => {
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as any,
mockLogger);
const dto: GetEntitySponsorshipPricingInput = {
@@ -88,14 +80,12 @@ describe('GetEntitySponsorshipPricingUseCase', () => {
};
mockFindByEntity.mockResolvedValue(pricing);
mockFindPendingByEntity.mockResolvedValue([]);
mockFindBySeasonId.mockResolvedValue([]);
const result = await useCase.execute(dto);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = (expect(presented.entityType).toBe('season');
const presented = result.unwrap();
expect(presented.entityType).toBe('season');
expect(presented.entityId).toBe('season1');
expect(presented.acceptingApplications).toBe(true);
expect(presented.customRequirements).toBe('Some requirements');
@@ -116,7 +106,7 @@ describe('GetEntitySponsorshipPricingUseCase', () => {
});
it('should return error when repository throws', async () => {
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as unknown as ISponsorshipPricingRepository,
const useCase = new GetEntitySponsorshipPricingUseCase(mockSponsorshipPricingRepo as any,
mockLogger);
const dto: GetEntitySponsorshipPricingInput = {
@@ -137,5 +127,5 @@ describe('GetEntitySponsorshipPricingUseCase', () => {
>;
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('Repository error');
});
});
});

View File

@@ -5,8 +5,6 @@
* Used by sponsors to see available slots and prices.
*/
import type { SponsorshipPricingRepository } from '../../domain/repositories/SponsorshipPricingRepository';
import type { Logger } from '@core/shared/application';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { SponsorableEntityType } from '../../domain/entities/SponsorshipRequest';

View File

@@ -1,18 +1,15 @@
import { describe, it, expect, beforeEach, vi, type Mock } from 'vitest';
import {
GetLeagueAdminPermissionsUseCase,
type GetLeagueAdminPermissionsInput,
type GetLeagueAdminPermissionsResult,
type GetLeagueAdminPermissionsErrorCode,
} from './GetLeagueAdminPermissionsUseCase';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
import type { Logger } from '@core/shared/domain/Logger';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { Logger } from '@core/shared/application';
import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest';
import {
GetLeagueAdminPermissionsUseCase,
type GetLeagueAdminPermissionsErrorCode,
type GetLeagueAdminPermissionsInput
} from './GetLeagueAdminPermissionsUseCase';
describe('GetLeagueAdminPermissionsUseCase', () => {
let mockLeagueRepo: ILeagueRepository;
let mockMembershipRepo: ILeagueMembershipRepository;
let mockLeagueRepo: any;
let mockMembershipRepo: any;
let mockFindById: Mock;
let mockGetMembership: Mock;
const logger: Logger = {
@@ -34,7 +31,7 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
exists: vi.fn(),
findByOwnerId: vi.fn(),
searchByName: vi.fn(),
} as ILeagueRepository;
};
mockMembershipRepo = {
getMembership: mockGetMembership,
@@ -46,9 +43,8 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
removeJoinRequest: vi.fn(),
countByLeagueId: vi.fn(),
getLeagueMembers: vi.fn(),
} as ILeagueMembershipRepository;
});
};
});
const createUseCase = () => new GetLeagueAdminPermissionsUseCase(mockLeagueRepo,
mockMembershipRepo,
@@ -59,7 +55,7 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
performerDriverId: 'driver1',
};
it('returns LEAGUE_NOT_FOUND when league does not exist and does not call output', async () => {
it('returns LEAGUE_NOT_FOUND when league does not exist', async () => {
mockFindById.mockResolvedValue(null);
const useCase = createUseCase();
@@ -69,9 +65,9 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<GetLeagueAdminPermissionsErrorCode, { message: string }>;
expect(err.code).toBe('LEAGUE_NOT_FOUND');
expect(err.details.message).toBe('League not found');
});
});
it('returns USER_NOT_MEMBER when membership is missing and does not call output', async () => {
it('returns USER_NOT_MEMBER when membership is missing', async () => {
mockFindById.mockResolvedValue({ id: 'league1' });
mockGetMembership.mockResolvedValue(null);
@@ -82,9 +78,9 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<GetLeagueAdminPermissionsErrorCode, { message: string }>;
expect(err.code).toBe('USER_NOT_MEMBER');
expect(err.details.message).toBe('User is not a member of this league');
});
});
it('returns USER_NOT_MEMBER when membership is not active and does not call output', async () => {
it('returns USER_NOT_MEMBER when membership is not active', async () => {
mockFindById.mockResolvedValue({ id: 'league1' });
mockGetMembership.mockResolvedValue({ status: 'inactive', role: 'admin' });
@@ -95,9 +91,9 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<GetLeagueAdminPermissionsErrorCode, { message: string }>;
expect(err.code).toBe('USER_NOT_MEMBER');
expect(err.details.message).toBe('User is not a member of this league');
});
});
it('returns USER_NOT_MEMBER when role is member and does not call output', async () => {
it('returns USER_NOT_MEMBER when role is member', async () => {
mockFindById.mockResolvedValue({ id: 'league1' });
mockGetMembership.mockResolvedValue({ status: 'active', role: 'member' });
@@ -108,10 +104,10 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<GetLeagueAdminPermissionsErrorCode, { message: string }>;
expect(err.code).toBe('USER_NOT_MEMBER');
expect(err.details.message).toBe('User is not a member of this league');
});
});
it('returns admin permissions for admin role and calls output once', async () => {
const league = { id: 'league1' } as unknown as { id: string };
it('returns admin permissions for admin role', async () => {
const league = { id: 'league1' } as any;
mockFindById.mockResolvedValue(league);
mockGetMembership.mockResolvedValue({ status: 'active', role: 'admin' });
@@ -119,9 +115,9 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = expect(presented.league).toBe(league);
expect(presented.league).toBe(league);
expect(presented.permissions).toEqual({
canManageSchedule: true,
canManageMembers: true,
@@ -130,8 +126,8 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
});
});
it('returns admin permissions for owner role and calls output once', async () => {
const league = { id: 'league1' } as unknown as { id: string };
it('returns admin permissions for owner role', async () => {
const league = { id: 'league1' } as any;
mockFindById.mockResolvedValue(league);
mockGetMembership.mockResolvedValue({ status: 'active', role: 'owner' });
@@ -139,9 +135,9 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const result = await useCase.execute(input);
expect(result.isOk()).toBe(true);
expect(result.unwrap()).toBeUndefined();
const presented = result.unwrap();
const presented = expect(presented.league).toBe(league);
expect(presented.league).toBe(league);
expect(presented.permissions).toEqual({
canManageSchedule: true,
canManageMembers: true,
@@ -150,7 +146,7 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
});
});
it('wraps repository errors in REPOSITORY_ERROR and does not call output', async () => {
it('wraps repository errors in REPOSITORY_ERROR', async () => {
const error = new Error('repo failed');
mockFindById.mockRejectedValue(error);
@@ -161,5 +157,5 @@ describe('GetLeagueAdminPermissionsUseCase', () => {
const err = result.unwrapErr() as ApplicationErrorCode<GetLeagueAdminPermissionsErrorCode, { message: string }>;
expect(err.code).toBe('REPOSITORY_ERROR');
expect(err.details.message).toBe('repo failed');
});
});
});

View File

@@ -1,14 +1,8 @@
import type { Logger } from '@core/shared/application';
import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { League } from '../../domain/entities/League';
import type { LeagueRepository } from '../../domain/repositories/LeagueRepository';
import type { LeagueMembershipRepository } from '../../domain/repositories/LeagueMembershipRepository';
export type GetLeagueAdminPermissionsInput = {
leagueId: string;
performerDriverId: string;
};
export type LeagueAdminPermissions = {
canManageSchedule: boolean;

Some files were not shown because too many files have changed in this diff Show More