9cd12f179f
В трёх статьях было написано «не берёт трубку → заменим». Это ошибка: замену мы не делаем, и бот честно обещал её клиентам. Тон тонкий, тему сглаживаем: - не обещать замену/возврат («заменим», «вернём деньги») — AnswerGuard::promisesReplacement; - но и не рубить дверью («замена не полагается», «деньги не возвращаются») — тоже режем; - мягко: недозвон — обычное дело; совет СМС → звонок позже → мессенджер; спорный случай на support@liderra.ru, специалист разбирает индивидуально, без обещаний; - не выдумывать срок ответа поддержки (она 24/7). Статьи: zamena-zayavki.md переписана, поправлены kak-rabotat-s-nomerom.md и sdelki.md. Два старых теста охраняли прежнее (неверное) правило — переписаны под новое. Тесты 130/130. Бот НЕ на проде. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
138 lines
7.7 KiB
PHP
138 lines
7.7 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\KnowledgeChunk;
|
|
use App\Services\Bot\KnowledgeSearch;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
/**
|
|
* Разбор пятого прогона четырьмя проверяющими (12.07.2026): поиск берёт соседнюю
|
|
* статью, бот выдумывает статусы и даёт юридические гарантии.
|
|
*/
|
|
beforeEach(function () {
|
|
KnowledgeChunk::query()->delete();
|
|
Artisan::call('help:rebuild-knowledge');
|
|
});
|
|
|
|
function topArticle(string $question): string
|
|
{
|
|
$hits = app(KnowledgeSearch::class)->search($question, 3);
|
|
expect($hits)->not->toBeEmpty();
|
|
|
|
return $hits[0]->title;
|
|
}
|
|
|
|
it('поиск берёт статью ПО ТЕМЕ, а не соседнюю с теми же словами', function () {
|
|
$cases = [
|
|
'как создать проект' => 'Создать проект — пошагово',
|
|
'какие поля надо заполнить при создании проекта' => 'Создать проект — пошагово',
|
|
'как запустить проект обратно' => 'Пауза и запуск проекта',
|
|
'что такое сделка' => 'Сделки',
|
|
'можно скачать заявки в csv' => 'Выгрузить сделки',
|
|
'есть ли абонентская плата' => 'Тарифы',
|
|
'в какое время дня приходят лиды' => 'Выгрузка по проекту',
|
|
'у меня один и тот же конкурент два раза' => 'Дубли конкурентов',
|
|
'нашел источник, что дальше делать' => 'Проект из найденного источника',
|
|
'а бесплатно можно?' => 'Бесплатный тест',
|
|
'дубликаты номеров бывают?' => 'Повторы и дубли номеров',
|
|
'насколько свежие заявки, сколько времени прошло' => 'Что вы получаете',
|
|
];
|
|
|
|
foreach ($cases as $q => $expected) {
|
|
expect(topArticle($q))->toContain($expected);
|
|
}
|
|
});
|
|
|
|
it('в инструкции нет выдуманных статусов «Отказ» и «Успешная»', function () {
|
|
// Судья 12.07.2026: бот советовал ставить статус «Отказ» — его нет. Пять статусов:
|
|
// Новая сделка / Просмотрено / В работе / Сделка / Не реализовано.
|
|
$all = KnowledgeChunk::query()->pluck('content')->implode(' ');
|
|
|
|
expect($all)->not->toContain('статус «Отказ»')
|
|
->and($all)->not->toContain('статус «Успешная»');
|
|
|
|
$deals = KnowledgeChunk::query()
|
|
->where('source_path', 'help/sdelki.md')
|
|
->orderBy('chunk_index')
|
|
->pluck('content')
|
|
->implode(' ');
|
|
|
|
expect($deals)->toContain('«Не реализовано»');
|
|
});
|
|
|
|
it('значение источника менять можно — бот не имеет права говорить «нельзя»', function () {
|
|
// Судья 12.07.2026: на «могу ли я поменять сайт в проекте» бот ответил «нельзя,
|
|
// заводите новый». Неправда: не меняется только ТИП источника, а сам сайт — меняется.
|
|
$hits = app(KnowledgeSearch::class)->search('могу ли я поменять сайт в уже созданном проекте', 3);
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
|
|
expect($hits[0]->title)->toContain('Сменить источник')
|
|
->and($text)->toContain('Менять можно значение');
|
|
});
|
|
|
|
it('за повтор номера деньги не берутся, а списание идёт за каждую заявку', function () {
|
|
// Судья 12.07.2026: на «списываются ли деньги за дубли» бот ответил, что деньги
|
|
// списываются ТОЛЬКО за услуги Конкурентного поля. Это враньё про деньги.
|
|
$hits = app(KnowledgeSearch::class)->search('списывается ли деньги за дубли', 3);
|
|
expect($hits[0]->title)->toContain('Повторы и дубли номеров');
|
|
|
|
$article = KnowledgeChunk::query()
|
|
->where('source_path', 'help/dubli-nomerov.md')
|
|
->orderBy('chunk_index')
|
|
->pluck('content')
|
|
->implode(' ');
|
|
|
|
expect($article)->toContain('30 дней')
|
|
->and($article)->toContain('дважды за него не заплатите');
|
|
});
|
|
|
|
it('про штрафы бот не даёт гарантий — ответственность на клиенте', function () {
|
|
// Судья 12.07.2026: бот категорично обещал «Нет, вас не оштрафуют». Гарантий не даём.
|
|
$hits = app(KnowledgeSearch::class)->search('меня оштрафуют если я буду звонить по вашим номерам', 3);
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
|
|
expect($text)->toContain('гарантий по вашей рекламе мы не даём');
|
|
});
|
|
|
|
it('зависшее изучение — это поддержка, а не «не хватило баланса»', function () {
|
|
// Судья 12.07.2026: бот выдумал причину «наверное, не хватает денег». При нехватке
|
|
// баланса портал вообще не запускает задание и прямо об этом говорит.
|
|
$hits = app(KnowledgeSearch::class)->search('изучение висит в очереди уже час', 3);
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
|
|
expect($text)->toContain('висит дольше')
|
|
->and($text)->toContain('support@liderra.ru');
|
|
});
|
|
|
|
it('на старте не требует пополнения — подарок уже на балансе', function () {
|
|
foreach (['с чего начать?', 'я только зарегистрировался что дальше'] as $q) {
|
|
$hits = app(KnowledgeSearch::class)->search($q, 3);
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
expect($text)->toContain('1000 ₽');
|
|
}
|
|
});
|
|
|
|
/**
|
|
* Правило владельца 13.07.2026: замены заявки НЕТ (недозвон браком не считается).
|
|
* Прежний текст статьи обещал «заменим» — это была ошибка, и бот её повторял клиентам.
|
|
*/
|
|
it('«верните деньги за заявку» — находится статья про недозвон, и она НЕ обещает замену', function () {
|
|
$hits = app(KnowledgeSearch::class)->search('верните деньги за плохую заявку', 3);
|
|
|
|
expect(collect($hits)->pluck('title')->implode(' | '))->toContain('не берёт трубку');
|
|
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
expect($text)->not->toContain('заменим')
|
|
->and($text)->not->toContain('можем заменить');
|
|
});
|
|
|
|
it('ночью и после лимита — отвечает по статье, без выдумок', function () {
|
|
$hits = app(KnowledgeSearch::class)->search('ночью заявки приходят?', 3);
|
|
$text = collect($hits)->pluck('content')->implode(' ');
|
|
|
|
expect($text)->toContain('Ночью заявки не приходят');
|
|
});
|