fix(bot): сторож ловит жёсткий отказ в замене в любой форме (живой баг 13.07)

This commit is contained in:
Дмитрий
2026-07-13 13:37:37 +03:00
parent 1804f38724
commit aa9537d43e
2 changed files with 212 additions and 0 deletions
+178
View File
@@ -184,3 +184,181 @@ it('без карточки: число ИЗ статьи (обычная, не
expect($out)->toContain('300');
});
/**
* 🔴 ЖИВОЙ БАГ 13.07.2026: правило владельца ЗАМЕНЫ ЗАЯВКИ НЕТ (недозвон не брак), но
* бот не должен РУБИТЬ ДВЕРЬЮ («не полагается», «не вернём»). Старое правило BANNED
* (строка ~74) знало только форму ЕДИНСТВЕННОГО числа «не полагается» «не полагаются»
* (множественное) прошло мимо на живом прогоне. Урок проекта: правила по СПИСКУ
* ФОРМУЛИРОВОК проигрывают гонку нужно правило ПО СМЫСЛУ (как promisesReplacement и
* соседи), а не очередная строка в BANNED.
*/
it('🔴 ЖИВОЙ БАГ: «Замены за недозвоны не полагаются» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Замены за недозвоны не полагаются — это нормальная часть работы.',
'Недозвон — обычная часть работы с контактами.'
);
expect($out)->not->toContain('не полагаются');
});
it('старая форма «Замена не полагается» — тоже рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean('Замена не полагается.', 'Недозвон — обычная часть работы.');
expect($out)->toBe('');
});
it('«Возврат денег за недозвон не предусмотрен» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Возврат денег за недозвон не предусмотрен.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Компенсации за такие заявки мы не делаем» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Компенсации за такие заявки мы не делаем.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Деньги за недозвоны не возвращаются» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Деньги за недозвоны не возвращаются.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Заменить заявку нельзя, возврата нет» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Заменить заявку нельзя, возврата нет.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Мы не меняем и не возвращаем деньги за такие номера» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Мы не меняем и не возвращаем деньги за такие номера.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Такие заявки не подлежат замене» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Такие заявки не подлежат замене.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
it('«Возмещение по недозвонам не положено» — рубка дверью, вырезается', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Возмещение по недозвонам не положено.',
'Недозвон — обычная часть работы.'
);
expect($out)->toBe('');
});
/**
* Честные фразы сгладить тему, но НЕ рубить дверью. Их резать НЕЛЬЗЯ: без них ответ
* потеряет весь смысл и клиент уйдёт с пустым ответом (эскалация «оставьте телефон»).
*/
it('честное: путь на support@ для спорного случая — НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Спорный случай опишите на support@liderra.ru — специалист разберёт его индивидуально.',
'Поддержка работает круглосуточно.'
);
expect($out)->toContain('support@liderra.ru')
->and($out)->toContain('специалист разберёт его индивидуально');
});
it('честное: «недозвон — обычная часть работы, попробуйте СМС и повтор» — НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Недозвон — обычная часть работы: попробуйте СМС и повторный звонок позже.',
'Недозвон — обычная часть работы.'
);
expect($out)->toContain('попробуйте СМС и повторный звонок позже');
});
it('честное: «из ста номеров обычно отвечают 8–12 человек» — НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Из ста номеров обычно отвечают 8–12 человек.',
'Из ста номеров обычно отвечают 8–12 человек.'
);
expect($out)->toContain('Из ста номеров обычно отвечают');
});
it('честное: «если заявка явно не по теме, напишите нам — разберёмся» — НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean(
'Если заявка явно не по теме, напишите нам — разберёмся.',
'Заявка не по теме — напишите нам.'
);
expect($out)->toContain('напишите нам — разберёмся');
});
/**
* Ложные срабатывания: слово «нет» самое частое слово в русском, встречается в
* контекстах, никак не связанных с заменой/возвратом/деньгами. Правило НЕ должно их резать.
*/
it('ложное срабатывание: «заявок сегодня нет» — про заявки, не про замену/деньги, НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
$out = $guard->clean('Заявок сегодня нет — это нормально, бывают дни потише.', 'Заявок сегодня нет.');
expect($out)->toContain('Заявок сегодня нет');
});
it('ложное срабатывание: «денег на балансе нет» (не отказ в замене/возврате) — НЕ режется', function () {
$guard = new App\Services\Bot\AnswerGuard;
// Слово «деньг\w*» — триггер моего правила, «нет» — тоже, но смысл предложения не про
// отказ в замене/возврате, а про то, что баланс пуст. Правило не должно это резать.
$out = $guard->clean(
'Денег на балансе нет — пополните счёт, чтобы получать новые заявки.',
'Пополнить баланс можно картой или по счёту.'
);
expect($out)->toContain('Денег на балансе нет');
});