133 lines
4.1 KiB
TypeScript
133 lines
4.1 KiB
TypeScript
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>(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(),
|
|
outputOptions: 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();
|
|
});
|
|
});
|
|
});
|