refactor: centralize provider projections

This commit is contained in:
2026-10-01 20:53:00 +03:00
parent 190eb90ffe
commit 7fe88a12c7
7 changed files with 179 additions and 154 deletions
@@ -129,6 +129,43 @@ describe('persistent portfolio projection', () => {
]);
expect(snapshot.cashBalances).toEqual([{ currency: 'RUB', value: '1' }]);
});
it('keeps net-zero cash groups, missing valuations and incompatible currencies distinct', async () => {
repository.reserve.mockResolvedValueOnce({
launch: true,
work: { id: 'work', attemptId: 'attempt' },
});
tbank.getPortfolio.mockResolvedValue({ positions: [
{ quantity: { units: '1' }, currentPrice: { units: '3' } },
{
quantity: { units: '1' },
currentPrice: { currency: 'USD', units: '3' },
averagePositionPrice: { currency: 'RUB', units: '2' },
},
] });
tbank.getPositions.mockResolvedValue({ money: [
{ currency: 'rub', units: '2' },
{ currency: 'RUB', units: '-2' },
{ units: '99' },
{ currency: 'EUR', units: '1' },
] });
await service.request('user', 'private', 'a');
for (let index = 0; index < 10 && !repository.publish.mock.calls.length; index++) {
await immediate();
}
const snapshot = (repository.publish.mock.calls as [unknown, string, PortfolioDto][])[0][2];
expect(snapshot.cashBalances).toEqual([
{ currency: 'RUB', value: '0' },
{ currency: 'EUR', value: '1' },
]);
expect(snapshot.totalsByCurrency).toEqual([
{ currency: 'EUR', value: '1' },
{ currency: 'USD', value: '3' },
]);
expect(snapshot.positions[0].currentValue).toBeNull();
expect(snapshot.positions[1].absoluteReturn).toBeNull();
expect(snapshot.positions[1].averagePurchasePrice).toEqual({ currency: 'RUB', value: '2' });
});
it('does not turn a missing account snapshot into aggregate zero; summaries stay independent', async () => {
const snapshot: PortfolioDto = {
lastSuccessfulAt: membershipAt.toISOString(),