Files
esg/test/integration/inquiry.e2e-spec.ts
s.hajizadeh 45ef396466 feat: add Persian error translations and comprehensive test suite
Implement normalized error handling with Persian translations across all
exception types, replace legacy NestJS exceptions with AppException,
and add unit, integration, and smoke tests.

- Add error catalog with gateway-owned codes and Persian messages
- Introduce AppException wrapping normalized error envelopes
- Add translateError helper for automatic messageFa population
- Remove claims module and update provider error normalization
- Add unit tests for error contracts and helper functions
- Add integration tests for admin and inquiry endpoints
- Add smoke tests for real provider connectivity
- Add test support utilities (auth mocks, assertions, app factory)
2026-06-29 17:25:19 +03:30

132 lines
4.6 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { INestApplication } from '@nestjs/common';
import request = require('supertest');
import { InquiryController } from '../../src/inquiry/inquiry.controller';
import { InquiryService } from '../../src/inquiry/inquiry.service';
import { ProviderOrchestratorService } from '../../src/providers/strategy/provider-orchestrator.service';
import { InquiryLogService } from '../../src/logging/inquiry-log.service';
import { JwtAuthGuard } from '../../src/auth/guards/jwt-auth.guard';
import { InquiryAccessGuard } from '../../src/auth/guards/inquiry-access.guard';
import { UserRateLimitGuard } from '../../src/rate-limit/user-rate-limit.guard';
import { inquiryEndpointCases } from '../support/inquiry-endpoints';
import { createHttpTestApp } from '../support/app';
import { authenticatedGuard, passGuard } from '../support/auth';
import {
normalizedProviderError,
providerErrorCases,
successfulProviderResult,
} from '../support/mock-provider';
import { expectErrorEnvelope, expectSuccessEnvelope } from '../support/assertions';
describe('inquiry endpoints (integration)', () => {
let app: INestApplication;
const orchestrator = {
executeWithFallback: jest.fn(),
};
const inquiryLogs = {
create: jest.fn().mockResolvedValue(undefined),
};
beforeAll(async () => {
app = await createHttpTestApp(
{
controllers: [InquiryController],
providers: [
InquiryService,
{ provide: ProviderOrchestratorService, useValue: orchestrator },
{ provide: InquiryLogService, useValue: inquiryLogs },
],
},
(builder) =>
builder
.overrideGuard(JwtAuthGuard)
.useValue(authenticatedGuard())
.overrideGuard(InquiryAccessGuard)
.useValue(passGuard())
.overrideGuard(UserRateLimitGuard)
.useValue(passGuard()),
);
});
beforeEach(() => {
jest.clearAllMocks();
orchestrator.executeWithFallback.mockResolvedValue(successfulProviderResult());
});
afterAll(async () => {
await app.close();
});
describe.each(inquiryEndpointCases)('$name', (endpoint) => {
it('returns a successful response and maps provider data', async () => {
orchestrator.executeWithFallback.mockResolvedValueOnce(
successfulProviderResult({ raw: { mapped: true, inquiryType: endpoint.inquiryType } }),
);
const response = await request(app.getHttpServer())
.post(endpoint.path)
.send(endpoint.validPayload)
.expect(201);
expectSuccessEnvelope(response);
expect(response.body.data).toMatchObject({
mapped: true,
inquiryType: endpoint.inquiryType,
});
expect(orchestrator.executeWithFallback).toHaveBeenCalledWith(
endpoint.inquiryType,
expect.objectContaining(endpoint.validPayload),
expect.objectContaining({ requestId: 'test-request-id' }),
);
});
it('returns validation errors without calling providers', async () => {
const response = await request(app.getHttpServer())
.post(endpoint.path)
.send(endpoint.invalidPayload)
.expect(400);
expectErrorEnvelope(response, 'VALIDATION_ERROR');
if (endpoint.name === 'person inquiry') {
expect(response.body.error).toMatchObject({
message: 'nationalCode must be exactly 10 digits',
messageFa: 'کد ملی باید دقیقا ۱۰ رقم باشد',
details: [
{
field: 'nationalCode',
constraints: ['nationalCode must be exactly 10 digits'],
},
],
});
}
expect(orchestrator.executeWithFallback).not.toHaveBeenCalled();
});
it('rejects malformed JSON without calling providers', async () => {
await request(app.getHttpServer())
.post(endpoint.path)
.set('Content-Type', 'application/json')
.send('{"broken":')
.expect(400);
expect(orchestrator.executeWithFallback).not.toHaveBeenCalled();
});
describe.each(providerErrorCases)('$name', (providerCase) => {
it('maps provider errors into the normalized response envelope', async () => {
orchestrator.executeWithFallback.mockRejectedValueOnce(
normalizedProviderError(providerCase.error),
);
const response = await request(app.getHttpServer())
.post(endpoint.path)
.send(endpoint.validPayload)
.expect(201);
expectErrorEnvelope(response, providerCase.code);
expect(response.body.data).toBeNull();
expect(response.body.error.providerCode).toBe(providerCase.error.providerCode);
});
});
});
});