*/ public array $seen = []; public function collect(Fingerprint $fp): array { $this->seen[] = $fp; return [[ 'signal_type' => 'call', 'identifier' => 'phone-'.$fp->name, 'phone_kind' => 'real', 'phone_type' => 'city', 'provenance_url' => null, 'provenance_label' => $fp->name, 'where_found' => [], 'office' => null, 'confirmations' => 1, ]]; } } /** Фетчер-заглушка: в глубоком пути не вызывается. */ class NullStudyFetcher implements Fetcher { public function site(string $url): FetchedSite { throw new \LogicException('not used in deep path'); } public function directory(string $url): array { return []; } } function studyAgent(FakeStudyCollector $c): RealCompetitorAgent { return new RealCompetitorAgent(new NullStudyFetcher, new FakeCompetitorAgent, deep: $c); } it('глубокое изучение строит отпечаток на КАЖДЫЙ элемент и сводит строки', function () { config(['autopodbor.deep_study' => true]); $c = new FakeStudyCollector; $res = studyAgent($c)->studyCompetitor(new StudyCompetitorRequest( competitor: [ 'name' => 'Сибиряк', 'is_federal' => false, 'elements' => [ ['name' => 'Нанжуль', 'sites' => ['nanzhul.ru'], 'cards' => []], ['name' => 'Плодово', 'sites' => ['plodovo.ru'], 'cards' => []], ], ], regionCode: 24, )); expect(array_map(fn (Fingerprint $fp) => $fp->name, $c->seen))->toBe(['Нанжуль', 'Плодово']) ->and(array_map(fn (Fingerprint $fp) => $fp->seedDomain, $c->seen))->toBe(['nanzhul.ru', 'plodovo.ru']) ->and(collect($res->sources)->pluck('identifier')->all())->toBe(['phone-Нанжуль', 'phone-Плодово']); }); it('без элементов — один отпечаток из имени+сайта (обратная совместимость)', function () { config(['autopodbor.deep_study' => true]); $c = new FakeStudyCollector; studyAgent($c)->studyCompetitor(new StudyCompetitorRequest( competitor: ['name' => 'Одиночка', 'site_url' => 'one.ru', 'is_federal' => false], regionCode: 24, )); expect($c->seen)->toHaveCount(1) ->and($c->seen[0]->name)->toBe('Одиночка') ->and($c->seen[0]->seedDomain)->toBe('one.ru'); });