Files
portal/app/tests/Unit/Sales/SalesPeriodResolverTest.php
T
Дмитрий 07b5758291 feat(sales): резолвер периода (этот/прошлый/позапрошлый/произвольный)
Task 1.1: SalesPeriodResolver.resolve({kind,from,to})→SalesPeriodRange (МСК, Europe/Moscow) + monthsIn() для ступенчатого тарифа. Поддержка this/prev/prev2/custom; неизвестный kind→this; custom from>to→исключение. Тест 10/10, stan 0. Один эскейп на сессию.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 13:35:48 +03:00

126 lines
5.1 KiB
PHP

<?php
declare(strict_types=1);
use App\Services\Sales\SalesPeriodRange;
use App\Services\Sales\SalesPeriodResolver;
use Carbon\CarbonImmutable;
// Заморозка времени: 15 июня 2026, 12:00 МСК
beforeEach(function (): void {
CarbonImmutable::setTestNow(
CarbonImmutable::parse('2026-06-15 12:00:00', 'Europe/Moscow'),
);
});
afterEach(function (): void {
CarbonImmutable::setTestNow();
});
// ─── kind=this ────────────────────────────────────────────────────────────────
it('kind=this returns current month range (June 2026)', function (): void {
$resolver = new SalesPeriodResolver;
$range = $resolver->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');
});