set('services.exa', ['key' => 't', 'base_url' => 'https://api.exa.ai']); Http::fake([ 'api.exa.ai/*' => Http::response(['results' => [ ['url' => 'https://kraslombard24.ru/'], ['url' => 'https://xn--80aacn0ahhppgf.xn--p1ai/'], // красломбард.рф в punycode ]]), '*' => Http::response(['response_body_base64' => '']), ]); $siteFetcher = new class implements Fetcher { public function site(string $url): FetchedSite { return new FetchedSite($url, ''.$url.''); } public function directory(string $url): array { return []; } }; $collector = new DeepStudyCollector(new XfetchClient(null), new SiteOpener($siteFetcher)); $fp = new Fingerprint('КрасЛомбард', 'kraslombard24.ru', 'Красноярск', '391', 'krasnoyarsk', false); $rows = $collector->collect($fp); $sites = array_values(array_filter($rows, fn ($r) => $r['signal_type'] === 'site')); $domains = array_map(fn ($r) => $r['identifier'], $sites); expect(count($sites))->toBe(2) ->and($domains)->toContain('kraslombard24.ru') ->and($domains)->toContain('красломбард.рф'); $call = array_values(array_filter($rows, fn ($r) => $r['signal_type'] === 'call' && $r['identifier'] === '73912920000'))[0]; expect(collect($call['where_found'])->pluck('label'))->toContain('в коде сайта') ->and($call['phone_kind'])->toBe('real'); });