import { Test, TestingModule } from '@nestjs/testing'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { UserRole } from './dto/auth.dto'; describe('AuthController', () => { let controller: AuthController; let authService: jest.Mocked; let configService: jest.Mocked; const mockAuthService = { registerUser: jest.fn(), signIn: jest.fn(), }; const mockConfigService = { get: jest.fn(), }; const mockJwtService = { signAsync: jest.fn(), verifyAsync: jest.fn(), }; beforeEach(async () => { jest.clearAllMocks(); const module: TestingModule = await Test.createTestingModule({ controllers: [AuthController], providers: [ { provide: AuthService, useValue: mockAuthService }, { provide: ConfigService, useValue: mockConfigService }, { provide: JwtService, useValue: mockJwtService }, ], }).compile(); controller = module.get(AuthController); authService = module.get(AuthService); configService = module.get(ConfigService); }); it('should be defined', () => { expect(controller).toBeDefined(); }); describe('registerUser', () => { const createUserDto = { nama_lengkap: 'Test User', username: 'testuser', password: 'password123', role: UserRole.User, }; const expectedResponse = { id: BigInt(1), nama_lengkap: 'Test User', username: 'testuser', role: UserRole.User, }; it('should register a new user', async () => { mockAuthService.registerUser.mockResolvedValue(expectedResponse); const result = await controller.registerUser(createUserDto); expect(result).toEqual(expectedResponse); expect(mockAuthService.registerUser).toHaveBeenCalledWith(createUserDto); expect(mockAuthService.registerUser).toHaveBeenCalledTimes(1); }); it('should propagate service errors', async () => { const error = new Error('Service error'); mockAuthService.registerUser.mockRejectedValue(error); await expect(controller.registerUser(createUserDto)).rejects.toThrow( 'Service error', ); }); }); describe('login', () => { const loginDto = { username: 'testuser', password: 'password123', }; const mockSignInResponse = { accessToken: 'jwt-token', csrfToken: 'csrf-token', user: { id: BigInt(1), username: 'testuser', role: 'user', }, }; it('should login user and set cookie in development mode', async () => { mockAuthService.signIn.mockResolvedValue(mockSignInResponse); mockConfigService.get.mockImplementation((key: string) => { if (key === 'NODE_ENV') return 'development'; if (key === 'COOKIE_MAX_AGE') return '3600000'; return undefined; }); const mockResponse = { cookie: jest.fn(), }; const result = await controller.login(loginDto, mockResponse as any); expect(result).toEqual({ user: mockSignInResponse.user, csrfToken: mockSignInResponse.csrfToken, }); expect(mockResponse.cookie).toHaveBeenCalledWith( 'access_token', 'jwt-token', { httpOnly: true, secure: false, // development mode sameSite: 'strict', maxAge: 3600000, }, ); }); it('should login user and set secure cookie in production mode', async () => { mockAuthService.signIn.mockResolvedValue(mockSignInResponse); mockConfigService.get.mockImplementation((key: string) => { if (key === 'NODE_ENV') return 'production'; if (key === 'COOKIE_MAX_AGE') return '3600000'; return undefined; }); const mockResponse = { cookie: jest.fn(), }; await controller.login(loginDto, mockResponse as any); expect(mockResponse.cookie).toHaveBeenCalledWith( 'access_token', 'jwt-token', { httpOnly: true, secure: true, // production mode sameSite: 'strict', maxAge: 3600000, }, ); }); it('should propagate authentication errors', async () => { mockAuthService.signIn.mockRejectedValue( new Error('Invalid credentials'), ); const mockResponse = { cookie: jest.fn(), }; await expect( controller.login(loginDto, mockResponse as any), ).rejects.toThrow('Invalid credentials'); expect(mockResponse.cookie).not.toHaveBeenCalled(); }); }); describe('logout', () => { it('should clear access_token cookie in development mode', () => { mockConfigService.get.mockReturnValue('development'); const mockResponse = { clearCookie: jest.fn(), }; const result = controller.logout(mockResponse as any); expect(result).toEqual({ message: 'Logout berhasil' }); expect(mockResponse.clearCookie).toHaveBeenCalledWith('access_token', { httpOnly: true, secure: false, sameSite: 'strict', }); }); it('should clear access_token cookie with secure flag in production mode', () => { mockConfigService.get.mockReturnValue('production'); const mockResponse = { clearCookie: jest.fn(), }; const result = controller.logout(mockResponse as any); expect(result).toEqual({ message: 'Logout berhasil' }); expect(mockResponse.clearCookie).toHaveBeenCalledWith('access_token', { httpOnly: true, secure: true, sameSite: 'strict', }); }); }); });