forked from Shared/esg
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)
79 lines
2.3 KiB
TypeScript
79 lines
2.3 KiB
TypeScript
import axios from 'axios';
|
|
import { lookup } from 'dns/promises';
|
|
import * as tls from 'tls';
|
|
|
|
const smokeEnabled = process.env.ESG_SMOKE_ENABLED === 'true';
|
|
const describeSmoke = smokeEnabled ? describe : describe.skip;
|
|
|
|
function requireEnv(name: string): string {
|
|
const value = process.env[name];
|
|
if (!value) {
|
|
throw new Error(`${name} is required for smoke tests`);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
describeSmoke('real provider smoke tests', () => {
|
|
let gatewayBaseUrl: string;
|
|
let inquiryPath: string;
|
|
let accessToken: string;
|
|
let payload: Record<string, unknown>;
|
|
let providerUrl: URL;
|
|
|
|
beforeAll(() => {
|
|
gatewayBaseUrl = requireEnv('SMOKE_GATEWAY_BASE_URL');
|
|
inquiryPath = process.env.SMOKE_INQUIRY_PATH ?? '/inquiry/person';
|
|
accessToken = requireEnv('SMOKE_ACCESS_TOKEN');
|
|
payload = JSON.parse(requireEnv('SMOKE_INQUIRY_PAYLOAD')) as Record<string, unknown>;
|
|
providerUrl = new URL(process.env.SMOKE_PROVIDER_URL ?? gatewayBaseUrl);
|
|
});
|
|
|
|
it('resolves provider DNS', async () => {
|
|
await expect(lookup(providerUrl.hostname)).resolves.toMatchObject({
|
|
address: expect.any(String),
|
|
});
|
|
});
|
|
|
|
it('negotiates SSL/TLS when provider uses HTTPS', async () => {
|
|
if (providerUrl.protocol !== 'https:') {
|
|
return;
|
|
}
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
const socket = tls.connect(
|
|
{
|
|
host: providerUrl.hostname,
|
|
port: Number(providerUrl.port || 443),
|
|
servername: providerUrl.hostname,
|
|
timeout: 10000,
|
|
},
|
|
() => {
|
|
socket.end();
|
|
resolve();
|
|
},
|
|
);
|
|
socket.on('error', reject);
|
|
socket.on('timeout', () => {
|
|
socket.destroy();
|
|
reject(new Error('TLS probe timed out'));
|
|
});
|
|
});
|
|
});
|
|
|
|
it('reaches the gateway and performs a real successful inquiry', async () => {
|
|
const response = await axios.post(`${gatewayBaseUrl}${inquiryPath}`, payload, {
|
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
timeout: Number(process.env.SMOKE_TIMEOUT_MS ?? 30000),
|
|
validateStatus: () => true,
|
|
});
|
|
|
|
expect(response.status).toBeGreaterThanOrEqual(200);
|
|
expect(response.status).toBeLessThan(300);
|
|
expect(response.data).toMatchObject({
|
|
success: true,
|
|
trackingCode: expect.any(String),
|
|
data: expect.any(Object),
|
|
});
|
|
});
|
|
});
|