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 { ConfigService } from "@nestjs/config"; import { Test, TestingModule } from "@nestjs/testing"; import { AuthController } from "./auth.controller"; import { AuthService } from "./auth.service"; import { BootstrapService } from "./bootstrap.service"; jest.mock("iron-session", () => ({ getIronSession: jest.fn().mockResolvedValue({ save: jest.fn(), destroy: jest.fn(), }), })); describe("AuthController", () => { let controller: AuthController; let authService: AuthService; let _configService: ConfigService; const mockAuthService = { register: jest.fn(), login: jest.fn(), verifyTwoFactorLogin: jest.fn(), refresh: jest.fn(), }; const mockBootstrapService = { consumeToken: jest.fn(), }; const mockConfigService = { get: jest .fn() .mockReturnValue("complex_password_at_least_32_characters_long"), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [AuthController], providers: [ { provide: AuthService, useValue: mockAuthService }, { provide: BootstrapService, useValue: mockBootstrapService }, { provide: ConfigService, useValue: mockConfigService }, ], }).compile(); controller = module.get(AuthController); authService = module.get(AuthService); _configService = module.get(ConfigService); }); it("should be defined", () => { expect(controller).toBeDefined(); }); describe("register", () => { it("should call authService.register", async () => { const dto = { email: "test@example.com", password: "password", username: "test", }; await controller.register(dto as any); expect(authService.register).toHaveBeenCalledWith(dto); }); }); describe("login", () => { it("should call authService.login and setup session if success", async () => { const dto = { email: "test@example.com", password: "password" }; const req = { ip: "127.0.0.1" } as any; const res = { json: jest.fn() } as any; const loginResult = { access_token: "at", refresh_token: "rt", userId: "1", message: "ok", }; mockAuthService.login.mockResolvedValue(loginResult); await controller.login(dto as any, "ua", req, res); expect(authService.login).toHaveBeenCalledWith(dto, "ua", "127.0.0.1"); expect(res.json).toHaveBeenCalledWith({ message: "ok", userId: "1" }); }); it("should return result if no access_token", async () => { const dto = { email: "test@example.com", password: "password" }; const req = { ip: "127.0.0.1" } as any; const res = { json: jest.fn() } as any; const loginResult = { message: "2fa_required", userId: "1" }; mockAuthService.login.mockResolvedValue(loginResult); await controller.login(dto as any, "ua", req, res); expect(res.json).toHaveBeenCalledWith(loginResult); }); }); describe("verifyTwoFactor", () => { it("should call authService.verifyTwoFactorLogin and setup session", async () => { const dto = { userId: "1", token: "123456" }; const req = { ip: "127.0.0.1" } as any; const res = { json: jest.fn() } as any; const verifyResult = { access_token: "at", refresh_token: "rt", message: "ok", }; mockAuthService.verifyTwoFactorLogin.mockResolvedValue(verifyResult); await controller.verifyTwoFactor(dto, "ua", req, res); expect(authService.verifyTwoFactorLogin).toHaveBeenCalledWith( "1", "123456", "ua", "127.0.0.1", ); expect(res.json).toHaveBeenCalledWith({ message: "ok" }); }); }); describe("refresh", () => { it("should refresh token if session has refresh token", async () => { const { getIronSession } = require("iron-session"); const session = { refreshToken: "rt", save: jest.fn() }; getIronSession.mockResolvedValue(session); const req = {} as any; const res = { json: jest.fn() } as any; mockAuthService.refresh.mockResolvedValue({ access_token: "at2", refresh_token: "rt2", }); await controller.refresh(req, res); expect(authService.refresh).toHaveBeenCalledWith("rt"); expect(res.json).toHaveBeenCalledWith({ message: "Token refreshed" }); }); it("should return 401 if no refresh token", async () => { const { getIronSession } = require("iron-session"); const session = { save: jest.fn() }; getIronSession.mockResolvedValue(session); const req = {} as any; const res = { status: jest.fn().mockReturnThis(), json: jest.fn() } as any; await controller.refresh(req, res); expect(res.status).toHaveBeenCalledWith(401); }); }); describe("logout", () => { it("should destroy session", async () => { const { getIronSession } = require("iron-session"); const session = { destroy: jest.fn() }; getIronSession.mockResolvedValue(session); const req = {} as any; const res = { json: jest.fn() } as any; await controller.logout(req, res); expect(session.destroy).toHaveBeenCalled(); expect(res.json).toHaveBeenCalledWith({ message: "User logged out" }); }); }); });