forked from Shared/esg
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)
This commit is contained in:
145
test/unit/helpers-and-mappers.spec.ts
Normal file
145
test/unit/helpers-and-mappers.spec.ts
Normal file
@@ -0,0 +1,145 @@
|
||||
import { maskPayload } from '../../src/common/helpers/mask-payload.helper';
|
||||
import { buildValidationErrorResponse } from '../../src/common/helpers/validation-error.helper';
|
||||
import {
|
||||
getShahkarProviderError,
|
||||
isShahkarSuccess,
|
||||
normalizeShahkarFields,
|
||||
parseShahkarInqueryResult,
|
||||
} from '../../src/common/helpers/shahkar-response.helper';
|
||||
import { getSayahProviderError } from '../../src/common/helpers/sayah-response.helper';
|
||||
import { getCentInsurProviderError } from '../../src/common/helpers/centinsur-response.helper';
|
||||
import { InquiryType } from '../../src/common/enums/inquiry-type.enum';
|
||||
import { ClientType } from '../../src/common/enums/client-type.enum';
|
||||
import { Role } from '../../src/common/enums/role.enum';
|
||||
import { UserMapper } from '../../src/users/mappers/user.mapper';
|
||||
import {
|
||||
createClientConfig,
|
||||
resolveClientConfig,
|
||||
} from '../support/client-config.factory';
|
||||
|
||||
describe('unit helpers and mappers', () => {
|
||||
it('masks sensitive payload fields recursively without mutating safe fields', () => {
|
||||
expect(
|
||||
maskPayload({
|
||||
nationalCode: '0012345678',
|
||||
nested: { password: 'secret', safe: 'visible' },
|
||||
amount: 100,
|
||||
}),
|
||||
).toEqual({
|
||||
nationalCode: '***MASKED***',
|
||||
nested: { password: '***MASKED***', safe: 'visible' },
|
||||
amount: 100,
|
||||
});
|
||||
});
|
||||
|
||||
it('maps Shahkar provider no-match responses to a stable internal code', () => {
|
||||
const fields = {
|
||||
response: '600',
|
||||
result: 'NotIdentifiedException',
|
||||
comment: '',
|
||||
requestId: 'provider-tracking',
|
||||
};
|
||||
|
||||
expect(isShahkarSuccess(fields)).toBe(false);
|
||||
expect(getShahkarProviderError(fields)).toMatchObject({
|
||||
code: 'INQUIRY_NO_MATCH',
|
||||
providerTrackingCode: 'provider-tracking',
|
||||
});
|
||||
});
|
||||
|
||||
it('parses SOAP Shahkar response fields', () => {
|
||||
const fields = normalizeShahkarFields(parseShahkarInqueryResult(`
|
||||
<ShahkarInqueryResult>
|
||||
<Response>200</Response>
|
||||
<Result>OK: matched</Result>
|
||||
</ShahkarInqueryResult>
|
||||
`) as Record<string, unknown>);
|
||||
|
||||
expect(fields).toMatchObject({ response: '200', result: 'OK: matched' });
|
||||
expect(isShahkarSuccess(fields)).toBe(true);
|
||||
});
|
||||
|
||||
it('maps Sayah and CentInsur business errors', () => {
|
||||
expect(
|
||||
getSayahProviderError({
|
||||
ReturnValue: false,
|
||||
HasError: false,
|
||||
Errors: [{ Code: 'SHEBA', Message: 'Mismatch' }],
|
||||
}),
|
||||
).toMatchObject({ code: 'SHEBA_MISMATCH' });
|
||||
|
||||
expect(
|
||||
getCentInsurProviderError(
|
||||
{ IsSucceed: true, Result: { Result: false, ErrorMessage: null } },
|
||||
InquiryType.REAL_ESTATE,
|
||||
),
|
||||
).toMatchObject({
|
||||
code: 'INQUIRY_NO_MATCH',
|
||||
message: 'No property ownership found for the provided national code and postal code',
|
||||
});
|
||||
});
|
||||
|
||||
it('maps user documents without leaking secrets', () => {
|
||||
const dto = UserMapper.toResponseDto({
|
||||
_id: { toString: () => 'user-1' },
|
||||
fullName: 'Admin User',
|
||||
username: 'admin',
|
||||
email: 'admin@example.com',
|
||||
role: Role.SUPER_ADMIN,
|
||||
clientType: ClientType.INTERNAL,
|
||||
appName: 'gateway',
|
||||
clientName: 'Gateway',
|
||||
isActive: true,
|
||||
isBlocked: false,
|
||||
allowedInquiries: [],
|
||||
requestLimitPerMinute: 10,
|
||||
requestLimitPerDay: 100,
|
||||
totalRequests: 3,
|
||||
createdAt: new Date('2026-01-01T00:00:00Z'),
|
||||
updatedAt: new Date('2026-01-02T00:00:00Z'),
|
||||
password: 'should-not-leak',
|
||||
} as never);
|
||||
|
||||
expect(dto).toMatchObject({ id: 'user-1', username: 'admin' });
|
||||
expect(dto).not.toHaveProperty('password');
|
||||
});
|
||||
|
||||
it('resolves client-specific credentials and network configuration', () => {
|
||||
const vpnClient = createClientConfig({
|
||||
clientId: 'vpn-client',
|
||||
requiresVpn: true,
|
||||
providerBaseUrl: 'https://vpn-only.provider.test',
|
||||
credentials: { username: 'vpn-user', password: 'vpn-password' },
|
||||
});
|
||||
|
||||
expect(resolveClientConfig([createClientConfig(), vpnClient], 'vpn-client')).toMatchObject({
|
||||
providerBaseUrl: 'https://vpn-only.provider.test',
|
||||
credentials: { username: 'vpn-user', password: 'vpn-password' },
|
||||
requiresVpn: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns one prioritized validation error instead of duplicate constraints', () => {
|
||||
const { normalizedError } = buildValidationErrorResponse([
|
||||
{
|
||||
property: 'nationalCode',
|
||||
constraints: {
|
||||
isLength: 'nationalCode must be longer than or equal to 10 characters',
|
||||
matches: 'nationalCode must be exactly 10 digits',
|
||||
},
|
||||
},
|
||||
] as never);
|
||||
|
||||
expect(normalizedError).toMatchObject({
|
||||
code: 'VALIDATION_ERROR',
|
||||
message: 'nationalCode must be exactly 10 digits',
|
||||
messageFa: 'کد ملی باید دقیقا ۱۰ رقم باشد',
|
||||
details: [
|
||||
{
|
||||
field: 'nationalCode',
|
||||
constraints: ['nationalCode must be exactly 10 digits'],
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user