mergeCompetitors([ ['name' => 'Драйв займ'], // только имя (из канала В) ['name' => 'Драйв Займ', 'site_url' => 'https://драйвзайм.рф/'], // имя + домен (из канала А) ]); // имя «драйвзайм» = корень домена «драйвзайм» → одна карточка expect($merged)->toHaveCount(1); expect($merged[0]['site_url'])->toBe('https://драйвзайм.рф/'); }); it('склеивает по общему домену при разном написании имени', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Окна Комфорт', 'site_url' => 'https://okna-komfort.ru/contacts'], ['name' => 'ОКНА-КОМФОРТ', 'site_url' => 'okna-komfort.ru'], ]); expect($merged)->toHaveCount(1); }); it('склеивает по общему телефону', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Фирма А', 'phones' => ['73912920000']], ['name' => 'Фирма Б', 'phones' => ['73912920000', '79991112233']], ]); expect($merged)->toHaveCount(1); expect($merged[0]['phones'])->toContain('73912920000')->toContain('79991112233'); }); it('объединяет ссылки справочников и телефоны внутри группы', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'X', 'site_url' => 'x.ru', 'directory_urls' => ['https://2gis.ru/firm/1'], 'phones' => ['71112223344']], ['name' => 'X', 'site_url' => 'https://x.ru/', 'directory_urls' => ['https://yandex.ru/maps/org/x/2'], 'phones' => ['75556667788']], ]); expect($merged)->toHaveCount(1); expect($merged[0]['directory_urls'])->toContain('https://2gis.ru/firm/1')->toContain('https://yandex.ru/maps/org/x/2'); expect($merged[0]['phones'])->toContain('71112223344')->toContain('75556667788'); }); it('склеивает по общему коду справочника (slug Яндекса), даже при разном написании имени', function () { // Один и тот же «КрасЛомбард», но имя записано по-разному → по имени НЕ сойдётся. // Спасает опознавательный код фирмы от Яндекса (slug) — это признак источника, не догадка. $merged = mergeService()->mergeCompetitors([ ['name' => 'КрасЛомбард', 'directory_keys' => ['ya:kraslombard'], 'directory_urls' => ['https://yandex.ru/maps/org/kraslombard/333']], ['name' => 'Красломбард 24 Взлётка', 'directory_keys' => ['ya:kraslombard'], 'directory_urls' => ['https://yandex.ru/maps/org/kraslombard/444']], ]); expect($merged)->toHaveCount(1) ->and($merged[0]['directory_urls'])->toHaveCount(2); }); it('склеивает «Имя» и «Имя + слово-рубрику» — слово берётся из рубрики фирмы (не из списка)', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Яричъ Ломбард', 'description' => 'Ломбард', 'directory_urls' => ['https://yandex.ru/maps/org/yarichl/1']], ['name' => 'Яричъ', 'description' => 'Ломбард', 'directory_urls' => ['https://yandex.ru/maps/org/yarich/2']], ]); expect($merged)->toHaveCount(1); }); it('склейка по словарю рубрик: длинное имя БЕЗ своей рубрики тоже цепляется (слово из общего словаря)', function () { // У «Яричъ Ломбард» своей рубрики нет, но «Ломбард» есть в словаре (его дала рубрика «Яричъ»). $merged = mergeService()->mergeCompetitors([ ['name' => 'Яричъ Ломбард', 'description' => null], ['name' => 'Яричъ', 'description' => 'Ломбард'], ]); expect($merged)->toHaveCount(1); }); it('универсально: та же склейка для стоматолога (слово-категория из рубрики, без зашитых ниш)', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Улыбка Стоматология', 'description' => 'Стоматология'], ['name' => 'Улыбка', 'description' => 'Стоматология'], ]); expect($merged)->toHaveCount(1); }); it('генерик из нескольких слов НЕ склеивается («Ломбард Займы под залог» ≠ «Займы под залог»)', function () { // После стрижки остаётся описательная фраза «займы под залог» (3 слова) — это генерик, не бренд. // Третья фирма кладёт «ломбард» в словарь рубрик (как в реальном прогоне). $merged = mergeService()->mergeCompetitors([ ['name' => 'Ломбард Займы под залог', 'description' => null], ['name' => 'Займы под залог', 'description' => 'Микрофинансовая организация'], ['name' => 'Рубин', 'description' => 'Ломбард'], ]); expect($merged)->toHaveCount(3); }); it('НЕ склеивает разные имена с одинаковой рубрикой (защита от пере-склейки)', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Быстро Ломбард', 'description' => 'Ломбард'], ['name' => 'Дёшево Ломбард', 'description' => 'Ломбард'], ]); expect($merged)->toHaveCount(2); // «быстро» ≠ «дёшево» }); it('голое имя-рубрика не плодит пустой ключ (генерики не слипаются по пустоте)', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Ломбард', 'description' => 'Ломбард'], ['name' => 'Займ', 'description' => 'Займ'], ]); expect($merged)->toHaveCount(2); }); it('не склеивает разных конкурентов', function () { $merged = mergeService()->mergeCompetitors([ ['name' => 'Окна Комфорт', 'site_url' => 'okna-komfort.ru'], ['name' => 'Пластика Окон', 'site_url' => 'plastika.ru'], ]); expect($merged)->toHaveCount(2); }); it('вычитает самого клиента (по домену и по имени)', function () { $merged = mergeService()->mergeCompetitors( [ ['name' => 'Мой Бизнес', 'site_url' => 'moy-biznes.ru'], // это сам клиент по домену ['name' => 'Драйв займ', 'site_url' => 'драйвзайм.рф'], ['name' => 'Мой Бизнес Плюс'], // сам клиент по имени ], clientKeys: ['https://moy-biznes.ru', 'Мой Бизнес Плюс'], ); $names = array_column($merged, 'name'); expect($names)->not->toContain('Мой Бизнес'); expect($names)->not->toContain('Мой Бизнес Плюс'); expect($names)->toContain('Драйв займ'); });