refactor(tbank): centralize access rules
This commit is contained in:
@@ -4,41 +4,41 @@ import {
|
||||
ServiceUnavailableException,
|
||||
} from '@nestjs/common';
|
||||
|
||||
jest.mock('../prisma/prisma.service', () => ({
|
||||
PrismaService: class PrismaService {},
|
||||
jest.mock('./tbank-access.service', () => ({
|
||||
TbankAccessService: class TbankAccessService {},
|
||||
}));
|
||||
|
||||
import { BrokerageOperationsService } from './brokerage-operations.service';
|
||||
import { TbankCrypto } from './tbank.crypto';
|
||||
import type { TbankAccountContext } from './tbank-access.service';
|
||||
|
||||
describe('BrokerageOperationsService', () => {
|
||||
const key = 'MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY=';
|
||||
const prisma = { tbankConnection: { findUnique: jest.fn() } };
|
||||
const config = { getOrThrow: jest.fn(() => key) };
|
||||
const access = { withAccount: jest.fn() };
|
||||
const tbank = {
|
||||
getOpenAccounts: jest.fn(),
|
||||
getOperationsByCursor: jest.fn(),
|
||||
};
|
||||
const requestContext = { set: jest.fn() };
|
||||
const service = new BrokerageOperationsService(
|
||||
prisma as never,
|
||||
config as never,
|
||||
access as never,
|
||||
tbank as never,
|
||||
requestContext as never,
|
||||
);
|
||||
const crypto = TbankCrypto.fromBase64(key);
|
||||
let operationsRequest: unknown;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
tbank.getOpenAccounts.mockReset();
|
||||
access.withAccount.mockReset();
|
||||
tbank.getOperationsByCursor.mockReset();
|
||||
prisma.tbankConnection.findUnique.mockResolvedValue({
|
||||
encryptedToken: crypto.encrypt('token'),
|
||||
});
|
||||
tbank.getOpenAccounts.mockResolvedValue([
|
||||
{ id: 'account', type: 'ACCOUNT_TYPE_TINKOFF' },
|
||||
]);
|
||||
access.withAccount.mockImplementation(
|
||||
(
|
||||
_userId: string,
|
||||
_accountId: string,
|
||||
handler: (context: TbankAccountContext) => Promise<unknown>,
|
||||
) =>
|
||||
handler({
|
||||
token: 'token',
|
||||
account: { id: 'account', type: 'ACCOUNT_TYPE_TINKOFF', name: '' },
|
||||
}),
|
||||
);
|
||||
tbank.getOperationsByCursor.mockImplementation(
|
||||
(_token: string, request: unknown) => {
|
||||
operationsRequest = request;
|
||||
@@ -116,7 +116,7 @@ describe('BrokerageOperationsService', () => {
|
||||
});
|
||||
|
||||
it('rejects inaccessible accounts before reading operations', async () => {
|
||||
tbank.getOpenAccounts.mockResolvedValue([]);
|
||||
access.withAccount.mockRejectedValueOnce(new NotFoundException());
|
||||
|
||||
await expect(service.get('user', 'account', {})).rejects.toEqual(
|
||||
expect.any(NotFoundException),
|
||||
@@ -125,15 +125,12 @@ describe('BrokerageOperationsService', () => {
|
||||
});
|
||||
|
||||
it('retains existing T-Bank error semantics', async () => {
|
||||
tbank.getOpenAccounts.mockRejectedValueOnce(
|
||||
new Error('INVALID_TBANK_TOKEN'),
|
||||
access.withAccount.mockRejectedValueOnce(
|
||||
new BadRequestException({ code: 'INVALID_TBANK_TOKEN' }),
|
||||
);
|
||||
await expect(service.get('user', 'account', {})).rejects.toEqual(
|
||||
expect.any(BadRequestException),
|
||||
);
|
||||
tbank.getOpenAccounts.mockResolvedValueOnce([
|
||||
{ id: 'account', type: 'ACCOUNT_TYPE_TINKOFF' },
|
||||
]);
|
||||
tbank.getOperationsByCursor.mockRejectedValueOnce(
|
||||
new ServiceUnavailableException({ code: 'TBANK_UNAVAILABLE' }),
|
||||
);
|
||||
@@ -158,7 +155,7 @@ describe('BrokerageOperationsService', () => {
|
||||
it('does not let a cached page bypass account ownership checks', async () => {
|
||||
const query = { from: '2025-01-01', to: '2025-01-31' };
|
||||
await service.get('user', 'account', query);
|
||||
tbank.getOpenAccounts.mockResolvedValueOnce([]);
|
||||
access.withAccount.mockRejectedValueOnce(new NotFoundException());
|
||||
|
||||
await expect(service.get('user', 'account', query)).rejects.toEqual(
|
||||
expect.any(NotFoundException),
|
||||
|
||||
Reference in New Issue
Block a user