import { Test, TestingModule } from '@nestjs/testing'; import { ProofController } from './proof.controller'; import { ProofService } from './proof.service'; import { RequestProofDto } from './dto/request-proof.dto'; import { LogProofDto } from './dto/log-proof.dto'; import { BadRequestException, NotFoundException } from '@nestjs/common'; describe('ProofController', () => { let controller: ProofController; let proofService: jest.Mocked; const mockProofService = { getProof: jest.fn(), logVerificationProof: jest.fn(), }; beforeEach(async () => { const module: TestingModule = await Test.createTestingModule({ controllers: [ProofController], providers: [ { provide: ProofService, useValue: mockProofService, }, ], }).compile(); controller = module.get(ProofController); proofService = module.get(ProofService); jest.clearAllMocks(); }); it('should be defined', () => { expect(controller).toBeDefined(); }); describe('requestProof', () => { const validRequestProofDto: RequestProofDto = { id_visit: 'VISIT_001', }; const mockProofResponse = { proof: { pi_a: ['123', '456'], pi_b: [ ['789', '012'], ['345', '678'], ], pi_c: ['901', '234'], protocol: 'groth16', curve: 'bn128', }, publicSignals: ['1', '18'], }; it('should return proof successfully for valid id_visit', async () => { mockProofService.getProof.mockResolvedValue(mockProofResponse); const result = await controller.requestProof(validRequestProofDto); expect(result).toEqual(mockProofResponse); expect(proofService.getProof).toHaveBeenCalledWith(validRequestProofDto); expect(proofService.getProof).toHaveBeenCalledTimes(1); }); it('should throw NotFoundException when id_visit does not exist', async () => { mockProofService.getProof.mockRejectedValue( new NotFoundException('ID Visit tidak ditemukan'), ); await expect( controller.requestProof(validRequestProofDto), ).rejects.toThrow(NotFoundException); expect(proofService.getProof).toHaveBeenCalledWith(validRequestProofDto); }); it('should throw BadRequestException when proof generation fails', async () => { mockProofService.getProof.mockRejectedValue( new BadRequestException( "Can't generate proof from input based on constraint. Please check the input data and try again.", ), ); await expect( controller.requestProof(validRequestProofDto), ).rejects.toThrow(BadRequestException); }); it('should pass dto with empty id_visit to service (validation happens at pipe level)', async () => { const emptyDto: RequestProofDto = { id_visit: '' }; mockProofService.getProof.mockRejectedValue( new NotFoundException('ID Visit tidak ditemukan'), ); await expect(controller.requestProof(emptyDto)).rejects.toThrow( NotFoundException, ); expect(proofService.getProof).toHaveBeenCalledWith(emptyDto); }); }); describe('logVerification', () => { const validLogProofDto: LogProofDto = { id_visit: 'VISIT_001', proof: { pi_a: ['123'], pi_b: [['456']], pi_c: ['789'] }, proofResult: true, timestamp: '2025-12-10T10:00:00Z', }; const mockLogResponse = { response: { txId: 'tx_123', success: true, }, responseData: { id: 'PROOF_VISIT_001', event: 'proof_verification_logged', user_id: '0', payload: 'hashed_payload', }, }; it('should log verification proof successfully', async () => { mockProofService.logVerificationProof.mockResolvedValue(mockLogResponse); const result = await controller.logVerification(validLogProofDto); expect(result).toEqual(mockLogResponse); expect(proofService.logVerificationProof).toHaveBeenCalledWith( validLogProofDto, ); expect(proofService.logVerificationProof).toHaveBeenCalledTimes(1); }); it('should handle service errors gracefully', async () => { mockProofService.logVerificationProof.mockRejectedValue( new Error('Blockchain connection failed'), ); await expect( controller.logVerification(validLogProofDto), ).rejects.toThrow('Blockchain connection failed'); }); it('should pass dto with false proofResult to service', async () => { const failedProofDto: LogProofDto = { ...validLogProofDto, proofResult: false, }; mockProofService.logVerificationProof.mockResolvedValue({ ...mockLogResponse, responseData: { ...mockLogResponse.responseData }, }); await controller.logVerification(failedProofDto); expect(proofService.logVerificationProof).toHaveBeenCalledWith( failedProofDto, ); }); // NOTE: This endpoint intentionally has no authentication // as it is designed for external parties to log verification proofs it('should accept request without authentication (intended for external parties)', async () => { mockProofService.logVerificationProof.mockResolvedValue(mockLogResponse); const result = await controller.logVerification(validLogProofDto); expect(result).toBeDefined(); }); }); });