map[$phoneDigits] ?? []; } }; } function fakeCards(array $map): YandexCardReader { return new class($map) implements YandexCardReader { public function __construct(private array $map) {} public function read(string $cardUrl): ?array { return $this->map[$cardUrl] ?? null; } }; } it('оставляет только карточку, где искомый номер РЕАЛЬНО есть (отсекает похожие/рыхлые)', function () { $search = fakeSearch(['73912179941' => [ ['name' => 'СМ.Сити', 'card_url' => 'https://yandex.ru/maps/org/sm/1/'], ['name' => 'Похожее СпецСтрой', 'card_url' => 'https://yandex.ru/maps/org/spec/2/'], ['name' => 'СМ Сити Капитанская', 'card_url' => 'https://yandex.ru/maps/org/smk/3/'], ]]); $cards = fakeCards([ 'https://yandex.ru/maps/org/sm/1/' => ['phones' => ['73912179941'], 'site' => 'https://sm-city.ru'], 'https://yandex.ru/maps/org/spec/2/' => ['phones' => ['73912990000'], 'site' => 'https://specstroy.ru'], 'https://yandex.ru/maps/org/smk/3/' => ['phones' => ['73912749569'], 'site' => 'https://smk.ru'], ]); $exp = new YandexPhoneReverseExpander($search, $cards); $res = $exp->expand('', ['73912179941'], [], '62/krasnoyarsk'); $domains = array_column($res->sites, 'domain'); expect($domains)->toContain('sm-city.ru') ->and($domains)->not->toContain('specstroy.ru') ->and($domains)->not->toContain('smk.ru'); }); it('пустой yandexSlug → разворот невозможен, пусто', function () { $exp = new YandexPhoneReverseExpander(fakeSearch([]), fakeCards([])); $res = $exp->expand('', ['73912179941'], [], ''); expect($res->sites)->toBe([])->and($res->phones)->toBe([]); }); it('порог общей линии — если подтверждённых карточек больше порога, номер общий, пропускаем', function () { $cands = []; $cardsMap = []; foreach (range(1, 5) as $i) { $u = "https://yandex.ru/maps/org/o$i/$i/"; $cands[] = ['name' => "O$i", 'card_url' => $u]; $cardsMap[$u] = ['phones' => ['78001234567'], 'site' => "https://o$i.ru"]; } $exp = new YandexPhoneReverseExpander(fakeSearch(['78001234567' => $cands]), fakeCards($cardsMap), crowdedCap: 4); $res = $exp->expand('', ['78001234567'], [], '62/krasnoyarsk'); expect($res->sites)->toBe([]); }); it('уже известный домен фирмы не повторяет', function () { $search = fakeSearch(['73912179941' => [['name' => 'СМ', 'card_url' => 'https://yandex.ru/maps/org/sm/1/']]]); $cards = fakeCards(['https://yandex.ru/maps/org/sm/1/' => ['phones' => ['73912179941'], 'site' => 'https://sm-city.ru']]); $exp = new YandexPhoneReverseExpander($search, $cards); $res = $exp->expand('', ['73912179941'], ['sm-city.ru'], '62/krasnoyarsk'); expect(array_column($res->sites, 'domain'))->not->toContain('sm-city.ru'); }); it('цепочка при maxDepth>=2: телефон найденной карточки разворачивается дальше', function () { $search = fakeSearch([ '73912179941' => [['name' => 'A', 'card_url' => 'https://yandex.ru/maps/org/a/1/']], '73912000002' => [['name' => 'B', 'card_url' => 'https://yandex.ru/maps/org/b/2/']], ]); $cards = fakeCards([ 'https://yandex.ru/maps/org/a/1/' => ['phones' => ['73912179941', '73912000002'], 'site' => 'https://a.ru'], 'https://yandex.ru/maps/org/b/2/' => ['phones' => ['73912000002'], 'site' => 'https://b-proekt.ru'], ]); $exp = new YandexPhoneReverseExpander($search, $cards, maxDepth: 2); $res = $exp->expand('', ['73912179941'], [], '62/krasnoyarsk'); expect(array_column($res->sites, 'domain'))->toContain('b-proekt.ru'); }); it('по умолчанию (глубина 1) НЕ разворачивает по номеру найденной карточки — дрейф отсечён', function () { $search = fakeSearch([ '73912179941' => [['name' => 'A', 'card_url' => 'https://yandex.ru/maps/org/a/1/']], '73912000002' => [['name' => 'B', 'card_url' => 'https://yandex.ru/maps/org/b/2/']], ]); $cards = fakeCards([ 'https://yandex.ru/maps/org/a/1/' => ['phones' => ['73912179941', '73912000002'], 'site' => 'https://a.ru'], 'https://yandex.ru/maps/org/b/2/' => ['phones' => ['73912000002'], 'site' => 'https://b-proekt.ru'], ]); $exp = new YandexPhoneReverseExpander($search, $cards); $res = $exp->expand('', ['73912179941'], [], '62/krasnoyarsk'); expect(array_column($res->sites, 'domain'))->not->toContain('b-proekt.ru'); });