Files
portal/app/tests/Unit/Autopodbor/ChannelB/ResearcherParserTest.php
T
Дмитрий 5a65165114 feat(автоподбор): движок шага 1 пересобран под финал v4 (каналы А+В, EXA)
Замена вырожденного «одна фраза → одна страница» на §12/§11.3 финал:
- Шаг АНАЛИЗ (ChannelA\AitunnelQueryAnalyzer): описание → запросы-рубрики (мелкая модель).
- Канал А (ChannelA\CategoryScraper): скрейп категории 2ГИС с пагинацией → резолв карточек.
- Канал В (ChannelB\*): ОДНА модель sonar-reasoning-pro × 2 прохода → ТОЛЬКО имена
  федералов; стоп-лист = имена из А + примеры; сайт федерала через EXA (ExaSiteFinder),
  т.к. у федерала нет карточки в 2ГИС/Яндексе на регион.
- Оркестратор LiveFindCompetitors переписан: АНАЛИЗ→А→В→слияние→отсев→дедуп→похожесть→DTO.
- Провайдер перепрошит; config services.php +research_model/exa.
Похожесть — эмбеддер-модель (математически), резолвер/дедуп — без изменений.
Всё за тонкими границами, офлайн-тесты на фикстурах: модуль 130 unit + 74 feature зелёные.
Провайдер за флагом autopodbor.real_find; на проде не меняется.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 21:06:10 +03:00

49 lines
2.1 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('пустой массив и мусор дают []', function () {
expect($this->parser->parse('[]'))->toBe([])
->and($this->parser->parse('извините, ничего'))->toBe([])
->and($this->parser->parse(''))->toBe([]);
});