174 lines
5.3 KiB
TypeScript
174 lines
5.3 KiB
TypeScript
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<ProofService>;
|
|
|
|
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>(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();
|
|
});
|
|
});
|
|
});
|