feat: unify income payment cards and drop period comparisons (WealthLedger/main#33) (#83)
feat: unify income payment cards and drop period comparisons (WealthLedger/main#33)
This commit was merged in pull request #83.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { status } from '@grpc/grpc-js';
|
||||
import { Metadata, status } from '@grpc/grpc-js';
|
||||
import { NotFoundException, ServiceUnavailableException } from '@nestjs/common';
|
||||
import { EventEmitter } from 'node:events';
|
||||
|
||||
import { TbankOperationState } from './tbank-protocol';
|
||||
import { TbankClient } from './tbank.client';
|
||||
@@ -136,6 +137,68 @@ describe('TbankClient operations', () => {
|
||||
);
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
it('distinguishes a gRPC deadline from queue delay without logging upstream secrets', async () => {
|
||||
const logger = { event: jest.fn() };
|
||||
const client = Object.assign(Object.create(TbankClient.prototype) as TbankClient, {
|
||||
logger,
|
||||
queue: { size: 0, run: (operation: () => Promise<unknown>) => operation() },
|
||||
});
|
||||
const channel = { getConnectivityState: () => 1 };
|
||||
const service = {
|
||||
getChannel: () => channel,
|
||||
getAccounts: (_input: unknown, _metadata: unknown, _options: unknown, callback: (error: unknown) => void) => {
|
||||
const call = new EventEmitter();
|
||||
void Promise.resolve().then(() => callback({ code: status.DEADLINE_EXCEEDED, details: 'Bearer secret' }));
|
||||
return call;
|
||||
},
|
||||
};
|
||||
const invoke = (client as unknown as { call: (token: string, service: unknown, method: string, input: object) => Promise<unknown> }).call.bind(client);
|
||||
jest.useFakeTimers();
|
||||
try {
|
||||
const failed = invoke('secret', service, 'getAccounts', {});
|
||||
await Promise.all([
|
||||
expect(failed).rejects.toBeInstanceOf(ServiceUnavailableException),
|
||||
jest.advanceTimersByTimeAsync(1000),
|
||||
]);
|
||||
expect(logger.event).toHaveBeenLastCalledWith('error', 'tbank.rpc.failed', expect.objectContaining({
|
||||
operation: 'getAccounts', grpcCode: status.DEADLINE_EXCEEDED,
|
||||
queueWaitMs: 0, channelState: 'CONNECTING', headersReceived: false,
|
||||
}));
|
||||
expect(JSON.stringify(logger.event.mock.calls)).not.toContain('secret');
|
||||
} finally {
|
||||
jest.useRealTimers();
|
||||
}
|
||||
});
|
||||
|
||||
it('records response headers and a validated upstream tracking ID', async () => {
|
||||
const logger = { event: jest.fn() };
|
||||
const client = Object.assign(Object.create(TbankClient.prototype) as TbankClient, {
|
||||
logger,
|
||||
queue: { size: 0, run: (operation: () => Promise<unknown>) => operation() },
|
||||
});
|
||||
const headers = new Metadata();
|
||||
headers.set('x-tracking-id', '123e4567-e89b-12d3-a456-426614174000');
|
||||
headers.set('authorization', 'Bearer secret');
|
||||
const service = {
|
||||
getChannel: () => ({ getConnectivityState: () => 2 }),
|
||||
getAccounts: (_input: unknown, _metadata: unknown, _options: unknown, callback: (error: null, response: object) => void) => {
|
||||
const call = new EventEmitter();
|
||||
void Promise.resolve().then(() => {
|
||||
call.emit('metadata', headers);
|
||||
callback(null, { accounts: [] });
|
||||
});
|
||||
return call;
|
||||
},
|
||||
};
|
||||
const invoke = (client as unknown as { call: (token: string, service: unknown, method: string, input: object) => Promise<unknown> }).call.bind(client);
|
||||
await expect(invoke('secret', service, 'getAccounts', {})).resolves.toEqual({ accounts: [] });
|
||||
expect(logger.event).toHaveBeenLastCalledWith('debug', 'tbank.rpc.completed', expect.objectContaining({
|
||||
channelState: 'READY', headersReceived: true,
|
||||
trackingId: '123e4567-e89b-12d3-a456-426614174000',
|
||||
}));
|
||||
expect(JSON.stringify(logger.event.mock.calls)).not.toContain('secret');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TbankClient metadata retries', () => {
|
||||
|
||||
Reference in New Issue
Block a user