resolve(['kind' => 'this']); expect($range)->toBeInstanceOf(SalesPeriodRange::class); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-01 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-30 23:59:59'); }); // ─── kind=prev ──────────────────────────────────────────────────────────────── it('kind=prev returns previous month range (May 2026)', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve(['kind' => 'prev']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-05-01 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-05-31 23:59:59'); }); // ─── kind=prev2 ─────────────────────────────────────────────────────────────── it('kind=prev2 returns month-before-previous range (April 2026)', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve(['kind' => 'prev2']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-04-01 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-04-30 23:59:59'); }); // ─── kind=custom ────────────────────────────────────────────────────────────── it('kind=custom returns exact from/to bounds', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve([ 'kind' => 'custom', 'from' => '2026-03-10', 'to' => '2026-05-20', ]); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-03-10 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-05-20 23:59:59'); }); it('kind=custom: monthsIn returns all three month-starts for Mar-May span', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve([ 'kind' => 'custom', 'from' => '2026-03-10', 'to' => '2026-05-20', ]); $months = $resolver->monthsIn($range); expect($months)->toHaveCount(3); expect($months[0]->format('Y-m-d'))->toBe('2026-03-01'); expect($months[1]->format('Y-m-d'))->toBe('2026-04-01'); expect($months[2]->format('Y-m-d'))->toBe('2026-05-01'); }); it('kind=custom: throws InvalidArgumentException when from > to', function (): void { $resolver = new SalesPeriodResolver; expect(fn () => $resolver->resolve([ 'kind' => 'custom', 'from' => '2026-05-20', 'to' => '2026-03-10', ]))->toThrow(InvalidArgumentException::class); }); it('kind=custom: throws InvalidArgumentException when from is missing', function (): void { $resolver = new SalesPeriodResolver; expect(fn () => $resolver->resolve([ 'kind' => 'custom', 'to' => '2026-05-20', ]))->toThrow(InvalidArgumentException::class); }); it('kind=custom: throws InvalidArgumentException when to is missing', function (): void { $resolver = new SalesPeriodResolver; expect(fn () => $resolver->resolve([ 'kind' => 'custom', 'from' => '2026-03-10', ]))->toThrow(InvalidArgumentException::class); }); // ─── monthsIn ───────────────────────────────────────────────────────────────── it('monthsIn for single-month "this" range returns one entry (June 2026)', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve(['kind' => 'this']); $months = $resolver->monthsIn($range); expect($months)->toHaveCount(1); expect($months[0]->format('Y-m-d'))->toBe('2026-06-01'); }); // ─── timezone ───────────────────────────────────────────────────────────────── it('range start carries Europe/Moscow timezone', function (): void { $resolver = new SalesPeriodResolver; $range = $resolver->resolve(['kind' => 'this']); expect($range->start->timezoneName)->toBe('Europe/Moscow'); }); // ─── новые периоды: сегодня / вчера / 7 дней / 30 дней ──────────────────────── it('kind=today — только сегодняшний день', function (): void { $range = (new SalesPeriodResolver)->resolve(['kind' => 'today']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-15 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59'); }); it('kind=yesterday — только вчерашний день', function (): void { $range = (new SalesPeriodResolver)->resolve(['kind' => 'yesterday']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-14 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-14 23:59:59'); }); it('kind=tomorrow — только завтрашний день', function (): void { $range = (new SalesPeriodResolver)->resolve(['kind' => 'tomorrow']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-16 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-16 23:59:59'); }); it('kind=d7 — последние 7 дней, включая сегодня', function (): void { $range = (new SalesPeriodResolver)->resolve(['kind' => 'd7']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-06-09 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59'); }); it('kind=d30 — последние 30 дней, включая сегодня', function (): void { $range = (new SalesPeriodResolver)->resolve(['kind' => 'd30']); expect($range->start->format('Y-m-d H:i:s'))->toBe('2026-05-17 00:00:00'); expect($range->end->format('Y-m-d H:i:s'))->toBe('2026-06-15 23:59:59'); });