import { ForbiddenException, NotFoundException } from "@nestjs/common"; import { Test, TestingModule } from "@nestjs/testing"; import { ContentsRepository } from "../contents/repositories/contents.repository"; import { EventsGateway } from "../realtime/events.gateway"; import { S3Service } from "../s3/s3.service"; import { CommentsService } from "./comments.service"; import { CommentLikesRepository } from "./repositories/comment-likes.repository"; import { CommentsRepository } from "./repositories/comments.repository"; describe("CommentsService", () => { let service: CommentsService; let repository: CommentsRepository; const mockCommentsRepository = { create: jest.fn(), findAllByContentId: jest.fn(), findOne: jest.fn(), findOneEnriched: jest.fn(), delete: jest.fn(), }; const mockCommentLikesRepository = { addLike: jest.fn(), removeLike: jest.fn(), countByCommentId: jest.fn(), isLikedByUser: jest.fn(), }; const mockContentsRepository = { findOne: jest.fn(), }; const mockS3Service = { getPublicUrl: jest.fn(), }; const mockEventsGateway = { sendToContent: jest.fn(), sendToUser: jest.fn(), }; beforeEach(async () => { jest.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ providers: [ CommentsService, { provide: CommentsRepository, useValue: mockCommentsRepository }, { provide: CommentLikesRepository, useValue: mockCommentLikesRepository }, { provide: ContentsRepository, useValue: mockContentsRepository }, { provide: S3Service, useValue: mockS3Service }, { provide: EventsGateway, useValue: mockEventsGateway }, ], }).compile(); service = module.get(CommentsService); repository = module.get(CommentsRepository); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("create", () => { it("should create a comment", async () => { const userId = "user1"; const contentId = "content1"; const dto = { text: "Nice meme", parentId: undefined }; const createdComment = { id: "c1", ...dto, user: { username: "u1" } }; mockCommentsRepository.create.mockResolvedValue(createdComment); mockCommentsRepository.findOneEnriched.mockResolvedValue(createdComment); mockCommentLikesRepository.countByCommentId.mockResolvedValue(0); mockCommentLikesRepository.isLikedByUser.mockResolvedValue(false); const result = await service.create(userId, contentId, dto); expect(result.id).toBe("c1"); expect(repository.create).toHaveBeenCalledWith({ userId, contentId, text: dto.text, parentId: undefined, }); expect(mockEventsGateway.sendToContent).toHaveBeenCalledWith( contentId, "new_comment", expect.any(Object), ); }); }); describe("findAllByContentId", () => { it("should return comments for a content", async () => { mockCommentsRepository.findAllByContentId.mockResolvedValue([ { id: "c1", user: { avatarUrl: "path" } }, ]); mockCommentLikesRepository.countByCommentId.mockResolvedValue(5); mockCommentLikesRepository.isLikedByUser.mockResolvedValue(true); mockS3Service.getPublicUrl.mockReturnValue("url"); const result = await service.findAllByContentId("content1", "u1"); expect(result).toHaveLength(1); expect(result[0].likesCount).toBe(5); expect(result[0].isLiked).toBe(true); expect(result[0].user.avatarUrl).toBe("url"); }); }); describe("remove", () => { it("should remove comment if owner", async () => { mockCommentsRepository.findOne.mockResolvedValue({ userId: "u1" }); await service.remove("u1", "c1"); expect(repository.delete).toHaveBeenCalledWith("c1"); }); it("should remove comment if admin", async () => { mockCommentsRepository.findOne.mockResolvedValue({ userId: "u1" }); await service.remove("other", "c1", true); expect(repository.delete).toHaveBeenCalledWith("c1"); }); it("should throw NotFoundException if comment does not exist", async () => { mockCommentsRepository.findOne.mockResolvedValue(null); await expect(service.remove("u1", "c1")).rejects.toThrow(NotFoundException); }); it("should throw ForbiddenException if not owner and not admin", async () => { mockCommentsRepository.findOne.mockResolvedValue({ userId: "u1" }); await expect(service.remove("other", "c1")).rejects.toThrow( ForbiddenException, ); }); }); describe("like", () => { it("should add like", async () => { mockCommentsRepository.findOne.mockResolvedValue({ id: "c1" }); await service.like("u1", "c1"); expect(mockCommentLikesRepository.addLike).toHaveBeenCalledWith("c1", "u1"); }); }); describe("unlike", () => { it("should remove like", async () => { mockCommentsRepository.findOne.mockResolvedValue({ id: "c1" }); await service.unlike("u1", "c1"); expect(mockCommentLikesRepository.removeLike).toHaveBeenCalledWith( "c1", "u1", ); }); }); });