import * as fs from "node:fs/promises"; import { BadRequestException, Logger } from "@nestjs/common"; import { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; import ffmpeg from "fluent-ffmpeg"; import sharp from "sharp"; import { MediaService } from "./media.service"; import { ImageProcessorStrategy } from "./strategies/image-processor.strategy"; import { VideoProcessorStrategy } from "./strategies/video-processor.strategy"; jest.mock("sharp"); jest.mock("fluent-ffmpeg"); jest.mock("node:fs/promises"); jest.mock("uuid", () => ({ v4: () => "mock-uuid" })); describe("MediaService", () => { let service: MediaService; const mockSharp = { metadata: jest.fn().mockResolvedValue({ width: 100, height: 100 }), webp: jest.fn().mockReturnThis(), toBuffer: jest.fn().mockResolvedValue(Buffer.from("processed")), }; beforeEach(async () => { jest.clearAllMocks(); jest.spyOn(Logger.prototype, "error").mockImplementation(() => {}); jest.spyOn(Logger.prototype, "warn").mockImplementation(() => {}); (sharp as unknown as jest.Mock).mockReturnValue(mockSharp); const module: TestingModule = await Test.createTestingModule({ providers: [ MediaService, ImageProcessorStrategy, VideoProcessorStrategy, { provide: ConfigService, useValue: { get: jest.fn((key) => { if (key === "CLAMAV_HOST") return "localhost"; if (key === "CLAMAV_PORT") return 3310; }), }, }, ], }).compile(); service = module.get(MediaService); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("processImage", () => { it("should process an image to webp", async () => { const result = await service.processImage(Buffer.from("input"), "webp"); expect(result.extension).toBe("webp"); expect(result.buffer).toEqual(Buffer.from("processed")); expect(mockSharp.webp).toHaveBeenCalled(); }); it("should throw BadRequestException on error", async () => { mockSharp.toBuffer.mockRejectedValue(new Error("Sharp error")); await expect(service.processImage(Buffer.from("input"))).rejects.toThrow( BadRequestException, ); }); }); describe("processVideo", () => { it("should process a video", async () => { const mockFfmpegCommand = { toFormat: jest.fn().mockReturnThis(), videoCodec: jest.fn().mockReturnThis(), audioCodec: jest.fn().mockReturnThis(), addOutputOptions: jest.fn().mockReturnThis(), on: jest.fn().mockImplementation(function (event, cb) { if (event === "end") setTimeout(cb, 0); return this; }), save: jest.fn().mockReturnThis(), }; (ffmpeg as unknown as jest.Mock).mockReturnValue(mockFfmpegCommand); (fs.readFile as jest.Mock).mockResolvedValue(Buffer.from("processed-video")); (fs.writeFile as jest.Mock).mockResolvedValue(undefined); (fs.unlink as jest.Mock).mockResolvedValue(undefined); const result = await service.processVideo( Buffer.from("video-input"), "webm", ); expect(result.extension).toBe("webm"); expect(result.buffer).toEqual(Buffer.from("processed-video")); }); }); describe("scanFile", () => { it("should return false if clamav not initialized", async () => { const result = await service.scanFile(Buffer.from(""), "test.txt"); expect(result.isInfected).toBe(false); }); it("should handle virus detection", async () => { // Mock private property to simulate initialized clamscan (service as any).isClamAvInitialized = true; (service as any).clamscan = { scanStream: jest.fn().mockResolvedValue({ isInfected: true, viruses: ["Eicar-Test-Signature"], }), }; const result = await service.scanFile(Buffer.from(""), "test.txt"); expect(result.isInfected).toBe(true); expect(result.virusName).toBe("Eicar-Test-Signature"); }); it("should handle scan error", async () => { (service as any).isClamAvInitialized = true; (service as any).clamscan = { scanStream: jest.fn().mockRejectedValue(new Error("Scan failed")), }; await expect( service.scanFile(Buffer.from(""), "test.txt"), ).rejects.toThrow(); }); }); });