Files
portal/app/tests/Unit/Autopodbor/ChannelB/ResearcherParserTest.php
T
Дмитрий df83fa836c feat(автоподбор): Конкурентное поле — финальный проход склейки, вскрыть всё, группы дублей, телефоны, отсев рекламных номеров 2ГИС
Шаг 1 «Конкурентное поле» — чекпоинт:
- Финальный проход «Найти и объединить дубли» на поле и предложениях; клиент решает по каждой группе, ничего не склеиваем молча.
- Тихая склейка только по сайту и коду справочника; телефон и людный номер у более чем 4 фирм — на решение клиента.
- Колонка phones jsonb; телефон-дубли видны уже на «Предложениях».
- Sonar даёт только имя и тип, сайт всегда через EXA; вскрываем все карточки без гейта «есть сайт».
- Яндекс-карточки через локальный Playwright параллельно; рубрика из заголовка идёт в описание.
- Фикс рекламных номеров: 2ГИС-парсер разбирает телефоны пообъектно и выкидывает рекламные кнопки с platforms/caption чужих фирм.

Тесты: автоподбор 224/224 unit, 105/105 feature; фронт-спеки зелёные. Времянки app/scripts/diag-*/probe-* в гит не входят.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-03 17:34:03 +03:00

61 lines
2.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Services\Autopodbor\Agent\ChannelB\ResearcherParser;
// Канал В, ФИНАЛ (ZAFIKSIROVANO §0-БИС + §11.3): ИИ даёт ТОЛЬКО НАЗВАНИЯ (+ тип), без сайтов/карточек.
// Якоря (сайт/2ГИС/Яндекс/телефоны) добывает ПОТОМ Firecrawl/резолвер. Парсер вытаскивает имена
// из сырого ответа модели (часто JSON в markdown-обёртке). Чистая логика, без сети.
beforeEach(function () {
$this->parser = new ResearcherParser;
});
it('разбирает чистый JSON в имена + тип', function () {
$raw = '[{"name":"CarMoney","type":"федеральная"},{"name":"Ваш инвестор","type":"региональная"}]';
$out = $this->parser->parse($raw);
expect($out)->toHaveCount(2)
->and($out[0]['name'])->toBe('CarMoney')
->and($out[0]['type'])->toBe('федеральная')
->and($out[1]['name'])->toBe('Ваш инвестор');
});
it('вытаскивает JSON из markdown-обёртки и текста вокруг', function () {
$raw = "Вот названия:\n```json\n".'[{"name":"Финео","type":"федеральная"}]'."\n```\nГотово.";
$out = $this->parser->parse($raw);
expect($out)->toHaveCount(1)->and($out[0]['name'])->toBe('Финео');
});
it('пропускает элемент без имени; тип может отсутствовать', function () {
$raw = '[{"name":"","type":"региональная"},{"name":"Голд Авто Инвест"}]';
$out = $this->parser->parse($raw);
expect($out)->toHaveCount(1)
->and($out[0]['name'])->toBe('Голд Авто Инвест')
->and($out[0]['type'])->toBeNull();
});
it('модель даёт ТОЛЬКО имя и тип — сайт/карточку парсер НЕ вытаскивает (их найдёт EXA)', function () {
// Даже если модель по инерции положит site/card — парсер их игнорирует: якорь ищет EXA.
$raw = '[{"name":"CarMoney","type":"федеральная","site":"carmoney.ru","card":"https://2gis.ru/firm/999"}]';
$out = $this->parser->parse($raw);
expect($out)->toHaveCount(1)
->and($out[0]['name'])->toBe('CarMoney')
->and($out[0]['type'])->toBe('федеральная')
->and(array_keys($out[0]))->toBe(['name', 'type']); // никаких site/card
});
it('пустой массив и мусор дают []', function () {
expect($this->parser->parse('[]'))->toBe([])
->and($this->parser->parse('извините, ничего'))->toBe([])
->and($this->parser->parse(''))->toBe([]);
});