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(), jwtVerify: jest.fn(), })); import { CACHE_MANAGER } from "@nestjs/cache-manager"; import { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; import { RbacService } from "../auth/rbac.service"; import { MediaService } from "../media/media.service"; import { S3Service } from "../s3/s3.service"; import { UsersRepository } from "./repositories/users.repository"; import { UsersService } from "./users.service"; describe("UsersService", () => { let service: UsersService; let repository: UsersRepository; const mockUsersRepository = { create: jest.fn(), findOne: jest.fn(), findByEmailHash: jest.fn(), findOneWithPrivateData: jest.fn(), countAll: jest.fn(), findAll: jest.fn(), findByUsername: jest.fn(), update: jest.fn(), getTwoFactorSecret: jest.fn(), getUserContents: jest.fn(), getUserFavorites: jest.fn(), softDeleteUserAndContents: jest.fn(), }; const mockCacheManager = { del: jest.fn(), }; const mockRbacService = { getUserRoles: jest.fn(), }; const mockMediaService = { scanFile: jest.fn(), processImage: jest.fn(), }; const mockS3Service = { uploadFile: jest.fn(), getPublicUrl: jest.fn(), }; const mockConfigService = { get: jest.fn(), }; beforeEach(async () => { jest.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ providers: [ UsersService, { provide: UsersRepository, useValue: mockUsersRepository }, { provide: CACHE_MANAGER, useValue: mockCacheManager }, { provide: RbacService, useValue: mockRbacService }, { provide: MediaService, useValue: mockMediaService }, { provide: S3Service, useValue: mockS3Service }, { provide: ConfigService, useValue: mockConfigService }, ], }).compile(); service = module.get(UsersService); repository = module.get(UsersRepository); }); it("should be defined", () => { expect(service).toBeDefined(); }); describe("create", () => { it("should create a user", async () => { const data = { username: "u1", email: "e1", passwordHash: "p1", emailHash: "eh1", }; mockUsersRepository.create.mockResolvedValue({ uuid: "uuid1", ...data }); const result = await service.create(data); expect(result.uuid).toBe("uuid1"); expect(repository.create).toHaveBeenCalledWith(data); }); }); describe("findOne", () => { it("should find a user", async () => { mockUsersRepository.findOne.mockResolvedValue({ uuid: "uuid1" }); const result = await service.findOne("uuid1"); expect(result.uuid).toBe("uuid1"); }); it("should return null if not found", async () => { mockUsersRepository.findOne.mockResolvedValue(null); const result = await service.findOne("uuid1"); expect(result).toBeNull(); }); }); describe("update", () => { it("should update a user", async () => { mockUsersRepository.update.mockResolvedValue([ { uuid: "uuid1", displayName: "New" }, ]); const result = await service.update("uuid1", { displayName: "New" }); expect(result[0].displayName).toBe("New"); }); }); describe("clearUserCache", () => { it("should delete cache", async () => { await service.clearUserCache("u1"); expect(mockCacheManager.del).toHaveBeenCalledWith("users/profile/u1"); }); }); describe("findByEmailHash", () => { it("should call repository.findByEmailHash", async () => { mockUsersRepository.findByEmailHash.mockResolvedValue({ uuid: "u1" }); const result = await service.findByEmailHash("hash"); expect(result.uuid).toBe("u1"); expect(mockUsersRepository.findByEmailHash).toHaveBeenCalledWith("hash"); }); }); describe("findOneWithPrivateData", () => { it("should return user with roles", async () => { mockUsersRepository.findOneWithPrivateData.mockResolvedValue({ uuid: "u1" }); mockRbacService.getUserRoles.mockResolvedValue(["admin"]); const result = await service.findOneWithPrivateData("u1"); expect(result.roles).toEqual(["admin"]); }); }); describe("findAll", () => { it("should return all users", async () => { mockUsersRepository.findAll.mockResolvedValue([{ uuid: "u1" }]); mockUsersRepository.countAll.mockResolvedValue(1); const result = await service.findAll(10, 0); expect(result.totalCount).toBe(1); expect(result.data[0].uuid).toBe("u1"); }); }); describe("findPublicProfile", () => { it("should return public profile", async () => { mockUsersRepository.findByUsername.mockResolvedValue({ uuid: "u1", username: "u1", }); const result = await service.findPublicProfile("u1"); expect(result.username).toBe("u1"); }); }); describe("updateConsent", () => { it("should update consent", async () => { await service.updateConsent("u1", "v1", "v2"); expect(mockUsersRepository.update).toHaveBeenCalledWith("u1", { termsVersion: "v1", privacyVersion: "v2", gdprAcceptedAt: expect.any(Date), }); }); }); describe("setTwoFactorSecret", () => { it("should set 2fa secret", async () => { await service.setTwoFactorSecret("u1", "secret"); expect(mockUsersRepository.update).toHaveBeenCalledWith("u1", { twoFactorSecret: "secret", }); }); }); describe("toggleTwoFactor", () => { it("should toggle 2fa", async () => { await service.toggleTwoFactor("u1", true); expect(mockUsersRepository.update).toHaveBeenCalledWith("u1", { isTwoFactorEnabled: true, }); }); }); describe("getTwoFactorSecret", () => { it("should return 2fa secret", async () => { mockUsersRepository.getTwoFactorSecret.mockResolvedValue("secret"); const result = await service.getTwoFactorSecret("u1"); expect(result).toBe("secret"); }); }); describe("exportUserData", () => { it("should return all user data", async () => { mockUsersRepository.findOneWithPrivateData.mockResolvedValue({ uuid: "u1" }); mockUsersRepository.getUserContents.mockResolvedValue([]); mockUsersRepository.getUserFavorites.mockResolvedValue([]); const result = await service.exportUserData("u1"); expect(result.profile).toBeDefined(); expect(result.contents).toBeDefined(); expect(result.favorites).toBeDefined(); }); }); describe("remove", () => { it("should soft delete user", async () => { await service.remove("u1"); expect(mockUsersRepository.softDeleteUserAndContents).toHaveBeenCalledWith( "u1", ); }); }); });