Files
portal/app/tests/Unit/PlatformWarmingDeciderTest.php
T

118 lines
4.6 KiB
PHP
Raw Normal View History

<?php
declare(strict_types=1);
use App\Models\SalesAdAudienceFirm;
use App\Models\SalesAdAudienceFirmChannel;
use App\Models\SalesAdAudiencePlatform;
use App\Services\Sales\PlatformWarmingDecider;
use Carbon\CarbonImmutable;
use Illuminate\Support\Carbon;
use Tests\TestCase;
// Модели вне контейнера Laravel не могут выставить datetime-cast атрибут
// (setAttribute → fromDateTime → getConnection() падает без резолвера
// соединений — резолвер регистрируется только при загрузке приложения).
// Файл сам просит контейнер, не трогая общий tests/Pest.php.
uses(TestCase::class);
it('одна фирма: на площадке с большим сроком active, с меньшим — stopped(ready_for_manager)', function () {
$now = CarbonImmutable::parse('2026-07-22 12:00:00');
$firm = new SalesAdAudienceFirm(['firm_name' => 'X']);
$firm->warmup_started_at = Carbon::parse('2026-07-18 12:00:00'); // 4 дня назад
$decider = new PlatformWarmingDecider;
$long = new SalesAdAudiencePlatform(['platform' => 'yandex']);
$long->warmup_days = 5;
$short = new SalesAdAudiencePlatform(['platform' => 'vk']);
$short->warmup_days = 3;
expect($decider->decide($firm, $long, null, $now)['state'])->toBe('active');
$shortDecision = $decider->decide($firm, $short, null, $now);
expect($shortDecision['state'])->toBe('stopped');
expect($shortDecision['reason'])->toBe('ready_for_manager');
});
it('flat active: строка канала mode=flat, flat_days больше прошедшего срока — active', function () {
$now = CarbonImmutable::parse('2026-07-22 12:00:00');
$firm = new SalesAdAudienceFirm(['firm_name' => 'X']);
$platform = new SalesAdAudiencePlatform(['platform' => 'yandex']);
$platform->warmup_days = 3;
$row = new SalesAdAudienceFirmChannel([
'channel' => 'yandex',
'status' => 'warming',
'mode' => 'flat',
'flat_days' => 10,
]);
$row->warming_started_at = Carbon::parse('2026-07-18 12:00:00'); // 4 дня назад
$decider = new PlatformWarmingDecider;
$decision = $decider->decideForChannelRow($firm, $platform, null, $row, $now);
expect($decision['state'])->toBe('active');
});
it('flat done: строка канала mode=flat, flat_days меньше прошедшего срока — stopped(flat_done)', function () {
$now = CarbonImmutable::parse('2026-07-22 12:00:00');
$firm = new SalesAdAudienceFirm(['firm_name' => 'X']);
$platform = new SalesAdAudiencePlatform(['platform' => 'yandex']);
$platform->warmup_days = 3;
$row = new SalesAdAudienceFirmChannel([
'channel' => 'yandex',
'status' => 'warming',
'mode' => 'flat',
'flat_days' => 3,
]);
$row->warming_started_at = Carbon::parse('2026-07-17 12:00:00'); // 5 дней назад
$decider = new PlatformWarmingDecider;
$decision = $decider->decideForChannelRow($firm, $platform, null, $row, $now);
expect($decision['state'])->toBe('stopped');
expect($decision['reason'])->toBe('flat_done');
});
it('funnel из строки: срок берётся из warming_started_at строки канала, не из firm.warmup_started_at', function () {
$now = CarbonImmutable::parse('2026-07-22 12:00:00');
$firm = new SalesAdAudienceFirm(['firm_name' => 'X']);
// Умышленно НЕ ставим firm->warmup_started_at — decideForChannelRow должен
// взять срок из строки канала, а не из фирмы.
$platform = new SalesAdAudiencePlatform(['platform' => 'yandex']);
$platform->warmup_days = 3;
$decider = new PlatformWarmingDecider;
$rowActive = new SalesAdAudienceFirmChannel([
'channel' => 'yandex',
'status' => 'warming',
'mode' => 'funnel',
]);
$rowActive->warming_started_at = Carbon::parse('2026-07-20 12:00:00'); // 2 дня назад
$activeDecision = $decider->decideForChannelRow($firm, $platform, null, $rowActive, $now);
expect($activeDecision['state'])->toBe('active');
$rowStopped = new SalesAdAudienceFirmChannel([
'channel' => 'yandex',
'status' => 'warming',
'mode' => 'funnel',
]);
$rowStopped->warming_started_at = Carbon::parse('2026-07-17 12:00:00'); // 5 дней назад
$stoppedDecision = $decider->decideForChannelRow($firm, $platform, null, $rowStopped, $now);
expect($stoppedDecision['state'])->toBe('stopped');
expect($stoppedDecision['reason'])->toBe('ready_for_manager');
});