2026-05-11 18:52:51 +03:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
use App\Jobs\SyncSupplierProjectJob;
|
|
|
|
|
use App\Models\Project;
|
|
|
|
|
use App\Models\SupplierProject;
|
|
|
|
|
use App\Models\Tenant;
|
2026-05-19 12:33:37 +03:00
|
|
|
use App\Services\Supplier\Channel\SupplierProjectChannel;
|
2026-07-14 11:26:03 +03:00
|
|
|
use App\Services\Supplier\SupplierProjectGrouping;
|
2026-05-11 18:52:51 +03:00
|
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
2026-07-14 11:26:03 +03:00
|
|
|
use Illuminate\Support\Facades\DB;
|
2026-05-21 15:02:40 +03:00
|
|
|
use Tests\Concerns\SharesSupplierPdo;
|
2026-05-11 18:52:51 +03:00
|
|
|
|
|
|
|
|
// TestCase auto-bound via tests/Pest.php (->in('Feature')).
|
|
|
|
|
// DatabaseTransactions — per-test isolation.
|
2026-07-14 11:26:03 +03:00
|
|
|
// SharesSupplierPdo — SyncSupplierProjectJob пишет через pgsql_supplier (BYPASSRLS);
|
2026-05-21 15:02:40 +03:00
|
|
|
// без шаринга PDO записи джоба не видны default-connection ассертам под DatabaseTransactions.
|
|
|
|
|
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
2026-05-11 18:52:51 +03:00
|
|
|
|
|
|
|
|
/**
|
2026-05-19 12:33:37 +03:00
|
|
|
* Хелпер: разрешает SupplierProjectChannel из контейнера и вызывает Job.handle().
|
2026-05-11 18:52:51 +03:00
|
|
|
*/
|
|
|
|
|
function dispatchJobSync(SyncSupplierProjectJob $job): void
|
|
|
|
|
{
|
2026-05-19 12:33:37 +03:00
|
|
|
$job->handle(app(SupplierProjectChannel::class));
|
2026-05-11 18:52:51 +03:00
|
|
|
}
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
/**
|
|
|
|
|
* Прод-инцидент 14.07.2026 (см. SyncSupplierProjectJobTest в tests/Feature/Supplier):
|
|
|
|
|
* batch-режим больше НЕ ходит к поставщику при создании — кабинет всё равно отбивает
|
|
|
|
|
* каркас с limit=0 («Введите limit!»), а строки создаёт вечерний прогон 18:00 МСК.
|
|
|
|
|
*
|
|
|
|
|
* Раньше этот файл проверял разбор проекта на площадки ЧЕРЕЗ вызовы createProject.
|
|
|
|
|
* Сам разбор (site/call → B1+B2+B3, sms+keyword → B2+B3, sms без keyword → B3) никуда
|
|
|
|
|
* не делся — проверяем его напрямую у SupplierProjectGrouping, без похода к поставщику.
|
|
|
|
|
*/
|
|
|
|
|
it('site project resolves to B1+B2+B3', function () {
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
|
|
|
|
'signal_type' => 'site',
|
|
|
|
|
'signal_identifier' => 'okna.ru',
|
|
|
|
|
]);
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
expect(SupplierProjectGrouping::resolvePlatforms($project))->toBe(['B1', 'B2', 'B3']);
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
it('call project resolves to B1+B2+B3', function () {
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
|
|
|
|
'signal_type' => 'call',
|
|
|
|
|
'signal_identifier' => '79161234567',
|
|
|
|
|
]);
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
expect(SupplierProjectGrouping::resolvePlatforms($project))->toBe(['B1', 'B2', 'B3']);
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
it('sms project with keyword resolves to B2+B3 only (no B1)', function () {
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
|
|
|
|
'signal_type' => 'sms',
|
|
|
|
|
'sms_senders' => ['TINKOFF'],
|
|
|
|
|
'sms_keyword' => 'ипотека',
|
|
|
|
|
]);
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
expect(SupplierProjectGrouping::resolvePlatforms($project))->toBe(['B2', 'B3']);
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
it('sms project without keyword resolves to B3 only', function () {
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
|
|
|
|
'signal_type' => 'sms',
|
|
|
|
|
'sms_senders' => ['TINKOFF'],
|
|
|
|
|
'sms_keyword' => null,
|
|
|
|
|
]);
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
expect(SupplierProjectGrouping::resolvePlatforms($project))->toBe(['B3']);
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
it('batch create: supplier is never called and nothing lands in the manual queue', function () {
|
|
|
|
|
$tenant = Tenant::factory()->create();
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
2026-07-14 11:26:03 +03:00
|
|
|
'tenant_id' => $tenant->id,
|
2026-05-11 18:52:51 +03:00
|
|
|
'signal_type' => 'site',
|
2026-07-14 11:26:03 +03:00
|
|
|
'signal_identifier' => 'okna.ru',
|
2026-05-11 18:52:51 +03:00
|
|
|
]);
|
|
|
|
|
|
2026-05-19 12:33:37 +03:00
|
|
|
$this->mock(SupplierProjectChannel::class, function ($mock) {
|
2026-07-14 11:26:03 +03:00
|
|
|
$mock->shouldNotReceive('createProject');
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
dispatchJobSync(new SyncSupplierProjectJob($project->id));
|
|
|
|
|
|
|
|
|
|
$project->refresh();
|
|
|
|
|
expect($project->supplier_b1_project_id)->toBeNull();
|
|
|
|
|
expect(SupplierProject::where('unique_key', 'okna.ru')->count())->toBe(0);
|
|
|
|
|
expect(DB::table('supplier_manual_sync_queue')->where('project_id', $project->id)->count())->toBe(0);
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
it('idempotency: pre-existing supplier_project row is linked, channel still not called', function () {
|
2026-05-11 18:52:51 +03:00
|
|
|
$project = Project::factory()->create([
|
|
|
|
|
'signal_type' => 'site',
|
|
|
|
|
'signal_identifier' => 'x.ru',
|
|
|
|
|
]);
|
|
|
|
|
|
2026-07-14 11:26:03 +03:00
|
|
|
// B2 уже существует локально (например, создан вечерним прогоном).
|
2026-05-11 18:52:51 +03:00
|
|
|
$spB2 = SupplierProject::factory()->create([
|
|
|
|
|
'platform' => 'B2',
|
|
|
|
|
'signal_type' => 'site',
|
|
|
|
|
'unique_key' => 'x.ru',
|
|
|
|
|
'sync_status' => 'failed',
|
|
|
|
|
]);
|
|
|
|
|
|
2026-05-19 12:33:37 +03:00
|
|
|
$this->mock(SupplierProjectChannel::class, function ($mock) {
|
2026-07-14 11:26:03 +03:00
|
|
|
$mock->shouldNotReceive('createProject');
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
dispatchJobSync(new SyncSupplierProjectJob($project->id));
|
|
|
|
|
|
|
|
|
|
$project->refresh();
|
2026-07-14 11:26:03 +03:00
|
|
|
// Существующая строка привязана...
|
2026-05-19 12:33:37 +03:00
|
|
|
expect($project->supplier_b2_project_id)->toBe($spB2->id);
|
2026-07-14 11:26:03 +03:00
|
|
|
// ...а недостающие B1/B3 остаются на вечерний прогон.
|
|
|
|
|
expect($project->supplier_b1_project_id)->toBeNull();
|
|
|
|
|
expect($project->supplier_b3_project_id)->toBeNull();
|
2026-05-11 18:52:51 +03:00
|
|
|
});
|