import { ROLES_KEY } from '../auth/decorators/roles.decorator'; import { ApiResponse } from '../../common/dto/api-response.dto'; import { TBankController } from './tbank.controller'; import { BrokerAccountsService } from './services/broker-accounts.service'; import { BrokerEventsService } from './services/broker-events.service'; import { BrokerOperationSyncService } from './services/broker-operation-sync.service'; import { BrokerOperationsService } from './services/broker-operations.service'; import { BrokerPortfolioService } from './services/broker-portfolio.service'; describe('TBankController', () => { const accounts = { findAll: vi.fn() } as unknown as BrokerAccountsService; const portfolio = { getPortfolio: vi.fn() } as unknown as BrokerPortfolioService; const events = { getEvents: vi.fn() } as unknown as BrokerEventsService; const operations = { getOperations: vi.fn() } as unknown as BrokerOperationsService; const sync = { syncAccount: vi.fn() } as unknown as BrokerOperationSyncService; beforeEach(() => { vi.clearAllMocks(); }); it('allows authenticated users to access broker endpoints', () => { expect(Reflect.getMetadata(ROLES_KEY, TBankController)).toEqual(['user']); }); it('returns accounts in a single API envelope', async () => { vi.mocked(accounts.findAll).mockResolvedValueOnce({ data: [ { id: 'acc-1', type: 'brokerage', name: 'Broker', status: 'ACCOUNT_STATUS_OPEN', openedAt: null, accessLevel: null, }, ], meta: { fromCache: true, cachedAt: '2026-06-17T00:00:00.000Z' }, }); const controller = new TBankController(accounts, portfolio, events, operations, sync); const response = await controller.getAccounts(); expect(response).toBeInstanceOf(ApiResponse); expect(response.data).toHaveLength(1); expect(response.meta).toEqual({ fromCache: true, cachedAt: '2026-06-17T00:00:00.000Z' }); }); it('exposes a sync trigger for durable operation history', async () => { vi.mocked(sync.syncAccount).mockResolvedValueOnce({ upserted: 2 }); const controller = new TBankController(accounts, portfolio, events, operations, sync); const response = await controller.syncOperations('acc-1', { from: '2026-06-01T00:00:00.000Z', to: '2026-06-17T00:00:00.000Z', }); expect(sync.syncAccount).toHaveBeenCalledWith('acc-1', { from: '2026-06-01T00:00:00.000Z', to: '2026-06-17T00:00:00.000Z', }); expect(response.data).toEqual({ upserted: 2 }); }); it('forwards events query and wraps response', async () => { const eventsData = { items: [], summary: { eventCount: 0, nearestEventDate: null, totalEstimatedCashflow: 0, dividendsTotal: 0, couponsTotal: 0, principalRepaymentTotal: 0, }, asOf: '2026-06-22T00:00:00.000Z', }; vi.mocked(events.getEvents).mockResolvedValueOnce({ data: eventsData, meta: { fromCache: false, cachedAt: '2026-06-22T00:00:00.000Z' }, }); const controller = new TBankController(accounts, portfolio, events, operations, sync); const response = await controller.getEvents('acc-1', { from: '2026-06-22', to: '2026-07-29' }); expect(events.getEvents).toHaveBeenCalledWith('acc-1', '2026-06-22', '2026-07-29'); expect(response).toBeInstanceOf(ApiResponse); expect(response.data).toEqual(eventsData); }); });