import { beforeEach, describe, expect, it, vi, type Mock } from 'vitest'; import { GetUserFeedUseCase, type GetUserFeedApplicationError, type GetUserFeedInput, type GetUserFeedResult, } from './GetUserFeedUseCase'; import type { IFeedRepository } from '../../domain/repositories/IFeedRepository'; import type { FeedItem } from '../../domain/types/FeedItem'; import type { Logger, UseCaseOutputPort } from '@core/shared/application'; describe('GetUserFeedUseCase', () => { let feedRepository: IFeedRepository & { getFeedForDriver: Mock }; let logger: Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock }; let output: UseCaseOutputPort & { present: Mock }; let useCase: GetUserFeedUseCase; beforeEach(() => { feedRepository = { getFeedForDriver: vi.fn(), getGlobalFeed: vi.fn(), } as unknown as IFeedRepository & { getFeedForDriver: Mock }; logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), } as unknown as Logger & { debug: Mock; info: Mock; warn: Mock; error: Mock }; output = { present: vi.fn(), } as unknown as UseCaseOutputPort & { present: Mock }; useCase = new GetUserFeedUseCase(feedRepository, logger, output); }); it('presents feed items when repository returns items', async () => { const items: FeedItem[] = [ { id: 'item-1', timestamp: new Date('2025-01-01T00:00:00.000Z'), type: 'friend-joined-league', actorFriendId: 'friend-1', leagueId: 'league-1', headline: 'Friend joined a league', body: 'Friend joined League 1', ctaLabel: 'View league', ctaHref: '/leagues/league-1', }, { id: 'item-2', timestamp: new Date('2025-01-02T00:00:00.000Z'), type: 'friend-finished-race', actorDriverId: 'driver-2', raceId: 'race-1', position: 3, headline: 'Race finished', }, ]; feedRepository.getFeedForDriver.mockResolvedValue(items); const input: GetUserFeedInput = { driverId: 'driver-1', limit: 10 }; const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(result.unwrap()).toBeUndefined(); expect(feedRepository.getFeedForDriver).toHaveBeenCalledTimes(1); expect(feedRepository.getFeedForDriver).toHaveBeenCalledWith('driver-1', 10); expect(output.present).toHaveBeenCalledTimes(1); const presented = (output.present as Mock).mock.calls[0]![0] as GetUserFeedResult; expect(presented.items).toEqual(items); expect(logger.warn).not.toHaveBeenCalled(); }); it('warns and presents empty list when no items exist', async () => { feedRepository.getFeedForDriver.mockResolvedValue([]); const input: GetUserFeedInput = { driverId: 'driver-1' }; const result = await useCase.execute(input); expect(result.isOk()).toBe(true); expect(output.present).toHaveBeenCalledTimes(1); const presented = (output.present as Mock).mock.calls[0]![0] as GetUserFeedResult; expect(presented.items).toEqual([]); expect(logger.warn).toHaveBeenCalledTimes(1); expect((logger.warn as Mock).mock.calls[0]![0]).toBe( 'No feed items found for driverId: driver-1', ); }); it('returns REPOSITORY_ERROR when repository throws', async () => { feedRepository.getFeedForDriver.mockRejectedValue(new Error('DB error')); const input: GetUserFeedInput = { driverId: 'driver-1', limit: 5 }; const result = await useCase.execute(input); expect(result.isErr()).toBe(true); const err = result.unwrapErr() as GetUserFeedApplicationError; expect(err.code).toBe('REPOSITORY_ERROR'); expect(err.details.message).toBe('DB error'); expect(output.present).not.toHaveBeenCalled(); expect(logger.error).toHaveBeenCalledTimes(1); }); });