test(contents): add unit tests for ContentsController and ContentsService with mocked dependencies
This commit is contained in:
230
backend/src/contents/contents.controller.spec.ts
Normal file
230
backend/src/contents/contents.controller.spec.ts
Normal file
@@ -0,0 +1,230 @@
|
|||||||
|
jest.mock("uuid", () => ({
|
||||||
|
v4: jest.fn(() => "mocked-uuid"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("@noble/post-quantum/ml-kem.js", () => ({
|
||||||
|
ml_kem768: {
|
||||||
|
keygen: jest.fn(),
|
||||||
|
encapsulate: jest.fn(),
|
||||||
|
decapsulate: jest.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock("jose", () => ({
|
||||||
|
SignJWT: jest.fn().mockReturnValue({
|
||||||
|
setProtectedHeader: jest.fn().mockReturnThis(),
|
||||||
|
setIssuedAt: jest.fn().mockReturnThis(),
|
||||||
|
setExpirationTime: jest.fn().mockReturnThis(),
|
||||||
|
sign: jest.fn().mockResolvedValue("mocked-jwt"),
|
||||||
|
}),
|
||||||
|
jwtVerify: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
import { CACHE_MANAGER } from "@nestjs/cache-manager";
|
||||||
|
import { Test, TestingModule } from "@nestjs/testing";
|
||||||
|
import { AuthGuard } from "../auth/guards/auth.guard";
|
||||||
|
import { OptionalAuthGuard } from "../auth/guards/optional-auth.guard";
|
||||||
|
import { RolesGuard } from "../auth/guards/roles.guard";
|
||||||
|
import { AuthenticatedRequest } from "../common/interfaces/request.interface";
|
||||||
|
import { ContentsController } from "./contents.controller";
|
||||||
|
import { ContentsService } from "./contents.service";
|
||||||
|
|
||||||
|
describe("ContentsController", () => {
|
||||||
|
let controller: ContentsController;
|
||||||
|
let service: ContentsService;
|
||||||
|
|
||||||
|
const mockContentsService = {
|
||||||
|
create: jest.fn(),
|
||||||
|
getUploadUrl: jest.fn(),
|
||||||
|
uploadAndProcess: jest.fn(),
|
||||||
|
findAll: jest.fn(),
|
||||||
|
findOne: jest.fn(),
|
||||||
|
incrementViews: jest.fn(),
|
||||||
|
incrementUsage: jest.fn(),
|
||||||
|
remove: jest.fn(),
|
||||||
|
removeAdmin: jest.fn(),
|
||||||
|
generateBotHtml: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCacheManager = {
|
||||||
|
get: jest.fn(),
|
||||||
|
set: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [ContentsController],
|
||||||
|
providers: [
|
||||||
|
{ provide: ContentsService, useValue: mockContentsService },
|
||||||
|
{ provide: CACHE_MANAGER, useValue: mockCacheManager },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.overrideGuard(AuthGuard)
|
||||||
|
.useValue({ canActivate: () => true })
|
||||||
|
.overrideGuard(RolesGuard)
|
||||||
|
.useValue({ canActivate: () => true })
|
||||||
|
.overrideGuard(OptionalAuthGuard)
|
||||||
|
.useValue({ canActivate: () => true })
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
controller = module.get<ContentsController>(ContentsController);
|
||||||
|
service = module.get<ContentsService>(ContentsService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be defined", () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("create", () => {
|
||||||
|
it("should call service.create", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
const dto = { title: "Title", type: "image" as any };
|
||||||
|
await controller.create(req, dto as any);
|
||||||
|
expect(service.create).toHaveBeenCalledWith("user-uuid", dto);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getUploadUrl", () => {
|
||||||
|
it("should call service.getUploadUrl", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
await controller.getUploadUrl(req, "test.jpg");
|
||||||
|
expect(service.getUploadUrl).toHaveBeenCalledWith("user-uuid", "test.jpg");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("upload", () => {
|
||||||
|
it("should call service.uploadAndProcess", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
const file = {} as Express.Multer.File;
|
||||||
|
const dto = { title: "Title" };
|
||||||
|
await controller.upload(req, file, dto as any);
|
||||||
|
expect(service.uploadAndProcess).toHaveBeenCalledWith(
|
||||||
|
"user-uuid",
|
||||||
|
file,
|
||||||
|
dto,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("explore", () => {
|
||||||
|
it("should call service.findAll", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
await controller.explore(
|
||||||
|
req,
|
||||||
|
10,
|
||||||
|
0,
|
||||||
|
"trend",
|
||||||
|
"tag",
|
||||||
|
"cat",
|
||||||
|
"auth",
|
||||||
|
"query",
|
||||||
|
false,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
expect(service.findAll).toHaveBeenCalledWith({
|
||||||
|
limit: 10,
|
||||||
|
offset: 0,
|
||||||
|
sortBy: "trend",
|
||||||
|
tag: "tag",
|
||||||
|
category: "cat",
|
||||||
|
author: "auth",
|
||||||
|
query: "query",
|
||||||
|
favoritesOnly: false,
|
||||||
|
userId: "user-uuid",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("trends", () => {
|
||||||
|
it("should call service.findAll with trend sort", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
await controller.trends(req, 10, 0);
|
||||||
|
expect(service.findAll).toHaveBeenCalledWith({
|
||||||
|
limit: 10,
|
||||||
|
offset: 0,
|
||||||
|
sortBy: "trend",
|
||||||
|
userId: "user-uuid",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("recent", () => {
|
||||||
|
it("should call service.findAll with recent sort", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
await controller.recent(req, 10, 0);
|
||||||
|
expect(service.findAll).toHaveBeenCalledWith({
|
||||||
|
limit: 10,
|
||||||
|
offset: 0,
|
||||||
|
sortBy: "recent",
|
||||||
|
userId: "user-uuid",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("findOne", () => {
|
||||||
|
it("should return json for normal user", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" }, headers: {} } as any;
|
||||||
|
const res = { json: jest.fn(), send: jest.fn() } as any;
|
||||||
|
const content = { id: "1" };
|
||||||
|
mockContentsService.findOne.mockResolvedValue(content);
|
||||||
|
|
||||||
|
await controller.findOne("1", req, res);
|
||||||
|
|
||||||
|
expect(res.json).toHaveBeenCalledWith(content);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return html for bot", async () => {
|
||||||
|
const req = {
|
||||||
|
user: { sub: "user-uuid" },
|
||||||
|
headers: { "user-agent": "Googlebot" },
|
||||||
|
} as any;
|
||||||
|
const res = { json: jest.fn(), send: jest.fn() } as any;
|
||||||
|
const content = { id: "1" };
|
||||||
|
mockContentsService.findOne.mockResolvedValue(content);
|
||||||
|
mockContentsService.generateBotHtml.mockReturnValue("<html></html>");
|
||||||
|
|
||||||
|
await controller.findOne("1", req, res);
|
||||||
|
|
||||||
|
expect(res.send).toHaveBeenCalledWith("<html></html>");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw NotFoundException if not found", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" }, headers: {} } as any;
|
||||||
|
const res = { json: jest.fn(), send: jest.fn() } as any;
|
||||||
|
mockContentsService.findOne.mockResolvedValue(null);
|
||||||
|
|
||||||
|
await expect(controller.findOne("1", req, res)).rejects.toThrow(
|
||||||
|
"Contenu non trouvé",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("incrementViews", () => {
|
||||||
|
it("should call service.incrementViews", async () => {
|
||||||
|
await controller.incrementViews("1");
|
||||||
|
expect(service.incrementViews).toHaveBeenCalledWith("1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("incrementUsage", () => {
|
||||||
|
it("should call service.incrementUsage", async () => {
|
||||||
|
await controller.incrementUsage("1");
|
||||||
|
expect(service.incrementUsage).toHaveBeenCalledWith("1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("remove", () => {
|
||||||
|
it("should call service.remove", async () => {
|
||||||
|
const req = { user: { sub: "user-uuid" } } as AuthenticatedRequest;
|
||||||
|
await controller.remove("1", req);
|
||||||
|
expect(service.remove).toHaveBeenCalledWith("1", "user-uuid");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("removeAdmin", () => {
|
||||||
|
it("should call service.removeAdmin", async () => {
|
||||||
|
await controller.removeAdmin("1");
|
||||||
|
expect(service.removeAdmin).toHaveBeenCalledWith("1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -23,6 +23,7 @@ describe("ContentsService", () => {
|
|||||||
incrementViews: jest.fn(),
|
incrementViews: jest.fn(),
|
||||||
incrementUsage: jest.fn(),
|
incrementUsage: jest.fn(),
|
||||||
softDelete: jest.fn(),
|
softDelete: jest.fn(),
|
||||||
|
softDeleteAdmin: jest.fn(),
|
||||||
findOne: jest.fn(),
|
findOne: jest.fn(),
|
||||||
findBySlug: jest.fn(),
|
findBySlug: jest.fn(),
|
||||||
};
|
};
|
||||||
@@ -147,4 +148,81 @@ describe("ContentsService", () => {
|
|||||||
expect(result[0].views).toBe(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");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user