13ce301ab3
Кампания, брошенная роботом на полпути, попадала в needs_review или draft_ready — статусы, из которых не вело ни одного перехода. Замороженные деньги клиента запирались навсегда, снять их мог только программист правкой боевой базы. В бою 06.08.2026 так заперло 268,80 ₽ по кампании №14. Теперь в админке есть карточка «Застрявшие кампании»: владелец видит номер кампании в кабинете МТС, запертую сумму и уже уплаченную МТС сумму — и решает сам. Кнопка «списать по факту» показывается ТОЛЬКО когда МТС уже уплачено; иначе списывать было бы нечего, кроме сметы — ровно та беда, ради которой заморозку и заводили. Заодно: клиенту больше не показывают внутренние адреса и команды запуска — причина отказа переводится на человеческий язык. Портал перестал принимать пустой отчёт робота как успешный. Проверено: модуль 354 теста, экраны 13 тестов, статанализ 0 ошибок. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
69 lines
3.8 KiB
PHP
69 lines
3.8 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
use App\Services\ClientTg\PrichinaDlyaKlienta;
|
||
|
||
/**
|
||
* Приёмка 06.08.2026, боевой прогон: в карточке кампании у ПЛАТЯЩЕГО клиента стояло
|
||
* дословно «Вход в кабинет МТС потерян — нужен вход руками (npm run login)», а после
|
||
* второго прогона — «После «Продолжить» не перешли на /\/budget$/ (шаг «Объявление»);
|
||
* текущий URL: https://marketolog.mts.ru/...».
|
||
*
|
||
* Клиент не программист: команда разработчика и внутренний адрес в его карточке —
|
||
* не объяснение, а мусор. Полный текст остаётся в журнале (Log::warning уже пишет
|
||
* 'reason'), клиенту показываем человеческую фразу.
|
||
*
|
||
* 🔴 Отказ МОДЕРАЦИИ МТС через этот перевод НЕ проходит: там текст их модератора,
|
||
* и клиенту он нужен дословно — иначе он не поймёт, что править.
|
||
*/
|
||
it('пустую причину не выдумывает', function () {
|
||
expect(PrichinaDlyaKlienta::perevesti(null))->toBeNull();
|
||
expect(PrichinaDlyaKlienta::perevesti(''))->toBeNull();
|
||
expect(PrichinaDlyaKlienta::perevesti(' '))->toBeNull();
|
||
});
|
||
|
||
it('прячет команду разработчика за человеческой фразой', function () {
|
||
$text = PrichinaDlyaKlienta::perevesti('Вход в кабинет МТС потерян — нужен вход руками (npm run login)');
|
||
|
||
expect($text)->not->toContain('npm')
|
||
->and($text)->not->toContain('run login')
|
||
->and($text)->toContain('кабинет');
|
||
});
|
||
|
||
it('прячет внутренний адрес и регулярку', function () {
|
||
$vnutri = 'После «Продолжить» не перешли на /\/budget$/ (шаг «Объявление»); текущий URL: https://marketolog.mts.ru/cabinet/campaign/telegram-a2p/2237812/message';
|
||
$text = PrichinaDlyaKlienta::perevesti($vnutri);
|
||
|
||
expect($text)->not->toContain('http')
|
||
->and($text)->not->toContain('budget')
|
||
->and($text)->not->toContain('URL')
|
||
->and($text)->not->toBe($vnutri);
|
||
});
|
||
|
||
it('временную беду называет временной, а не поломкой', function () {
|
||
$text = PrichinaDlyaKlienta::perevesti('МТС не пускает этот адрес — отказ 3 раз подряд; если повторится, роботу нужен другой адрес выхода');
|
||
|
||
expect($text)->toContain('временно');
|
||
});
|
||
|
||
it('про потолок траты говорит клиенту как есть — это ему полезно', function () {
|
||
$vnutri = 'Бюджет 1500₽ превышает потолок 1000₽ — боевой запуск запрещён';
|
||
|
||
expect(PrichinaDlyaKlienta::perevesti($vnutri))->toBe($vnutri);
|
||
});
|
||
|
||
it('в любом переводе нет латиницы — клиент читает по-русски', function () {
|
||
$prichiny = [
|
||
'Вход в кабинет МТС потерян — нужен вход руками (npm run login)',
|
||
'submitWithPayment: кнопка оплаты неактивна',
|
||
'Кабинет МТС не открылся — пустая страница после 3 попыток',
|
||
'Статус кампании 2237821 не определён в списке',
|
||
'что-то совсем новое и незнакомое',
|
||
];
|
||
|
||
foreach ($prichiny as $p) {
|
||
expect(PrichinaDlyaKlienta::perevesti($p))->not->toMatch('/[A-Za-z]/');
|
||
}
|
||
});
|