109 lines
4.9 KiB
PHP
109 lines
4.9 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
use App\Exceptions\Autopodbor\BatchBudgetException;
|
|||
|
|
use App\Jobs\Autopodbor\RunAutopodborStudyJob;
|
|||
|
|
use App\Models\AutopodborCompetitor;
|
|||
|
|
use App\Models\AutopodborRun;
|
|||
|
|
use App\Models\PricingTier;
|
|||
|
|
use App\Models\Project;
|
|||
|
|
use App\Models\SystemSetting;
|
|||
|
|
use App\Models\Tenant;
|
|||
|
|
use App\Services\Autopodbor\AutopodborRunService;
|
|||
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|||
|
|
use Illuminate\Support\Facades\DB;
|
|||
|
|
use Illuminate\Support\Facades\Queue;
|
|||
|
|
use Illuminate\Support\Str;
|
|||
|
|
use Tests\Concerns\SharesSupplierPdo;
|
|||
|
|
|
|||
|
|
uses(DatabaseTransactions::class, SharesSupplierPdo::class);
|
|||
|
|
|
|||
|
|
function batchCompetitor(int $tenantId, string $name): AutopodborCompetitor
|
|||
|
|
{
|
|||
|
|
return AutopodborCompetitor::create([
|
|||
|
|
'tenant_id' => $tenantId,
|
|||
|
|
'name' => $name,
|
|||
|
|
'origin' => 'manual',
|
|||
|
|
'dedup_key' => Str::slug($name).'-'.Str::random(6),
|
|||
|
|
'box' => 'field',
|
|||
|
|
]);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
it('ставит пакет: N queued-прогонов с общим batch_id, распорядитель стартует ровно первого', function () {
|
|||
|
|
Queue::fake();
|
|||
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '100000.00']);
|
|||
|
|
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
|||
|
|
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '300', 'type' => 'decimal']);
|
|||
|
|
|
|||
|
|
$c1 = batchCompetitor($tenant->id, 'Окна А');
|
|||
|
|
$c2 = batchCompetitor($tenant->id, 'Окна Б');
|
|||
|
|
$c3 = batchCompetitor($tenant->id, 'Окна В');
|
|||
|
|
|
|||
|
|
$result = app(AutopodborRunService::class)->startStudyBatch($tenant->id, [$c1->id, $c2->id, $c3->id]);
|
|||
|
|
|
|||
|
|
expect($result->queued)->toBe(3)
|
|||
|
|
->and($result->batch_id)->not->toBeNull();
|
|||
|
|
|
|||
|
|
$runs = AutopodborRun::where('batch_id', $result->batch_id)->get();
|
|||
|
|
expect($runs)->toHaveCount(3)
|
|||
|
|
->and($runs->pluck('status')->unique()->values()->all())->toBe(['queued'])
|
|||
|
|
->and($runs->pluck('kind')->unique()->values()->all())->toBe(['study']);
|
|||
|
|
|
|||
|
|
// Джобы не диспатчатся напрямую из сервиса — распорядитель стартует РОВНО первого.
|
|||
|
|
Queue::assertPushed(RunAutopodborStudyJob::class, 1);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('нормализация: дубли, чужие конкуренты и уже-в-очереди отсеиваются', function () {
|
|||
|
|
Queue::fake();
|
|||
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '100000.00']);
|
|||
|
|
$other = Tenant::factory()->create(['balance_rub' => '100000.00']);
|
|||
|
|
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
|||
|
|
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '300', 'type' => 'decimal']);
|
|||
|
|
|
|||
|
|
$c1 = batchCompetitor($tenant->id, 'Окна А');
|
|||
|
|
$c2 = batchCompetitor($tenant->id, 'Окна Б');
|
|||
|
|
$foreign = batchCompetitor($other->id, 'Чужой');
|
|||
|
|
$busy = batchCompetitor($tenant->id, 'Занятый');
|
|||
|
|
// У «занятого» уже есть queued study-прогон → должен быть отсеян.
|
|||
|
|
AutopodborRun::create([
|
|||
|
|
'tenant_id' => $tenant->id, 'kind' => 'study', 'status' => 'queued',
|
|||
|
|
'competitor_id' => $busy->id, 'region_code' => 1, 'params' => [],
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$result = app(AutopodborRunService::class)->startStudyBatch(
|
|||
|
|
$tenant->id,
|
|||
|
|
[$c1->id, $c1->id, $c2->id, $foreign->id, $busy->id],
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
expect($result->queued)->toBe(2);
|
|||
|
|
|
|||
|
|
$runs = AutopodborRun::where('batch_id', $result->batch_id)->get();
|
|||
|
|
expect($runs)->toHaveCount(2)
|
|||
|
|
->and($runs->pluck('competitor_id')->sort()->values()->all())
|
|||
|
|
->toBe(collect([$c1->id, $c2->id])->sort()->values()->all());
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
it('нет баланса на весь пакет → BatchBudgetException, ни одного прогона не создано', function () {
|
|||
|
|
Queue::fake();
|
|||
|
|
$tenant = Tenant::factory()->create(['balance_rub' => '100.00', 'delivered_in_month' => 0]);
|
|||
|
|
DB::statement('SET app.current_tenant_id = '.$tenant->id);
|
|||
|
|
SystemSetting::updateOrCreate(['key' => 'autopodbor_price_study_rub'], ['value' => '300', 'type' => 'decimal']);
|
|||
|
|
|
|||
|
|
// Активный проект-обязательство + сетка тарифов — гейт учитывает резерв проектов.
|
|||
|
|
Project::factory()->create(['tenant_id' => $tenant->id, 'is_active' => true, 'daily_limit_target' => 10]);
|
|||
|
|
PricingTier::factory()->create([
|
|||
|
|
'tier_no' => 1, 'leads_in_tier' => null, 'price_per_lead_kopecks' => 3000,
|
|||
|
|
'is_active' => true, 'effective_from' => now()->toDateString(),
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
$c1 = batchCompetitor($tenant->id, 'Окна А');
|
|||
|
|
$c2 = batchCompetitor($tenant->id, 'Окна Б');
|
|||
|
|
|
|||
|
|
expect(fn () => app(AutopodborRunService::class)->startStudyBatch($tenant->id, [$c1->id, $c2->id]))
|
|||
|
|
->toThrow(BatchBudgetException::class);
|
|||
|
|
|
|||
|
|
expect(AutopodborRun::where('tenant_id', $tenant->id)->where('kind', 'study')->count())->toBe(0);
|
|||
|
|
Queue::assertNotPushed(RunAutopodborStudyJob::class);
|
|||
|
|
});
|