import { BadRequestException } from '@nestjs/common'; import { dateOnlyBounds } from '../investment-calendar'; import { planIncomePeriod, rangeBuckets } from './income-period'; describe('income period planning', () => { afterEach(() => jest.useRealTimers()); it('uses the Moscow current month and validates strict inclusive ranges', () => { jest.useFakeTimers(); jest.setSystemTime(new Date('2026-01-31T22:30:00.000Z')); expect(planIncomePeriod(undefined, undefined)).toMatchObject({ selected: { from: '2026-02-01', to: '2026-02-01' }, }); for (const [from, to] of [ ['2026-02-30', '2026-03-01'], ['2026-03-02', '2026-03-01'], ['2026-01-01', '2027-01-02'], ['2026-01-01', '2026-04-01'], ]) { expect(() => planIncomePeriod(from, to)).toThrow(BadRequestException); } }); it('keeps empty buckets and skips removed comparison ranges', () => { expect( rangeBuckets({ from: '2026-01-01', to: '2026-02-15' }, 'week'), ).toHaveLength(7); expect(planIncomePeriod('2026-03-01', '2026-03-31')).toEqual({ selected: { from: '2026-03-01', to: '2026-03-31' }, granularity: 'day', }); }); it('accepts multi-year periods and creates monthly buckets for the entire range', () => { jest.useFakeTimers(); jest.setSystemTime(new Date('2026-09-09T12:00:00Z')); const plan = planIncomePeriod('2020-01-01', '2025-12-31'); expect(plan.selected).toEqual({ from: '2020-01-01', to: '2025-12-31' }); expect(plan.granularity).toBe('month'); expect(rangeBuckets(plan.selected, plan.granularity)).toHaveLength(72); }); it('does not send the unfinished Moscow day as a future instant', () => { jest.useFakeTimers(); jest.setSystemTime(new Date('2026-08-27T21:44:18.510Z')); expect(dateOnlyBounds({ from: '2026-08-01', to: '2026-08-28' })).toEqual({ from: new Date('2026-07-31T21:00:00.000Z'), to: new Date('2026-08-27T21:44:18.510Z'), }); }); it.each([ ['2024-02-29', '2024-02-28T21:00:00.000Z', '2024-02-29T21:00:00.000Z'], ['2025-12-31', '2025-12-30T21:00:00.000Z', '2025-12-31T21:00:00.000Z'], ])('maps inclusive %s to one full half-open Moscow day', (date, from, to) => { expect( dateOnlyBounds( { from: date, to: date }, new Date('2026-01-01T12:00:00Z'), ), ).toEqual({ from: new Date(from), to: new Date(to), }); }); });