115 lines
5.6 KiB
PHP
115 lines
5.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
declare(strict_types=1);
|
||
|
|
|
||
|
|
use App\Models\AutopodborCompetitor;
|
||
|
|
use App\Models\AutopodborRun;
|
||
|
|
use App\Models\AutopodborSource;
|
||
|
|
use App\Models\Project;
|
||
|
|
use App\Models\Tenant;
|
||
|
|
use App\Services\Autopodbor\AutopodborCompetitorMerger;
|
||
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
use Tests\Concerns\SharesSupplierPdo;
|
||
|
|
|
||
|
|
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
||
|
|
|
||
|
|
function mergerTenant(): Tenant
|
||
|
|
{
|
||
|
|
$t = Tenant::factory()->create(['balance_rub' => '100000.00']);
|
||
|
|
DB::statement('SET app.current_tenant_id = '.$t->id);
|
||
|
|
|
||
|
|
return $t;
|
||
|
|
}
|
||
|
|
|
||
|
|
function mkComp(Tenant $t, string $name, ?string $site, array $dirs = []): AutopodborCompetitor
|
||
|
|
{
|
||
|
|
$run = AutopodborRun::create(['tenant_id' => $t->id, 'kind' => 'search', 'status' => 'done', 'region_code' => 16, 'params' => []]);
|
||
|
|
|
||
|
|
return AutopodborCompetitor::create([
|
||
|
|
'tenant_id' => $t->id, 'search_run_id' => $run->id, 'name' => $name,
|
||
|
|
'dedup_key' => 'c:'.md5($name), 'site_url' => $site, 'directory_urls' => $dirs,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
function mkSrc(Tenant $t, AutopodborCompetitor $c, string $type, string $ident, string $dedupKey, ?int $projectId = null, string $box = 'field'): AutopodborSource
|
||
|
|
{
|
||
|
|
$run = AutopodborRun::create(['tenant_id' => $t->id, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'competitor_id' => $c->id, 'params' => []]);
|
||
|
|
|
||
|
|
return AutopodborSource::create([
|
||
|
|
'tenant_id' => $t->id, 'competitor_id' => $c->id, 'study_run_id' => $run->id,
|
||
|
|
'signal_type' => $type, 'identifier' => $ident, 'dedup_key' => $dedupKey,
|
||
|
|
'box' => $box, 'confirmations' => 1, 'created_project_id' => $projectId,
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
it('переносит источники проигравшего на выжившего, дубль-конкурента удаляет, справочники объединяет', function () {
|
||
|
|
$t = mergerTenant();
|
||
|
|
$a = mkComp($t, 'AutoMoney', 'automoney-krsk.ru', ['https://2gis.ru/krasnoyarsk/firm/1']);
|
||
|
|
$b = mkComp($t, 'Автодом24', 'automoney-krsk.ru', ['https://yandex.ru/maps/org/x/2']);
|
||
|
|
mkSrc($t, $a, 'site', 'automoney-krsk.ru', 'site:automoney-krsk.ru');
|
||
|
|
mkSrc($t, $b, 'call', '79135396546', 'call:79135396546');
|
||
|
|
|
||
|
|
$survivorId = app(AutopodborCompetitorMerger::class)->merge($t->id, [$a->id, $b->id]);
|
||
|
|
|
||
|
|
expect(AutopodborSource::where('competitor_id', $survivorId)->count())->toBe(2);
|
||
|
|
$loserId = $survivorId === $a->id ? $b->id : $a->id;
|
||
|
|
expect(AutopodborCompetitor::find($loserId))->toBeNull();
|
||
|
|
$s = AutopodborCompetitor::find($survivorId);
|
||
|
|
expect($s->directory_urls)->toContain('https://2gis.ru/krasnoyarsk/firm/1')->toContain('https://yandex.ru/maps/org/x/2');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('источник с проектом не теряется — переносится, created_project_id сохранён, конкурент с проектом выживает', function () {
|
||
|
|
$t = mergerTenant();
|
||
|
|
$a = mkComp($t, 'A', 'a.ru');
|
||
|
|
$b = mkComp($t, 'B', 'a.ru');
|
||
|
|
$project = Project::factory()->create(['tenant_id' => $t->id, 'is_active' => true, 'preflight_blocked_at' => null]);
|
||
|
|
mkSrc($t, $b, 'call', '79001112233', 'call:79001112233', $project->id);
|
||
|
|
|
||
|
|
$survivorId = app(AutopodborCompetitorMerger::class)->merge($t->id, [$a->id, $b->id]);
|
||
|
|
|
||
|
|
expect($survivorId)->toBe($b->id); // у B источник с проектом → он выживает
|
||
|
|
$src = AutopodborSource::where('created_project_id', $project->id)->first();
|
||
|
|
expect($src)->not->toBeNull();
|
||
|
|
expect($src->competitor_id)->toBe($survivorId);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('коллизия ключа: у выжившего источник с проектом, дубль без проекта отброшен, проект цел', function () {
|
||
|
|
$t = mergerTenant();
|
||
|
|
$project = Project::factory()->create(['tenant_id' => $t->id, 'is_active' => true, 'preflight_blocked_at' => null]);
|
||
|
|
$a = mkComp($t, 'A', 'a.ru');
|
||
|
|
mkSrc($t, $a, 'call', '79001112233', 'call:79001112233', $project->id);
|
||
|
|
$b = mkComp($t, 'B', 'a.ru');
|
||
|
|
mkSrc($t, $b, 'call', '79001112233', 'call:79001112233'); // тот же ключ, без проекта
|
||
|
|
|
||
|
|
$survivorId = app(AutopodborCompetitorMerger::class)->merge($t->id, [$a->id, $b->id]);
|
||
|
|
|
||
|
|
expect($survivorId)->toBe($a->id);
|
||
|
|
expect(AutopodborSource::where('competitor_id', $survivorId)->where('dedup_key', 'call:79001112233')->count())->toBe(1);
|
||
|
|
$src = AutopodborSource::where('competitor_id', $survivorId)->where('dedup_key', 'call:79001112233')->first();
|
||
|
|
expect($src->created_project_id)->toBe($project->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('три конкурента в группе → один выживший, два удалены, все источники у выжившего', function () {
|
||
|
|
$t = mergerTenant();
|
||
|
|
$a = mkComp($t, 'A', 'x.ru');
|
||
|
|
$b = mkComp($t, 'B', 'x.ru');
|
||
|
|
$c = mkComp($t, 'C', 'x.ru');
|
||
|
|
mkSrc($t, $a, 'site', 'x.ru', 'site:x.ru');
|
||
|
|
mkSrc($t, $b, 'call', '79001112233', 'call:79001112233');
|
||
|
|
mkSrc($t, $c, 'call', '79004445566', 'call:79004445566');
|
||
|
|
|
||
|
|
$survivorId = app(AutopodborCompetitorMerger::class)->merge($t->id, [$a->id, $b->id, $c->id]);
|
||
|
|
|
||
|
|
expect(AutopodborCompetitor::whereIn('id', [$a->id, $b->id, $c->id])->count())->toBe(1);
|
||
|
|
expect(AutopodborSource::where('competitor_id', $survivorId)->count())->toBe(3);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('меньше двух конкурентов тенанта → исключение (ничего не сливаем)', function () {
|
||
|
|
$t = mergerTenant();
|
||
|
|
$a = mkComp($t, 'A', 'a.ru');
|
||
|
|
|
||
|
|
expect(fn () => app(AutopodborCompetitorMerger::class)->merge($t->id, [$a->id]))
|
||
|
|
->toThrow(InvalidArgumentException::class);
|
||
|
|
});
|