1, 'leads_in_tier' => null, 'price_per_lead_kopecks' => 10000, 'is_active' => true, 'effective_from' => now()->toDateString(), ]); }); /** * Создаёт tenant + user + run + competitor + 2 источника типа site. * Возвращает [$tenant, $user, $s1, $s2]. */ function setupGateScene(array $tenantAttrs = []): array { $tenant = Tenant::factory()->create($tenantAttrs); $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' => 'search', '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' => 'Gate-конкурент', 'box' => 'field', 'site_url' => 'gatetest1.ru', 'dedup_key' => 'site:gatetest1.ru', ]); $s1 = AutopodborSource::create([ 'tenant_id' => $tenant->id, 'competitor_id' => $comp->id, 'study_run_id' => $run->id, 'signal_type' => 'site', 'identifier' => 'gatetest1.ru', 'box' => 'field', 'dedup_key' => 'site:gatetest1.ru', ]); $s2 = AutopodborSource::create([ 'tenant_id' => $tenant->id, 'competitor_id' => $comp->id, 'study_run_id' => $run->id, 'signal_type' => 'site', 'identifier' => 'gatetest2.ru', 'box' => 'field', 'dedup_key' => 'site:gatetest2.ru', ]); return [$tenant, $user, $s1, $s2]; } it('creates all held with summary when balance insufficient (201)', function () { Queue::fake(); // Баланс на 1 лид, запрашиваем 5 лидов × 2 источника — оба удержаны [$tenant, $user, $s1, $s2] = setupGateScene(['balance_rub' => '100.00', 'delivered_in_month' => 0]); // Реквизиты чтобы пройти гейт реквизитов (tenant новый, проектов 0, нужны реквизиты) TenantRequisites::create([ 'tenant_id' => $tenant->id, 'subject_type' => 'individual', 'contact_name' => 'Тест Тестов', 'contact_phone' => '+79991234567', ]); $resp = $this->actingAs($user)->postJson('/api/autopodbor/projects', [ 'source_ids' => [$s1->id, $s2->id], 'daily_limit_target' => 5, 'delivery_days_mask' => 127, 'launch' => true, ]); $resp->assertCreated(); $resp->assertJsonPath('launch.launched', 0); $resp->assertJsonPath('launch.deferred', 2); // balance payload должен быть от первого удержанного expect($resp->json('launch.balance'))->not->toBeNull(); expect($resp->json('launch.balance.topup_rub'))->not->toBeNull(); // оба проекта созданы (не заблокировано), но не запущены expect($resp->json('data'))->toHaveCount(2); expect(collect($resp->json('data'))->filter(fn ($p) => $p['is_active'])->count())->toBe(0); }); it('blocks first project without requisites (422 requisites_required)', function () { Queue::fake(); // Tenant без реквизитов, проектов 0 [$tenant, $user, $s1, $s2] = setupGateScene(['balance_rub' => '10000.00', 'delivered_in_month' => 0]); // Реквизиты НЕ создаём намеренно $resp = $this->actingAs($user)->postJson('/api/autopodbor/projects', [ 'source_ids' => [$s1->id, $s2->id], 'daily_limit_target' => 5, 'delivery_days_mask' => 127, 'launch' => true, ]); $resp->assertStatus(422); $resp->assertJsonPath('error', 'requisites_required'); });