Files
portal/app/tests/Feature/Autopodbor/LiveFindProgressTest.php
T
Дмитрий 428b7b0f01 feat(autopodbor): прогресс прогона — очередь и этапы (бэкенд)
Клиент во время сбора видит место в общей очереди (пока ждёт) и текущий
этап (пока идёт). Очередь — длина общей Redis-очереди (честное ожидание,
без RLS, одинаково dev/прод). Этапы пишет сам движок через RunProgressChannel:
поиск (анализ→справочники→федералы→сбор→отбор), изучение — по каждому элементу.

- миграция progress (jsonb) на autopodbor_runs + cast
- RunResource отдаёт queue_position (AutopodborQueue) и progress
- RunProgressChannel (синглтон), джобы привязывают его к своему прогону
- SearchStages + инструментовка LiveFindCompetitors и RealCompetitorAgent
- 16 тестов (Feature+Unit), все зелёные

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-05 04:31:01 +03:00

98 lines
3.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Services\Autopodbor\Agent\Aggregator\AggregatorFilter;
use App\Services\Autopodbor\Agent\Aggregator\AitunnelAggregatorClassifier;
use App\Services\Autopodbor\Agent\ChannelA\CategoryListingParser;
use App\Services\Autopodbor\Agent\ChannelA\CategoryScraper;
use App\Services\Autopodbor\Agent\ChannelA\QueryAnalyzer;
use App\Services\Autopodbor\Agent\ChannelA\YandexDirectory;
use App\Services\Autopodbor\Agent\ChannelB\AitunnelResearcher;
use App\Services\Autopodbor\Agent\ChannelB\ChannelBSearch;
use App\Services\Autopodbor\Agent\ChannelB\ExaSiteFinder;
use App\Services\Autopodbor\Agent\ChannelB\ResearcherParser;
use App\Services\Autopodbor\Agent\Dto\FindCompetitorsRequest;
use App\Services\Autopodbor\Agent\Fetch\LivePageFetcher;
use App\Services\Autopodbor\Agent\Fetch\XfetchClient;
use App\Services\Autopodbor\Agent\FindCompetitorsAssembler;
use App\Services\Autopodbor\Agent\LiveFindCompetitors;
use App\Services\Autopodbor\Agent\Similarity\AitunnelEmbedder;
use App\Services\Autopodbor\Agent\Similarity\EmbeddingRelevance;
use App\Services\Autopodbor\AutopodborDedup;
use App\Services\Autopodbor\AutopodborNormalizer;
use App\Services\Autopodbor\RunProgressChannel;
use Illuminate\Http\Client\Factory as HttpFactory;
/** Канал-шпион: копит вызовы этапов вместо записи в БД. */
function spyProgressChannel(): RunProgressChannel
{
return new class extends RunProgressChannel
{
/** @var list<array{int,int,string}> */
public array $calls = [];
public function stage(int $stage, int $total, string $label): void
{
$this->calls[] = [$stage, $total, $label];
}
};
}
/**
* Живой движок с заглушками-интерфейсами (анализ отдаёт пустые запросы → живые каналы не дёргаются) и
* дешёвыми экземплярами конкретных зависимостей (они не вызываются при пустом наборе). Так проверяем
* ТОЛЬКО последовательность этапов, не поднимая сеть.
*/
function liveFindWithProgress(RunProgressChannel $progress, bool $withEnricher = false): LiveFindCompetitors
{
$http = app(HttpFactory::class);
$xfetch = new XfetchClient(apiKey: '', endpoint: 'https://xf4.ru/fetch', concurrency: 1);
$analyzer = new class implements QueryAnalyzer
{
public function analyze(string $description, string $region, string $selfSite = '', array $competitorExamples = []): array
{
return [];
}
};
$yandex = new class implements YandexDirectory
{
public function collect(string $city, array $queries): array
{
return [];
}
};
$assembler = new FindCompetitorsAssembler(
new AggregatorFilter(app(AitunnelAggregatorClassifier::class)),
new AutopodborDedup(new AutopodborNormalizer),
new EmbeddingRelevance(app(AitunnelEmbedder::class)),
);
return new LiveFindCompetitors(
$analyzer,
new CategoryScraper(new LivePageFetcher($xfetch), new CategoryListingParser, 4),
$yandex,
new ChannelBSearch(new AitunnelResearcher($http), new ResearcherParser),
new ExaSiteFinder($http),
$assembler,
progress: $progress,
);
}
it('поиск отмечает этапы в канал по порядку (базовый: 3 этапа)', function () {
$spy = spyProgressChannel();
$live = liveFindWithProgress($spy);
$live->find(new FindCompetitorsRequest(
regionCode: 16, examples: [], aboutSelf: [], includeFederal: false, maxCompetitors: 15,
));
expect($spy->calls)->toBe([
[1, 3, 'Разбираем ваш запрос'],
[2, 3, 'Ищем фирмы в справочниках'],
[3, 3, 'Отбираем самых похожих'],
]);
});