Files
portal/app/tests/Feature/Autopodbor/AutopodborManualApiTest.php
T
Дмитрий ec907d27fa feat(autopodbor): имя проекта без бессмысленной цифры и переименование при переносе
Имя проекта = имя конкурента; при совпадении различаем источником сайт/номер,
а не хвостом «2». Один проект у конкурента — просто «Сибиряк», несколько —
«Сибиряк · сибиряк.рф». При переносе источника к другому конкуренту проект
переименовывается на нового конкурента.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 05:28:24 +03:00

129 lines
7.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Models\AutopodborCompetitor;
use App\Models\AutopodborRun;
use App\Models\AutopodborSource;
use App\Models\Project;
use App\Models\SystemSetting;
use App\Models\Tenant;
use App\Models\User;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Queue;
use Tests\Concerns\SharesSupplierPdo;
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
beforeEach(fn () => Queue::fake());
it('manual-study по сайту создаёт ручного конкурента и study-прогон', function () {
$tenant = Tenant::factory()->create(['balance_rub' => '100000.00']);
$user = User::factory()->create(['tenant_id' => $tenant->id]);
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '1', 'type' => 'decimal']);
$this->actingAs($user)->postJson('/api/autopodbor/manual-study', [
'site_url' => 'https://okna-komfort-kzn.ru/contacts', 'region_code' => 16,
])->assertCreated()->assertJsonPath('data.kind', 'study');
DB::statement('SET app.current_tenant_id = '.$tenant->id);
expect(AutopodborCompetitor::where('tenant_id', $tenant->id)->where('origin', 'manual')->exists())->toBeTrue();
});
it('manual-study без названия и без сайта → 422', function () {
$tenant = Tenant::factory()->create(['balance_rub' => '100000.00']);
$user = User::factory()->create(['tenant_id' => $tenant->id]);
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '1', 'type' => 'decimal']);
$this->actingAs($user)->postJson('/api/autopodbor/manual-study', ['region_code' => 16])
->assertStatus(422);
});
it('sources/manual добавляет источник изученному конкуренту', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
DB::statement('SET app.current_tenant_id = '.$tenant->id);
$run = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'params' => []]);
$comp = AutopodborCompetitor::create(['tenant_id' => $tenant->id, 'search_run_id' => $run->id, 'study_run_id' => $run->id, 'studied_at' => now(), 'name' => 'Окна Комфорт', 'origin' => 'auto', 'dedup_key' => 'okna']);
$this->actingAs($user)->postJson('/api/autopodbor/sources/manual', [
'competitor_id' => $comp->id, 'raw' => 'okna-komfort.ru',
])->assertCreated()
->assertJsonPath('data.signal_type', 'site')
->assertJsonPath('data.box', 'field'); // ручной источник — сразу «в работу», не в предложения
expect(AutopodborSource::where('competitor_id', $comp->id)->where('identifier', 'okna-komfort.ru')->first())
->box->toBe('field');
});
// ——— #4: защита от дублей источника ———
function makeStudiedCompetitors(int $tenantId): array
{
$run = AutopodborRun::create(['tenant_id' => $tenantId, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'params' => []]);
$a = AutopodborCompetitor::create(['tenant_id' => $tenantId, 'search_run_id' => $run->id, 'study_run_id' => $run->id, 'studied_at' => now(), 'name' => 'Конкурент А', 'origin' => 'auto', 'dedup_key' => 'a']);
$b = AutopodborCompetitor::create(['tenant_id' => $tenantId, 'search_run_id' => $run->id, 'study_run_id' => $run->id, 'studied_at' => now(), 'name' => 'Конкурент Б', 'origin' => 'auto', 'dedup_key' => 'b']);
return [$run, $a, $b];
}
it('sources/manual — такой же источник у другого конкурента → 409 duplicate_source', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
DB::statement('SET app.current_tenant_id = '.$tenant->id);
[$run, $a, $b] = makeStudiedCompetitors($tenant->id);
AutopodborSource::create(['tenant_id' => $tenant->id, 'competitor_id' => $a->id, 'study_run_id' => $run->id, 'signal_type' => 'site', 'identifier' => 'dubel-test.ru', 'dedup_key' => 's:dubel-test.ru', 'box' => 'field']);
$this->actingAs($user)->postJson('/api/autopodbor/sources/manual', [
'competitor_id' => $b->id, 'raw' => 'dubel-test.ru',
])->assertStatus(409)
->assertJsonPath('error', 'duplicate_source')
->assertJsonPath('duplicate.competitor_name', 'Конкурент А')
->assertJsonPath('duplicate.has_project', false);
});
it('sources/manual — по источнику уже есть проект → 409 с has_project', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
DB::statement('SET app.current_tenant_id = '.$tenant->id);
[$run, $a, $b] = makeStudiedCompetitors($tenant->id);
$project = Project::factory()->asSiteSignal('dubel-test.ru')->create(['tenant_id' => $tenant->id]);
$this->actingAs($user)->postJson('/api/autopodbor/sources/manual', [
'competitor_id' => $b->id, 'raw' => 'dubel-test.ru',
])->assertStatus(409)
->assertJsonPath('error', 'duplicate_source')
->assertJsonPath('duplicate.has_project', true)
->assertJsonPath('duplicate.project_id', $project->id);
});
it('sources/manual — force+absorb переносит источник и привязывает существующий проект, старый источник удалён', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
DB::statement('SET app.current_tenant_id = '.$tenant->id);
[$run, $a, $b] = makeStudiedCompetitors($tenant->id);
$srcA = AutopodborSource::create(['tenant_id' => $tenant->id, 'competitor_id' => $a->id, 'study_run_id' => $run->id, 'signal_type' => 'site', 'identifier' => 'dubel-test.ru', 'dedup_key' => 's:dubel-test.ru', 'box' => 'field']);
$project = Project::factory()->asSiteSignal('dubel-test.ru')->create(['tenant_id' => $tenant->id]);
$this->actingAs($user)->postJson('/api/autopodbor/sources/manual', [
'competitor_id' => $b->id, 'raw' => 'dubel-test.ru', 'force' => true, 'absorb_source_id' => $srcA->id,
])->assertCreated()->assertJsonPath('data.box', 'field');
$newSrc = AutopodborSource::where('competitor_id', $b->id)->where('identifier', 'dubel-test.ru')->first();
expect($newSrc->created_project_id)->toBe($project->id); // проект привязан к новому источнику
expect(AutopodborSource::find($srcA->id))->toBeNull(); // старый источник-дубль убран
expect($project->fresh()->name)->toBe('Конкурент Б'); // имя проекта обновлено на нового конкурента
});
it('sources/manual телефоном создаёт call-источник', function () {
$tenant = Tenant::factory()->create();
$user = User::factory()->create(['tenant_id' => $tenant->id]);
DB::statement('SET app.current_tenant_id = '.$tenant->id);
$run = AutopodborRun::create(['tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'done', 'region_code' => 16, 'params' => []]);
$comp = AutopodborCompetitor::create(['tenant_id' => $tenant->id, 'search_run_id' => $run->id, 'study_run_id' => $run->id, 'studied_at' => now(), 'name' => 'Окна', 'origin' => 'auto', 'dedup_key' => 'okna2']);
$this->actingAs($user)->postJson('/api/autopodbor/sources/manual', [
'competitor_id' => $comp->id, 'raw' => '+7 (843) 200-11-22',
])->assertCreated()->assertJsonPath('data.signal_type', 'call');
});