108 lines
4.0 KiB
PHP
108 lines
4.0 KiB
PHP
|
|
<?php
|
|||
|
|
|
|||
|
|
declare(strict_types=1);
|
|||
|
|
|
|||
|
|
use App\Models\Project;
|
|||
|
|
use App\Models\Tenant;
|
|||
|
|
use App\Models\User;
|
|||
|
|
use Database\Seeders\PricingTierSeeder;
|
|||
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|||
|
|
|
|||
|
|
uses(DatabaseTransactions::class);
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* GET /api/billing/balance-status — статус баланса для UI префлайта (Billing v2
|
|||
|
|
* Spec C Task 1.10): питает баннер заморозки (BalanceFrozenBanner) и индикатор
|
|||
|
|
* ёмкости (BalanceCapacityIndicator).
|
|||
|
|
*
|
|||
|
|
* PricingTierSeeder: ступень 1 — 100 лидов × 500₽ (см. BillingOverviewControllerTest).
|
|||
|
|
*/
|
|||
|
|
beforeEach(function () {
|
|||
|
|
$this->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);
|
|||
|
|
});
|