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

194 lines
6.6 KiB
PHP

<?php
declare(strict_types=1);
use App\Services\Sales\AdAudienceScheduler;
use Carbon\CarbonImmutable;
/** Сроки по умолчанию из спеки v2. */
function durations(array $override = []): array
{
return array_merge([
'warmup_days' => 3,
'new_days' => 3,
'in_work_days' => 14,
'negotiation_overdue_days' => 3,
'negotiation_far_threshold_days' => 14,
'negotiation_far_head_days' => 3,
'negotiation_far_lead_days' => 3,
'no_answer_days' => 7,
'rejected_days' => 3,
'registered_days' => 30,
'testing_days' => 30,
], $override);
}
function decide(array $firm, array $override = [], string $now = '2026-08-01 12:00:00'): array
{
return (new AdAudienceScheduler(durations($override)))
->decide($firm, CarbonImmutable::parse($now));
}
it('греет фирму без менеджера три дня', function () {
$result = decide([
'stage' => null,
'warmup_started_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
'next_call_at' => null,
]);
expect($result['state'])->toBe('active');
});
it('останавливает прогрев и зовёт назначить менеджера, когда три дня вышли', function () {
$result = decide([
'stage' => null,
'warmup_started_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
'next_call_at' => null,
]);
expect($result['state'])->toBe('stopped')
->and($result['reason'])->toBe('ready_for_manager');
});
it('крутит рекламу на новой карточке три дня от назначения', function () {
$result = decide([
'stage' => 'new',
'assigned_at' => CarbonImmutable::parse('2026-07-30 12:00:00'),
'next_call_at' => null,
]);
expect($result['state'])->toBe('active');
});
it('гасит новую карточку после трёх дней', function () {
$result = decide([
'stage' => 'new',
'assigned_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
'next_call_at' => null,
]);
expect($result['state'])->toBe('stopped')
->and($result['reason'])->toBe('stage_limit');
});
it('взятому в работу даёт четырнадцать дней, потому что даты созвона там нет', function () {
expect(decide([
'stage' => 'in_work',
'stage_changed_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
'next_call_at' => null,
])['state'])->toBe('active');
expect(decide([
'stage' => 'in_work',
'stage_changed_at' => CarbonImmutable::parse('2026-07-01 12:00:00'),
'next_call_at' => null,
])['state'])->toBe('stopped');
});
it('в переговорах крутит рекламу до дня созвона', function () {
expect(decide([
'stage' => 'negotiation',
'next_call_at' => CarbonImmutable::parse('2026-08-05 12:00:00'),
])['state'])->toBe('active');
});
it('после просроченного созвона даёт три дня и гасит', function () {
expect(decide([
'stage' => 'negotiation',
'next_call_at' => CarbonImmutable::parse('2026-07-30 12:00:00'),
])['state'])->toBe('active');
expect(decide([
'stage' => 'negotiation',
'next_call_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
])['state'])->toBe('stopped')
->and(decide([
'stage' => 'negotiation',
'next_call_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
])['reason'])->toBe('call_overdue');
});
it('при далёком созвоне крутит три дня, потом ставит на паузу', function () {
$far = CarbonImmutable::parse('2026-09-30 12:00:00');
expect(decide([
'stage' => 'negotiation',
'stage_changed_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
'next_call_at' => $far,
])['state'])->toBe('active');
$paused = decide([
'stage' => 'negotiation',
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
'next_call_at' => $far,
]);
expect($paused['state'])->toBe('paused')
->and($paused['resume_at']->toDateString())->toBe('2026-09-27');
});
it('включает рекламу снова за три дня до далёкого созвона', function () {
expect(decide([
'stage' => 'negotiation',
'stage_changed_at' => CarbonImmutable::parse('2026-06-01 12:00:00'),
'next_call_at' => CarbonImmutable::parse('2026-08-03 12:00:00'),
], ['negotiation_far_threshold_days' => 1])['state'])->toBe('active');
});
it('недозвону даёт семь дней', function () {
expect(decide([
'stage' => 'no_answer',
'stage_changed_at' => CarbonImmutable::parse('2026-07-28 12:00:00'),
])['state'])->toBe('active');
expect(decide([
'stage' => 'no_answer',
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
])['state'])->toBe('stopped');
});
it('отказу даёт три дня', function () {
expect(decide([
'stage' => 'rejected',
'stage_changed_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
])['state'])->toBe('active');
expect(decide([
'stage' => 'rejected',
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
])['state'])->toBe('stopped');
});
it('регистрации и тестированию даёт тридцать дней', function () {
foreach (['registered', 'testing'] as $stage) {
expect(decide([
'stage' => $stage,
'stage_changed_at' => CarbonImmutable::parse('2026-07-20 12:00:00'),
])['state'])->toBe('active');
expect(decide([
'stage' => $stage,
'stage_changed_at' => CarbonImmutable::parse('2026-05-01 12:00:00'),
])['state'])->toBe('stopped');
}
});
it('за пополнивших баланс и пользователей мы не платим', function () {
foreach (['topped_up', 'user'] as $stage) {
$result = decide([
'stage' => $stage,
'stage_changed_at' => CarbonImmutable::parse('2026-07-31 12:00:00'),
]);
expect($result['state'])->toBe('stopped')
->and($result['reason'])->toBe('client_won');
}
});
it('слушается изменённых настроек', function () {
expect(decide([
'stage' => null,
'warmup_started_at' => CarbonImmutable::parse('2026-07-25 12:00:00'),
], ['warmup_days' => 30])['state'])->toBe('active');
});