import { accountHistory, ledgerRange, planInvestmentScreen, } from './investment-sync-plan'; describe('screen dependency and history planning', () => { const now = new Date('2026-09-25T12:00:00Z'); const context = { membershipKnown: true, accounts: [{ accountId: 'a', openedOn: '2020-01-01' }], instruments: [ { uid: 'uid', type: 'STOCK' as const, historyFrom: new Date('2020-01-01Z'), }, ], }; it('prioritizes discovery, selected/comparisons, then history; Moscow dates are converted once', () => { const plan = planInvestmentScreen( { screen: 'income', accountId: 'a', from: '2024-02-01', to: '2024-02-29', }, context, now, ); expect(plan.sources[0].kind).toBe('membership'); const selected = plan.sources.filter( (source) => source.kind === 'operations' && source.priority === 'screen', ); expect( selected.some( (source) => source.from?.toISOString() === '2024-01-31T21:00:00.000Z', ), ).toBe(true); expect( selected.some( (source) => source.to?.toISOString() === '2024-02-29T21:00:00.000Z', ), ).toBe(true); expect(plan.unavailable).toEqual([ { resourceKey: 'income/yearAgo', reason: 'calendar_or_account_boundary' }, // gitleaks:allow public resource identifier ]); const firstHistory = plan.sources.findIndex( (source) => source.priority === 'history', ); expect( plan.sources .slice(firstHistory) .every((source) => source.priority === 'history'), ).toBe(true); }); it('force contains only shown dependencies and retains instrument reconciliation predicates', () => { const plan = planInvestmentScreen( { screen: 'stock', instrumentUid: 'uid', from: '2026-01-01', to: '2026-02-01', candlePeriod: 'year', }, context, now, true, ); expect(plan.sources.map((source) => source.kind)).toEqual( expect.arrayContaining([ 'membership', 'portfolio', 'operations', 'metadata', 'quote', 'company', 'calendar', 'candles', ]), ); expect(plan.sources.every((source) => source.priority !== 'history')).toBe( true, ); expect( plan.sources .filter((source) => source.kind === 'operations') .every((source) => source.instrumentUid === 'uid'), ).toBe(true); const accounts = planInvestmentScreen( { screen: 'accounts' }, context, now, true, ); expect( accounts.sources.some((source) => source.kind === 'operations'), ).toBe(false); }); it('never invents an account or candle history boundary; discovery gates unknown membership', () => { const unknown = { ...context, accounts: [{ accountId: 'a', openedOn: null }], instruments: [], }; expect(accountHistory(unknown.accounts, now)[0].from.toISOString()).toBe( '1970-01-01T00:00:00.000Z', ); expect( ledgerRange( undefined, undefined, 'uid', unknown.accounts, now, ).from.toISOString(), ).toBe('1970-01-01T00:00:00.000Z'); const plan = planInvestmentScreen( { screen: 'bond', instrumentUid: 'uid', candlePeriod: 'all' }, unknown, now, ); expect(plan.sources.some((source) => source.kind === 'candles')).toBe( false, ); expect(plan.unavailable[0].reason).toBe('history_start_unavailable'); expect( planInvestmentScreen( { screen: 'account' }, { ...context, membershipKnown: false }, now, ).sources, ).toEqual([{ kind: 'membership', priority: 'discovery' }]); }); it('keeps UTC event days separate from Moscow instrument calendar days at year rollover', () => { const rollover = new Date('2026-12-31T22:00:00Z'); const events = planInvestmentScreen( { screen: 'events' }, context, rollover, true, ); expect( events.sources.find((source) => source.kind === 'calendar')?.from, ).toEqual(new Date('2026-12-31Z')); const stock = planInvestmentScreen( { screen: 'stock', instrumentUid: 'uid' }, context, rollover, true, ); expect( stock.sources.find((source) => source.kind === 'calendar'), ).toMatchObject({ from: new Date('2022-01-01Z'), to: new Date('2028-01-01Z'), }); expect(rollover.toISOString()).toBe('2026-12-31T22:00:00.000Z'); }); it('keeps search queries separate and events scoped to their inclusive period', () => { const search = planInvestmentScreen( { screen: 'stocks', query: ' sber ' }, context, now, true, ); expect( search.sources.find((source) => source.kind === 'catalog')?.query, ).toBe('SBER'); const events = planInvestmentScreen( { screen: 'events', from: '2026-10-01', to: '2026-10-31' }, context, now, true, ); expect( events.sources.find((source) => source.kind === 'calendar'), ).toMatchObject({ from: new Date('2026-10-01Z'), to: new Date('2026-10-31Z'), }); expect(events.sources.some((source) => source.kind === 'operations')).toBe( false, ); }); });