seed(PricingTierSeeder::class); $this->tenant = Tenant::factory()->create([ 'balance_rub' => '5000.00', 'delivered_in_month' => 0, 'frozen_by_balance_at' => null, ]); $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); $this->actingAs($this->user); }); test('GET /api/billing/balance-status: структура ответа', function () { $this->getJson('/api/billing/balance-status') ->assertOk() ->assertJsonStructure([ 'frozen_by_balance_at', 'balance_rub', 'capacity_leads', 'required_leads_per_day', 'deficit_leads', 'deficit_rub', ]); }); test('balance-status: хватает баланса — deficit=0, не заморожен', function () { // 5000₽ при ступени 1 (500₽/лид) = 10 лидов ёмкости. Проект лимит 5 — впритык. Project::factory()->create([ 'tenant_id' => $this->tenant->id, 'is_active' => true, 'daily_limit_target' => 5, ]); $resp = $this->getJson('/api/billing/balance-status')->assertOk(); expect($resp->json('balance_rub'))->toBe('5000.00'); expect($resp->json('capacity_leads'))->toBe(10); expect($resp->json('required_leads_per_day'))->toBe(5); expect($resp->json('deficit_leads'))->toBe(0); expect($resp->json('deficit_rub'))->toBe('0.00'); expect($resp->json('frozen_by_balance_at'))->toBeNull(); }); test('balance-status: не хватает — deficit_leads + deficit_rub точные', function () { // Ёмкость = 10 лидов. Проект лимит 25 → нужно 25, дефицит 15 лидов. // minBalanceForLeads(25) = 25 × 500₽ = 12500₽ → deficit_rub = 12500 − 5000 = 7500.00. Project::factory()->create([ 'tenant_id' => $this->tenant->id, 'is_active' => true, 'daily_limit_target' => 25, ]); $resp = $this->getJson('/api/billing/balance-status')->assertOk(); expect($resp->json('capacity_leads'))->toBe(10); expect($resp->json('required_leads_per_day'))->toBe(25); expect($resp->json('deficit_leads'))->toBe(15); expect($resp->json('deficit_rub'))->toBe('7500.00'); }); test('balance-status: required исключает inactive и preflight_blocked проекты', function () { Project::factory()->create([ 'tenant_id' => $this->tenant->id, 'is_active' => true, 'daily_limit_target' => 5, ]); Project::factory()->create([ 'tenant_id' => $this->tenant->id, 'is_active' => false, 'daily_limit_target' => 100, ]); Project::factory()->create([ 'tenant_id' => $this->tenant->id, 'is_active' => true, 'daily_limit_target' => 100, 'preflight_blocked_at' => now(), ]); $resp = $this->getJson('/api/billing/balance-status')->assertOk(); expect($resp->json('required_leads_per_day'))->toBe(5); }); test('balance-status: возвращает frozen_by_balance_at когда установлен', function () { $this->tenant->update(['frozen_by_balance_at' => now()]); $resp = $this->getJson('/api/billing/balance-status')->assertOk(); expect($resp->json('frozen_by_balance_at'))->not->toBeNull(); }); test('GET /api/billing/balance-status без auth: 401', function () { auth()->logout(); $this->getJson('/api/billing/balance-status')->assertStatus(401); });