website refactor
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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', () => ({
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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,
|
||||
) {}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 = {};
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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 {}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user