сторож на развилку: отчёт об остановке идёт в свой применитель, а не в запуск

Связка «задание вида stop → TelegramOstanovkaApplier» не охранялась ничем: удали или
перепутай ветку match — и отчёт уехал бы в применитель ЗАПУСКА кампании, который двигает
статус и деньги. Опасность в этом файле известна и записана над самим match, но для
нового вида работы сторожа ещё не было.

Сторож ходит через настоящую ручку робота, а не зовёт применитель напрямую: отказ по
заданию stop обязан снять отметку «останавливается» — этого не делает никто, кроме
нужного применителя.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-08-08 23:28:40 +03:00
parent ec35cd1931
commit b1bc6fd6e7
@@ -94,3 +94,42 @@ it('отказ пересдачи при наличии номера кабин
expect(Campaign::find($campaign->id)->status)->toBe(Campaign::STATUS_NEEDS_REVIEW);
});
// 🔴 Развилка не охранялась ничем: удали/перепутай ветку MODE_STOP в match — и отчёт об
// остановке уехал бы в default (применитель ЗАПУСКА), который двигает статус и деньги.
// Отказ по заданию stop обязан снять отметку «останавливается» — этого не делает никто,
// кроме TelegramOstanovkaApplier, так что отметка — самый наглядный различитель.
it('отчёт об отказе остановки снимает отметку, а не двигает кампанию как после запуска', function () {
$campaign = tgModesCampaign($this->tenant->id, Campaign::STATUS_LAUNCHED);
$campaign->update(['stop_requested_at' => now()]);
$job = $this->queue->enqueue($this->tenant->id, $campaign->id, RobotJob::MODE_STOP, ['campaignId' => '777']);
$this->queue->takeNext();
$this->withHeader('X-Tg-Robot-Token', 'tg-secret-1')
->postJson("/api/tg-robot/jobs/{$job->id}/done", [
'ok' => false, 'step' => 'stop', 'reason' => 'Вход в кабинет слетел',
])
->assertOk();
$fresh = Campaign::find($campaign->id);
expect($fresh->stop_requested_at)->toBeNull();
expect($fresh->status)->toBe(Campaign::STATUS_LAUNCHED);
});
it('успешное нажатие «Завершить» оставляет отметку остановки и записывает цифры', function () {
$campaign = tgModesCampaign($this->tenant->id, Campaign::STATUS_LAUNCHED);
$campaign->update(['stop_requested_at' => now()]);
$job = $this->queue->enqueue($this->tenant->id, $campaign->id, RobotJob::MODE_STOP, ['campaignId' => '777']);
$this->queue->takeNext();
$this->withHeader('X-Tg-Robot-Token', 'tg-secret-1')
->postJson("/api/tg-robot/jobs/{$job->id}/done", [
'ok' => true, 'stopped' => true, 'status' => 'stopping', 'impressions' => 22, 'spendRub' => '10.56',
])
->assertOk();
$fresh = Campaign::find($campaign->id);
expect($fresh->stop_requested_at)->not->toBeNull();
expect($fresh->status)->toBe(Campaign::STATUS_LAUNCHED);
expect((string) $fresh->stats_spend_rub)->toBe('10.56');
});