import { describe, it, expect, vi, type Mock } from 'vitest'; import { Readable } from 'node:stream'; import type { Logger } from '@core/shared/application'; import { Result } from '@core/shared/application/Result'; import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode'; import { UploadMediaUseCase, type MulterFile, type UploadMediaErrorCode, type UploadMediaInput, type UploadMediaResult, } from './UploadMediaUseCase'; import type { IMediaRepository } from '../../domain/repositories/IMediaRepository'; import type { MediaStoragePort } from '../ports/MediaStoragePort'; import { Media } from '../../domain/entities/Media'; vi.mock('uuid', () => ({ v4: () => 'media-1', })); describe('UploadMediaUseCase', () => { let mediaRepo: { save: Mock }; let mediaStorage: { uploadMedia: Mock }; let logger: Logger; let useCase: UploadMediaUseCase; const baseFile: MulterFile = { fieldname: 'file', originalname: 'avatar.png', encoding: '7bit', mimetype: 'image/png', size: 123, buffer: Buffer.from('abc'), stream: Readable.from([]), destination: '/tmp', filename: 'avatar.png', path: '/tmp/avatar.png', }; beforeEach(() => { mediaRepo = { save: vi.fn(), }; mediaStorage = { uploadMedia: vi.fn(), }; logger = { debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), } as unknown as Logger; useCase = new UploadMediaUseCase( mediaRepo as unknown as IMediaRepository, mediaStorage as unknown as MediaStoragePort, logger, ); }); it('returns UPLOAD_FAILED when storage returns unsuccessful result', async () => { mediaStorage.uploadMedia.mockResolvedValue({ success: false, errorMessage: 'Upload error', }); const input: UploadMediaInput = { file: baseFile, uploadedBy: 'user-1' }; const result: Result> = await useCase.execute(input); expect(result.isErr()).toBe(true); const err = result.unwrapErr(); expect(err.code).toBe('UPLOAD_FAILED'); expect(err.details?.message).toBe('Upload error'); expect(mediaRepo.save).not.toHaveBeenCalled(); }); it('returns UploadMediaResult on success (includes metadata)', async () => { mediaStorage.uploadMedia.mockResolvedValue({ success: true, url: 'https://example.com/media.png', filename: 'stored.png', }); const input: UploadMediaInput = { file: baseFile, uploadedBy: 'user-1', metadata: { foo: 'bar' }, }; const result = await useCase.execute(input); expect(result).toBeInstanceOf(Result); expect(result.isOk()).toBe(true); expect(mediaStorage.uploadMedia).toHaveBeenCalledWith(baseFile.buffer, { filename: baseFile.originalname, mimeType: baseFile.mimetype, metadata: { foo: 'bar' }, }); expect(mediaRepo.save).toHaveBeenCalledTimes(1); const saved = (mediaRepo.save as unknown as Mock).mock.calls[0]![0] as Media; expect(saved.id).toBe('media-1'); expect(saved.url.value).toBe('https://example.com/media.png'); expect(saved.filename).toBe('stored.png'); expect(saved.originalName).toBe('avatar.png'); expect(saved.mimeType).toBe('image/png'); expect(saved.size).toBe(123); expect(saved.type).toBe('image'); expect(saved.uploadedBy).toBe('user-1'); expect(saved.metadata).toEqual({ foo: 'bar' }); const successResult = result.unwrap(); expect(successResult).toEqual({ mediaId: 'media-1', url: 'https://example.com/media.png', }); }); it('returns REPOSITORY_ERROR when repository throws', async () => { mediaStorage.uploadMedia.mockResolvedValue({ success: true, url: 'https://example.com/media.png', }); mediaRepo.save.mockRejectedValue(new Error('DB error')); const input: UploadMediaInput = { file: baseFile, uploadedBy: 'user-1' }; const result: Result> = await useCase.execute(input); expect(result.isErr()).toBe(true); const err = result.unwrapErr(); expect(err.code).toBe('REPOSITORY_ERROR'); expect(err.details?.message).toBe('DB error'); expect((logger.error as unknown as Mock)).toHaveBeenCalled(); }); });