fix: translate permanent invalid-token errors on online entrypoints (WealthLedger/backend#59) (#110)
Backend verify / commits (push) Skipped
Backend verify / commits (pull_request) Failing after 33s
Backend verify / security (pull_request) Skipped
Backend verify / verify (pull_request) Skipped
Backend verify / security (push) Successful in 11s
Backend verify / verify (push) Failing after 6m40s
Backend verify / commits (push) Skipped
Backend verify / commits (pull_request) Failing after 33s
Backend verify / security (pull_request) Skipped
Backend verify / verify (pull_request) Skipped
Backend verify / security (push) Successful in 11s
Backend verify / verify (push) Failing after 6m40s
This commit was merged in pull request #110.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type { Server } from 'node:http';
|
||||
|
||||
import { status } from '@grpc/grpc-js';
|
||||
import {
|
||||
ConflictException,
|
||||
INestApplication,
|
||||
@@ -17,6 +18,13 @@ import { StockIncomeService } from '../src/modules/investments/instruments/stock
|
||||
import { StockMarketService } from '../src/modules/investments/instruments/stocks/application/stock-market.service';
|
||||
import { StockScreenerService } from '../src/modules/investments/instruments/stocks/application/stock-screener.service';
|
||||
import { TbankHourlyScheduler } from '../src/modules/investments/tbank-sync/tbank-hourly.scheduler';
|
||||
import {
|
||||
bootOnlineApp,
|
||||
expectSafeBody,
|
||||
failUpstream,
|
||||
onlineUserId,
|
||||
refusals,
|
||||
} from './online-upstream';
|
||||
|
||||
const uid = 'e6123145-9665-43e0-8413-cd61b8aa9b13';
|
||||
describe('Stock HTTP contract', () => {
|
||||
@@ -201,3 +209,40 @@ describe('Stock HTTP contract', () => {
|
||||
expect(response.body).not.toHaveProperty('stack');
|
||||
});
|
||||
});
|
||||
|
||||
// Real services and TbankClient error mapping; only the gRPC transport is stubbed.
|
||||
describe('Stock online reads: upstream failure translation', () => {
|
||||
const rpc = jest.fn();
|
||||
let ctx: Awaited<ReturnType<typeof bootOnlineApp>>;
|
||||
const get = (path: string) => request(ctx.app.getHttpServer()).get(path);
|
||||
// [entrypoint, request, RPC that fails second]: a failure after the first call must translate too.
|
||||
const entrypoints: [string, () => request.Test, string][] = [
|
||||
['search', () => get('/api/v1/stocks?query=SBER'), 'getLastPrices'],
|
||||
['detail', () => get(`/api/v1/stocks/${uid}`), 'shareBy'],
|
||||
['ticker', () => get('/api/v1/stocks/by-ticker/SBER'), 'shareBy'],
|
||||
['company', () => get(`/api/v1/stocks/${uid}/company`), 'getAssetBy'],
|
||||
['screener', () => request(ctx.app.getHttpServer()).post('/api/v1/stocks/screener/search').send({}), 'shares'],
|
||||
];
|
||||
const cases = entrypoints.flatMap(([name, send, later]) =>
|
||||
refusals.flatMap((code) => [undefined, later].map((at) => [name, code, at, send] as const)),
|
||||
);
|
||||
|
||||
beforeAll(async () => {
|
||||
ctx = await bootOnlineApp(rpc);
|
||||
});
|
||||
afterAll(() => ctx.app.close());
|
||||
|
||||
it.each(cases)('%s: upstream auth refusal %s (failing RPC %s) becomes 400/INVALID_TBANK_TOKEN', async (_name, code, at, send) => {
|
||||
failUpstream(rpc, 'share', code, at);
|
||||
const response = await send().set('Authorization', ctx.bearer).expect(400);
|
||||
expectSafeBody(response.body, 400, 'INVALID_TBANK_TOKEN');
|
||||
const saved = await ctx.prisma.tbankConnection.findUniqueOrThrow({ where: { userId: onlineUserId } });
|
||||
expect(saved).toMatchObject({ invalidToken: false, revision: ctx.connection.revision });
|
||||
});
|
||||
|
||||
it.each(entrypoints)('%s: temporary unavailability stays 503/TBANK_UNAVAILABLE', async (_name, send) => {
|
||||
failUpstream(rpc, 'share', status.UNAVAILABLE);
|
||||
const response = await send().set('Authorization', ctx.bearer).expect(503);
|
||||
expectSafeBody(response.body, 503, 'TBANK_UNAVAILABLE');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user