import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { UpsertMembershipFeeUseCase, type UpsertMembershipFeeInput } from './UpsertMembershipFeeUseCase'; import type { IMembershipFeeRepository } from '../../domain/repositories/IMembershipFeeRepository'; import { MembershipFeeType, type MembershipFee } from '../../domain/entities/MembershipFee'; import type { UseCaseOutputPort } from '@core/shared/application/UseCaseOutputPort'; describe('UpsertMembershipFeeUseCase', () => { let membershipFeeRepository: { findByLeagueId: Mock; create: Mock; update: Mock; }; let output: { present: Mock; }; let useCase: UpsertMembershipFeeUseCase; beforeEach(() => { membershipFeeRepository = { findByLeagueId: vi.fn(), create: vi.fn(), update: vi.fn(), }; output = { present: vi.fn(), }; useCase = new UpsertMembershipFeeUseCase( membershipFeeRepository as unknown as IMembershipFeeRepository, output as unknown as UseCaseOutputPort, ); }); it('creates a fee when none exists and presents it', async () => { vi.useFakeTimers(); vi.setSystemTime(new Date('2025-01-01T00:00:00.000Z')); vi.spyOn(Math, 'random').mockReturnValue(0.123456789); try { const input: UpsertMembershipFeeInput = { leagueId: 'league-1', type: MembershipFeeType.SEASON, amount: 100, }; membershipFeeRepository.findByLeagueId.mockResolvedValue(null); membershipFeeRepository.create.mockImplementation(async (fee: MembershipFee) => ({ ...fee })); const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(membershipFeeRepository.create).toHaveBeenCalledWith( expect.objectContaining({ id: expect.stringMatching(/^fee-1735689600000-[a-z0-9]{9}$/), leagueId: 'league-1', type: MembershipFeeType.SEASON, amount: 100, enabled: true, createdAt: new Date('2025-01-01T00:00:00.000Z'), updatedAt: new Date('2025-01-01T00:00:00.000Z'), }), ); const createdFee = (output.present.mock.calls[0]?.[0] as { fee: MembershipFee }).fee; expect(createdFee.enabled).toBe(true); expect(createdFee.amount).toBe(100); } finally { vi.useRealTimers(); } }); it('updates an existing fee and sets enabled=false when amount is 0', async () => { vi.useFakeTimers(); vi.setSystemTime(new Date('2025-01-02T00:00:00.000Z')); try { const input: UpsertMembershipFeeInput = { leagueId: 'league-1', seasonId: 'season-2', type: MembershipFeeType.MONTHLY, amount: 0, }; const existingFee: MembershipFee = { id: 'fee-1', leagueId: 'league-1', seasonId: 'season-1', type: MembershipFeeType.SEASON, amount: 100, enabled: true, createdAt: new Date('2024-01-01T00:00:00.000Z'), updatedAt: new Date('2024-01-01T00:00:00.000Z'), }; membershipFeeRepository.findByLeagueId.mockResolvedValue(existingFee); membershipFeeRepository.update.mockImplementation(async (fee: MembershipFee) => ({ ...fee })); const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(membershipFeeRepository.update).toHaveBeenCalledWith( expect.objectContaining({ id: 'fee-1', leagueId: 'league-1', seasonId: 'season-2', type: MembershipFeeType.MONTHLY, amount: 0, enabled: false, updatedAt: new Date('2025-01-02T00:00:00.000Z'), }), ); const updatedFee = (output.present.mock.calls[0]?.[0] as { fee: MembershipFee }).fee; expect(updatedFee.enabled).toBe(false); expect(updatedFee.amount).toBe(0); expect(updatedFee.seasonId).toBe('season-2'); expect(updatedFee.type).toBe(MembershipFeeType.MONTHLY); } finally { vi.useRealTimers(); } }); });