2026-07-14 18:32:40 +03:00
< ? php
declare ( strict_types = 1 );
use App\Services\Bot\AnswerGuard ;
use Illuminate\Foundation\Testing\RefreshDatabase ;
uses ( RefreshDatabase :: class );
/**
* Находки прогона 250 вопросов × 2 режима (14.07.2026, гость + вошедший клиент).
* Судьи прочитали 500 ответов; цены оказались вылечены полностью, но вылез другой пласт:
* бот путает гостя с вошедшим клиентом, обещает объём и выдумывает пороги ступеней.
*
* Разбор с цитатами: docs/superpowers/findings/2026-07-14-bot-250-both-modes-run.md
*/
$card = <<< ' CARD '
ДАННЫЕ ЭТОГО КЛИЕНТА (посчитаны только что):
Баланс: 11 025 ₽ ≈ 200 заявок, при текущем заказе хватит примерно на 10 дней.
Ступень: 1-я, 55 ₽ за заявку, получено в этом месяце 180 заявок, до следующей ступени — 320 заявок.
Проекты (3): «Стоматология — Москва» — работает, лимит 20 в день, сегодня 0; «Импланты — Санкт-Петербург» — на паузе с 14.07 (паузу поставил клиент); «Отбеливание — Казань» — остановлен из-за нехватки денег.
Заявки: сегодня 0, вчера 12, за 7 дней 25, за месяц 25.
Списано за месяц: 1 375 ₽. Последнее пополнение: 5 000 ₽ 11.07.2026.
CARD ;
it ( 'гостю не приписывают счёт и подарок: у него их нет' , function () {
$guard = app ( AnswerGuard :: class );
$text = 'У вас уже есть 1000 ₽ подарка — они лежат на счёте, пополнять ничего не нужно. При регистрации мы дарим 1000 ₽ на баланс.' ;
$clean = $guard -> clean ( $text , 'При регистрации мы дарим 1000 ₽ на баланс.' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'У вас уже есть' )
-> and ( $clean ) -> not -> toContain ( 'лежат на счёте' )
// Общее объяснение про подарок остаётся: человек не должен остаться без ответа.
-> and ( $clean ) -> toContain ( 'дарим 1000 ₽' );
});
it ( 'вошедшему не называют его баланс подарком портала' , function () use ( $card ) {
$guard = app ( AnswerGuard :: class );
$text = 'У вас на балансе 11 025 ₽ — это подарок портала, первые заявки пойдут за его счёт.' ;
$clean = $guard -> clean ( $text , $card , personalFactsKnown : true );
expect ( $clean ) -> not -> toContain ( 'подарок портала' );
});
it ( 'давнему клиенту не подсовывают сценарий новичка «живите на подарок»' , function () use ( $card ) {
$guard = app ( AnswerGuard :: class );
// Подарок в 1000 ₽ этот клиент потратил давно: баланс 11 025 ₽, за месяц списано 1 375 ₽.
$text = 'Баланс — у вас уже есть. Первые заявки идут за счёт подарка, пополнять ничего не нужно. Проект создаётся в разделе «Проекты».' ;
$clean = $guard -> clean ( $text , $card , personalFactsKnown : true );
expect ( $clean ) -> not -> toContain ( 'за счёт подарка' )
-> and ( $clean ) -> not -> toContain ( 'пополнять ничего не нужно' )
-> and ( $clean ) -> toContain ( 'разделе «Проекты»' );
});
it ( 'новичку, у которого подарок нетронут, про подарок сказать можно' , function () {
$guard = app ( AnswerGuard :: class );
$freshCard = " ДАННЫЕ ЭТОГО КЛИЕНТА (посчитаны только что): \n Баланс: 1000 ₽ ≈ 18 заявок. \n " ;
$text = 'Первые заявки идут за счёт подарка, пополнять ничего не нужно.' ;
expect ( $guard -> clean ( $text , $freshCard , personalFactsKnown : true )) -> toContain ( 'за счёт подарка' );
});
it ( 'вошедшему не врут про состояние его проектов' , function () use ( $card ) {
$guard = app ( AnswerGuard :: class );
// Правда: работает ОДИН проект, один на паузе, один остановлен из-за денег.
$text = 'У вас сейчас три проекта: два работают («Стоматология — Москва» и «Отбеливание — Казань»), один на паузе.' ;
$clean = $guard -> clean ( $text , $card , personalFactsKnown : true );
expect ( $clean ) -> not -> toContain ( 'два работают' );
});
it ( 'вошедшему не выдают остановленный проект за работающий' , function () use ( $card ) {
$guard = app ( AnswerGuard :: class );
$text = 'Проект «Отбеливание — Казань» работает, заявки по нему идут.' ;
$clean = $guard -> clean ( $text , $card , personalFactsKnown : true );
expect ( $clean ) -> not -> toContain ( '«Отбеливание — Казань» работает' );
});
it ( 'бот не обещает, что от повышения лимита заявок станет больше' , function () {
$guard = app ( AnswerGuard :: class );
$text = 'Лимит — это потолок, а не гарантия. Если заявок стабильно меньше, чем нужно, — увеличьте лимит, и их будет больше.' ;
$clean = $guard -> clean ( $text , 'Лимит — это потолок, а не гарантия.' , personalFactsKnown : false );
expect ( $clean ) -> toContain ( 'потолок' )
-> and ( $clean ) -> not -> toContain ( 'их будет больше' );
});
it ( 'бот не заявляет «такого не бывает» про списание без заявки' , function () {
$guard = app ( AnswerGuard :: class );
$text = 'Такого не бывает — каждое списание привязано к конкретной заявке. Проверить каждое списание можно на вкладке «Списания».' ;
$clean = $guard -> clean ( $text , 'Проверить каждое списание можно на вкладке «Списания».' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'Такого не бывает' )
-> and ( $clean ) -> toContain ( 'вкладке «Списания»' );
});
it ( 'номер и заявка — одно и то же: «поштучно не продаём» бот больше не говорит' , function () {
// Живой лог 14.07.2026, замечание владельца: «номера это и есть заявки, а он их делит».
// Мы продаём любой объём, хоть одну штуку — отказа в поштучной продаже нет и не было.
$guard = app ( AnswerGuard :: class );
$text = 'Мы не продаём номера поштучно — платите вы за заявки, которые получаете. Заявка стоит от 55 ₽ до 25 ₽.' ;
$clean = $guard -> clean ( $text , 'Заявка стоит от 55 ₽ до 25 ₽. Номер, заявка и лид — это одно и то же.' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'поштучно' )
-> and ( $clean ) -> toContain ( 'от 55 ₽' );
});
it ( 'число, названное самим клиентом, сторож не считает выдумкой' , function () {
$guard = app ( AnswerGuard :: class );
// Клиент спросил «можно купить всего 5 номеров?» — повторить его же «5 номеров»
// в ответе не выдумка. Сторожу отдаётся и текст вопроса (BotAnswerService).
$context = " Минимального заказа нет. \n \n можно купить всего 5 номеров? " ;
$text = 'Да, конечно — можно взять и 5 номеров, минимального заказа нет.' ;
expect ( $guard -> clean ( $text , $context , personalFactsKnown : false )) -> toContain ( '5 номеров' );
});
2026-07-14 18:51:13 +03:00
it ( 'правду «минимальный заказ — одна заявка» сторож не режет, а выдуманный минимум режет' , function () {
// Живая проверка на боевом 14.07.2026: сторож съел верный ответ про 5 номеров, остался
// обрубок «Платите только за то, что получите». Виновата была моя же грубая регулярка.
$guard = app ( AnswerGuard :: class );
$ctx = 'Минимального заказа нет: продаём любой объём, хоть одну штуку.' ;
$true = 'Да, конечно. Минимальный заказ — от 1 номера, берите сколько нужно.' ;
expect ( $guard -> clean ( $true , $ctx , personalFactsKnown : false )) -> toContain ( 'от 1 номера' );
$lie = 'Минимальный заказ — 100 заявок в месяц.' ;
expect ( $guard -> clean ( $lie , $ctx , personalFactsKnown : false )) -> not -> toContain ( '100 заявок' );
});
2026-07-14 18:32:40 +03:00
it ( 'сколько заявок выйдет на подарочную тысячу — бот не обещает' , function () {
$guard = app ( AnswerGuard :: class );
$text = 'При регистрации дарим 1000 ₽ — этого хватит на несколько десятков заявок. Заявка стоит от 55 ₽.' ;
$clean = $guard -> clean ( $text , 'При регистрации дарим 1000 ₽. Заявка стоит от 55 ₽.' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'несколько десятков' )
-> and ( $clean ) -> toContain ( 'от 55 ₽' );
});
it ( 'правду про возврат ОСТАТКА баланса сторож не режет' , function () {
$guard = app ( AnswerGuard :: class );
$article = 'Неизрасходованный остаток баланса возвращается по заявлению на support@liderra.ru, '
. 'до 3 рабочих дней, за вычетом комиссий. Стоимость уже переданных заявок возврату не подлежит.' ;
$text = 'Неизрасходованный остаток баланса можно вернуть по заявлению на support@liderra.ru — деньги придут на те же реквизиты.' ;
$clean = $guard -> clean ( $text , $article , personalFactsKnown : false );
expect ( $clean ) -> toContain ( 'остаток баланса можно вернуть' );
});
it ( 'обещание вернуть деньги ЗА ЗАЯВКУ сторож по-прежнему режет' , function () {
$guard = app ( AnswerGuard :: class );
$text = 'Мы вернём вам деньги за эту заявку, раз человек не взял трубку.' ;
$clean = $guard -> clean ( $text , 'Недозвон — не брак.' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'вернём вам деньги' );
});
it ( 'после вырезанного предложения не остаётся обрубок «Они лежат…»' , function () {
$guard = app ( AnswerGuard :: class );
// Первое предложение — выдумка про гостя, его режем. Второе без него бессмысленно.
$text = 'У вас уже есть 1000 ₽ подарка. Они лежат на счёте, и первые заявки идут за их счёт. Пополнять баланс можно картой или по счёту.' ;
$clean = $guard -> clean ( $text , 'Пополнять баланс можно картой или по счёту.' , personalFactsKnown : false );
expect ( $clean ) -> not -> toContain ( 'Они лежат' )
-> and ( $clean ) -> toContain ( 'картой или по счёту' );
});