Files
portal/app/tests/Feature/Autopodbor/DeepStudyClientCardAnchorTest.php
T

115 lines
4.2 KiB
PHP
Raw Normal View History

<?php
use App\Services\Autopodbor\Agent\Extract\TwoGisReader;
use App\Services\Autopodbor\Agent\Fetch\BatchPageFetcher;
use App\Services\Autopodbor\Agent\Fetch\DirectoryCard;
use App\Services\Autopodbor\Agent\Fetch\FetchedSite;
use App\Services\Autopodbor\Agent\Fetch\Fetcher;
use App\Services\Autopodbor\Agent\Study\DeepStudyCollector;
use App\Services\Autopodbor\Agent\Study\Fingerprint;
use App\Services\Autopodbor\Agent\Study\SiteOpener;
// Земля: клиент привязал карточку 2ГИС с номером 002005 → он якорь и источник. Слепой поиск по
// широкому имени «Притяжение» вернул чужой салон (303303) — он должен отсеяться (не бьётся с якорем).
// Доказано на живых данных (стоматология/салоны попадали в карточку конкурента) — 2026-07-06.
const CLIENT_CARD = 'https://2gis.ru/krasnoyarsk/firm/CLIENT';
const FOREIGN_FIRM = 'https://2gis.ru/krasnoyarsk/firm/FOREIGN';
function fakeXfetch(): BatchPageFetcher
{
return new class implements BatchPageFetcher
{
public function html(string $url): string
{
if ($url === CLIENT_CARD) {
return 'CLIENT';
}
if (str_contains($url, '/search/')) {
return 'SEARCH';
}
if (str_contains($url, 'FOREIGN')) {
return 'FOREIGN';
}
return '';
}
public function htmlBatch(array $urls): array
{
$out = [];
foreach ($urls as $u) {
$out[$u] = $this->html($u);
}
return $out;
}
};
}
function fakeReader(): TwoGisReader
{
return new class implements TwoGisReader
{
public function listing(string $payload): array
{
return [];
}
public function card(string $payload, string $url): array
{
if ($payload === 'CLIENT') {
return [new DirectoryCard(number: '8 (391) 200-20-05', office: null, url: $url, source: '2ГИС', siteUrl: null, description: null)];
}
if ($payload === 'FOREIGN') {
return [new DirectoryCard(number: '8 (391) 230-33-03', office: null, url: $url, source: '2ГИС', siteUrl: 'http://salon.ru', description: 'Парикмахерские')];
}
return [];
}
public function branchList(string $payload): array
{
return $payload === 'SEARCH' ? ['/krasnoyarsk/firm/FOREIGN'] : [];
}
};
}
function emptyOpener(): SiteOpener
{
$fetcher = new class implements Fetcher
{
public function site(string $url): FetchedSite
{
return new FetchedSite($url, '');
}
public function directory(string $url): array
{
return [];
}
};
return new SiteOpener($fetcher);
}
it('привязанная карточка = якорь: её номер в источниках, чужой салон из слепого поиска отсеян', function () {
$collector = new DeepStudyCollector(xfetch: fakeXfetch(), opener: emptyOpener(), reader: fakeReader());
$fp = new Fingerprint('Притяжение', '', 'Красноярск', '391', 'krasnoyarsk', false, '', [CLIENT_CARD]);
$calls = array_map(
fn ($r) => $r['identifier'],
array_values(array_filter($collector->collect($fp), fn ($r) => $r['signal_type'] === 'call')),
);
expect($calls)->toContain('73912002005') // номер с привязанной карточки — есть
->and($calls)->not->toContain('73912303303'); // салон из слепого поиска — отсеян
});
it('нет якоря совсем (без сайта и без карточек) — слепой поиск не берётся, пусто', function () {
$collector = new DeepStudyCollector(xfetch: fakeXfetch(), opener: emptyOpener(), reader: fakeReader());
$fp = new Fingerprint('Притяжение', '', 'Красноярск', '391', 'krasnoyarsk', false, '', []);
expect($collector->collect($fp))->toBe([]);
});