Files
backend/src/modules/investments/brokerage-accounts/brokerage-accounts.service.spec.ts
T

105 lines
3.2 KiB
TypeScript

jest.mock('../../../prisma/prisma.service', () => ({
PrismaService: class {},
}));
import { investmentResourceMeta } from '../investment-resource.mapper';
import { BrokerageAccountsService } from './brokerage-accounts.service';
jest.mock('../portfolio/application/portfolio.service', () => ({
PortfolioService: class PortfolioService {},
}));
const meta = investmentResourceMeta({
resourceKey: 'membership',
dataRevision: '1',
availability: 'complete',
fetchedAt: new Date(),
freshForMs: 300_000,
work: null,
});
describe('saved brokerage account projection', () => {
const portfolio = { saved: jest.fn() };
const sync = { hourly: jest.fn().mockResolvedValue(null) };
const service = new BrokerageAccountsService(portfolio as never, sync as never);
it('distinguishes unknown membership from a confirmed empty list', async () => {
portfolio.saved.mockResolvedValueOnce({
membership: { ...meta, completeness: 'incomplete' },
accounts: [],
});
expect((await service.list('owner')).data).toBeNull();
portfolio.saved.mockResolvedValueOnce({ membership: meta, accounts: [] });
expect((await service.list('owner')).data).toEqual([]);
});
it('keeps exact saved totals and independent missing summaries', async () => {
portfolio.saved.mockResolvedValue({
membership: meta,
accounts: [
{
id: 'a',
name: 'A',
type: 'BROKERAGE',
openedOn: null,
meta,
snapshot: {
totalsByCurrency: [
{ currency: 'RUB', value: '100000000.000000001' },
],
positions: [{ type: 'STOCK' }, { type: 'BOND' }],
},
},
{
id: 'b',
name: 'B',
type: 'BROKERAGE',
openedOn: null,
meta: { ...meta, availability: 'missing' },
snapshot: null,
},
],
});
const response = await service.list('owner');
expect(response.data?.[0].portfolioSummary).toEqual({
totalsByCurrency: [{ currency: 'RUB', value: '100000000.000000001' }],
stockPositionCount: 1,
bondPositionCount: 1,
});
expect(response.data?.[1].portfolioSummary).toBeNull();
expect(response.meta.availability).toBe('available');
expect(response.meta.completeness).toBe('complete');
});
it('observes the latest hourly walk without starting another one', async () => {
portfolio.saved.mockResolvedValue({ membership: meta, accounts: [] });
sync.hourly.mockResolvedValue({
startedAt: new Date('2026-09-25T12:00:00Z'),
finishedAt: new Date('2026-09-25T12:01:00Z'),
state: 'partial',
sections: [
{
section: 'portfolio',
accountId: 'a',
errorCode: 'TBANK_UNAVAILABLE',
},
],
});
const response = await service.list('owner');
expect(response.sync).toEqual({
startedAt: '2026-09-25T12:00:00.000Z',
finishedAt: '2026-09-25T12:01:00.000Z',
state: 'partial',
problems: [
{
section: 'portfolio',
accountId: 'a',
errorCode: 'TBANK_UNAVAILABLE',
},
],
});
expect(sync.hourly).toHaveBeenCalledWith('owner');
});
});