229 lines
6.8 KiB
TypeScript
229 lines
6.8 KiB
TypeScript
jest.mock("uuid", () => ({
|
|
v4: jest.fn(() => "mocked-uuid"),
|
|
}));
|
|
|
|
import { CACHE_MANAGER } from "@nestjs/cache-manager";
|
|
import { BadRequestException } from "@nestjs/common";
|
|
import { ConfigService } from "@nestjs/config";
|
|
import { Test, TestingModule } from "@nestjs/testing";
|
|
import { MediaService } from "../media/media.service";
|
|
import { S3Service } from "../s3/s3.service";
|
|
import { ContentsService } from "./contents.service";
|
|
import { ContentsRepository } from "./repositories/contents.repository";
|
|
|
|
describe("ContentsService", () => {
|
|
let service: ContentsService;
|
|
let s3Service: S3Service;
|
|
let mediaService: MediaService;
|
|
|
|
const mockContentsRepository = {
|
|
findAll: jest.fn(),
|
|
count: jest.fn(),
|
|
create: jest.fn(),
|
|
incrementViews: jest.fn(),
|
|
incrementUsage: jest.fn(),
|
|
softDelete: jest.fn(),
|
|
softDeleteAdmin: jest.fn(),
|
|
findOne: jest.fn(),
|
|
findBySlug: jest.fn(),
|
|
};
|
|
|
|
const mockS3Service = {
|
|
getUploadUrl: jest.fn(),
|
|
uploadFile: jest.fn(),
|
|
getPublicUrl: jest.fn(),
|
|
};
|
|
|
|
const mockMediaService = {
|
|
scanFile: jest.fn(),
|
|
processImage: jest.fn(),
|
|
processVideo: jest.fn(),
|
|
};
|
|
|
|
const mockConfigService = {
|
|
get: jest.fn(),
|
|
};
|
|
|
|
const mockCacheManager = {
|
|
clear: jest.fn(),
|
|
del: jest.fn(),
|
|
};
|
|
|
|
beforeEach(async () => {
|
|
jest.clearAllMocks();
|
|
|
|
const module: TestingModule = await Test.createTestingModule({
|
|
providers: [
|
|
ContentsService,
|
|
{ provide: ContentsRepository, useValue: mockContentsRepository },
|
|
{ provide: S3Service, useValue: mockS3Service },
|
|
{ provide: MediaService, useValue: mockMediaService },
|
|
{ provide: ConfigService, useValue: mockConfigService },
|
|
{ provide: CACHE_MANAGER, useValue: mockCacheManager },
|
|
],
|
|
}).compile();
|
|
|
|
service = module.get<ContentsService>(ContentsService);
|
|
s3Service = module.get<S3Service>(S3Service);
|
|
mediaService = module.get<MediaService>(MediaService);
|
|
});
|
|
|
|
it("should be defined", () => {
|
|
expect(service).toBeDefined();
|
|
});
|
|
|
|
describe("getUploadUrl", () => {
|
|
it("should return an upload URL", async () => {
|
|
mockS3Service.getUploadUrl.mockResolvedValue("http://s3/url");
|
|
const result = await service.getUploadUrl("user1", "test.png");
|
|
expect(result).toHaveProperty("url", "http://s3/url");
|
|
expect(result).toHaveProperty("key");
|
|
expect(result.key).toContain("uploads/user1/");
|
|
});
|
|
});
|
|
|
|
describe("uploadAndProcess", () => {
|
|
const file = {
|
|
buffer: Buffer.from("test"),
|
|
originalname: "test.png",
|
|
mimetype: "image/png",
|
|
size: 1000,
|
|
} as Express.Multer.File;
|
|
|
|
it("should upload and process an image", async () => {
|
|
mockConfigService.get.mockReturnValue(1024); // max size
|
|
mockMediaService.scanFile.mockResolvedValue({ isInfected: false });
|
|
mockMediaService.processImage.mockResolvedValue({
|
|
buffer: Buffer.from("processed"),
|
|
extension: "webp",
|
|
mimeType: "image/webp",
|
|
size: 500,
|
|
});
|
|
mockContentsRepository.findBySlug.mockResolvedValue(null);
|
|
mockContentsRepository.create.mockResolvedValue({ id: "content-id" });
|
|
|
|
const result = await service.uploadAndProcess("user1", file, {
|
|
title: "Meme",
|
|
type: "meme",
|
|
});
|
|
|
|
expect(mediaService.scanFile).toHaveBeenCalled();
|
|
expect(mediaService.processImage).toHaveBeenCalled();
|
|
expect(s3Service.uploadFile).toHaveBeenCalled();
|
|
expect(result).toEqual({ id: "content-id" });
|
|
});
|
|
|
|
it("should throw if file is infected", async () => {
|
|
mockConfigService.get.mockReturnValue(1024);
|
|
mockMediaService.scanFile.mockResolvedValue({
|
|
isInfected: true,
|
|
virusName: "Eicar",
|
|
});
|
|
|
|
await expect(
|
|
service.uploadAndProcess("user1", file, { title: "X", type: "meme" }),
|
|
).rejects.toThrow(BadRequestException);
|
|
});
|
|
});
|
|
|
|
describe("findAll", () => {
|
|
it("should return contents and total count", async () => {
|
|
mockContentsRepository.count.mockResolvedValue(10);
|
|
mockContentsRepository.findAll.mockResolvedValue([{ id: "1" }]);
|
|
|
|
const result = await service.findAll({ limit: 10, offset: 0 });
|
|
|
|
expect(result.totalCount).toBe(10);
|
|
expect(result.data).toHaveLength(1);
|
|
});
|
|
});
|
|
|
|
describe("incrementViews", () => {
|
|
it("should increment views", async () => {
|
|
mockContentsRepository.incrementViews.mockResolvedValue([
|
|
{ id: "1", views: 1 },
|
|
]);
|
|
const result = await service.incrementViews("1");
|
|
expect(mockContentsRepository.incrementViews).toHaveBeenCalledWith("1");
|
|
expect(result[0].views).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe("incrementUsage", () => {
|
|
it("should increment usage", async () => {
|
|
mockContentsRepository.incrementUsage.mockResolvedValue([
|
|
{ id: "1", usageCount: 1 },
|
|
]);
|
|
await service.incrementUsage("1");
|
|
expect(mockContentsRepository.incrementUsage).toHaveBeenCalledWith("1");
|
|
});
|
|
});
|
|
|
|
describe("remove", () => {
|
|
it("should soft delete content", async () => {
|
|
mockContentsRepository.softDelete.mockResolvedValue({ id: "1" });
|
|
await service.remove("1", "u1");
|
|
expect(mockContentsRepository.softDelete).toHaveBeenCalledWith("1", "u1");
|
|
});
|
|
});
|
|
|
|
describe("removeAdmin", () => {
|
|
it("should soft delete content without checking owner", async () => {
|
|
mockContentsRepository.softDeleteAdmin.mockResolvedValue({ id: "1" });
|
|
await service.removeAdmin("1");
|
|
expect(mockContentsRepository.softDeleteAdmin).toHaveBeenCalledWith("1");
|
|
});
|
|
});
|
|
|
|
describe("findOne", () => {
|
|
it("should return content by id", async () => {
|
|
mockContentsRepository.findOne.mockResolvedValue({
|
|
id: "1",
|
|
storageKey: "k",
|
|
author: { avatarUrl: "a" },
|
|
});
|
|
mockS3Service.getPublicUrl.mockReturnValue("url");
|
|
const result = await service.findOne("1");
|
|
expect(result.id).toBe("1");
|
|
expect(result.url).toBe("url");
|
|
});
|
|
|
|
it("should return content by slug", async () => {
|
|
mockContentsRepository.findOne.mockResolvedValue({
|
|
id: "1",
|
|
slug: "s",
|
|
storageKey: "k",
|
|
});
|
|
const result = await service.findOne("s");
|
|
expect(result.slug).toBe("s");
|
|
});
|
|
});
|
|
|
|
describe("generateBotHtml", () => {
|
|
it("should generate html with og tags", () => {
|
|
const content = { title: "Title", storageKey: "k" };
|
|
mockS3Service.getPublicUrl.mockReturnValue("url");
|
|
const html = service.generateBotHtml(content as any);
|
|
expect(html).toContain("<title>Title</title>");
|
|
expect(html).toContain('content="Title"');
|
|
expect(html).toContain('content="url"');
|
|
});
|
|
});
|
|
|
|
describe("ensureUniqueSlug", () => {
|
|
it("should return original slug if unique", async () => {
|
|
mockContentsRepository.findBySlug.mockResolvedValue(null);
|
|
const slug = (service as any).ensureUniqueSlug("My Title");
|
|
await expect(slug).resolves.toBe("my-title");
|
|
});
|
|
|
|
it("should append counter if not unique", async () => {
|
|
mockContentsRepository.findBySlug
|
|
.mockResolvedValueOnce({ id: "1" })
|
|
.mockResolvedValueOnce(null);
|
|
const slug = await (service as any).ensureUniqueSlug("My Title");
|
|
expect(slug).toBe("my-title-1");
|
|
});
|
|
});
|
|
});
|