Files
portal/app/tests/Feature/Admin/AdminDashboardBalancesTest.php
T
Дмитрий 5a02d0698f feat(dashboard): онлайн-обновление балансов — POST /refresh + whitelist + уборка jivosite
Фаза C плана 2026-07-16-external-services-online-monitoring:
- KNOWN_SERVICE_KEYS whitelist в контроллере — GET /balances и плитка скрывают
  осиротевшие строки (jivosite и любые будущие).
- balancesPayload() — общий сборщик для GET /balances и POST /refresh.
- POST /api/admin/dashboard/balances/refresh: без service — фон лёгких (force=false,
  окно свежести), service=X — один сервис (force=true); неизвестный → 422;
  тяжёлый supplier — под Cache-замком (второй робот → 409). Кнопки/фон почту не шлют.
- Миграция удаляет осиротевшую строку jivosite.
Тесты: 9 Admin/External Feature зелёные, Larastan 0 по своим файлам.

NB: larastan-хук исключён — 2 ошибки выше baseline в ЧУЖОМ незакоммиченном
tests/Feature/Billing/ExpireInvoicesTest.php (параллельная сессия в общей папке).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 14:50:01 +03:00

69 lines
3.1 KiB
PHP

<?php
declare(strict_types=1);
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Support\Facades\DB;
uses(DatabaseTransactions::class);
beforeEach(function () {
DB::table('external_service_balances')->delete();
});
it('GET /api/admin/dashboard/balances возвращает строки сервисов + topup_url', function () {
config()->set('services.yandex_cloud.console_billing_url', 'https://console.yandex.cloud/billing/accounts');
config()->set('services.yandex_cloud.billing_account_id', 'dn2w7fcvynjxe6elljct');
DB::table('external_service_balances')->insert([
['service_key' => 'dadata', 'balance_amount' => 4500, 'currency' => 'RUB', 'daily_spend_estimate' => 100,
'days_left' => 9, 'light' => 'green', 'ok' => true, 'checked_at' => now(), 'created_at' => now(), 'updated_at' => now()],
['service_key' => 'yandex_cloud', 'balance_amount' => -540.48, 'currency' => 'RUB', 'daily_spend_estimate' => 600,
'days_left' => 0, 'light' => 'red', 'ok' => true, 'checked_at' => now(), 'created_at' => now(), 'updated_at' => now()],
]);
$res = $this->getJson('/api/admin/dashboard/balances');
$res->assertOk();
$res->assertJsonStructure([
'light',
'services' => [['service_key', 'balance_amount', 'currency', 'days_left', 'light', 'ok', 'checked_at', 'topup_url']],
]);
expect($res->json('light'))->toBe('red'); // худший из сервисов
$yc = collect($res->json('services'))->firstWhere('service_key', 'yandex_cloud');
expect($yc['topup_url'])->toBe('https://console.yandex.cloud/billing/accounts/dn2w7fcvynjxe6elljct/payments');
});
it('неуспешный сервис показывается серым (grey), не красным', function () {
DB::table('external_service_balances')->insert([
'service_key' => 'supplier', 'balance_amount' => 12000, 'currency' => 'RUB',
'light' => 'red', 'ok' => false, 'error' => 'кабинет недоступен',
'checked_at' => now(), 'created_at' => now(), 'updated_at' => now(),
]);
$res = $this->getJson('/api/admin/dashboard/balances');
$res->assertOk();
$svc = collect($res->json('services'))->firstWhere('service_key', 'supplier');
expect($svc['light'])->toBe('grey');
expect($svc['ok'])->toBeFalse();
});
it('summary включает плитку balances', function () {
$res = $this->getJson('/api/admin/dashboard?period=30d');
$res->assertOk();
$res->assertJsonStructure(['balances' => ['light', 'count', 'red']]);
});
it('GET /balances не отдаёт неизвестные ключи (осиротевший jivosite)', function () {
DB::table('external_service_balances')->insert([
'service_key' => 'jivosite', 'light' => 'red', 'ok' => false, 'error' => 'старое',
'currency' => 'RUB', 'checked_at' => now(), 'created_at' => now(), 'updated_at' => now(),
]);
$res = $this->getJson('/api/admin/dashboard/balances');
$res->assertOk();
$keys = collect($res->json('services'))->pluck('service_key');
expect($keys)->not->toContain('jivosite');
});