Files
panel/tests/Feature/Controllers/Admin/OverviewControllerTest.php
Eric Wang 8e1729ec6f feat: add admin overview dashboard with metrics endpoint
Adds /api/admin/overview returning cached aggregate metrics for servers,
nodes, capacity, addresses, backups, and ISOs, plus a refreshed admin
dashboard UI consuming it via SWR.

Refactored from #153:
- Split OverviewService into focused per-section methods over a single
  orchestrator, dropping the monolithic freshMetrics()
- Dropped the recent-activity panel and supporting index migration —
  ActivityLog has no writers in the codebase, so the panel always
  rendered empty
- Removed the 999.99 percent cap; overallocation is a legitimate state
  and the frontend already clamps bar width
- Added capacity byte-conversion and restoring-bucket test cases

Co-Authored-By: Ajdin <AjdinDev@users.noreply.github.com>
2026-05-28 21:22:40 -04:00

103 lines
3.4 KiB
PHP

<?php
use Convoy\Enums\Server\Status;
use Convoy\Models\Address;
use Convoy\Models\AddressPool;
use Convoy\Models\Backup;
use Convoy\Models\ISO;
use Convoy\Models\Location;
use Convoy\Models\Node;
use Convoy\Models\Server;
use Convoy\Models\User;
it('returns overview metrics for admins', function () {
$admin = User::factory()->create([
'root_admin' => true,
]);
$location = Location::factory()->create();
$node = Node::factory()->for($location)->create([
'memory' => 64 * 1024 * 1024 * 1024,
'disk' => 128 * 1024 * 1024 * 1024,
]);
$server = Server::factory()->for($node)->for($admin)->create([
'status' => null,
'memory' => 8 * 1024 * 1024 * 1024,
'disk' => 32 * 1024 * 1024 * 1024,
]);
Server::factory()->for($node)->for($admin)->create([
'status' => Status::INSTALL_FAILED->value,
'memory' => 4 * 1024 * 1024 * 1024,
'disk' => 16 * 1024 * 1024 * 1024,
]);
$pool = AddressPool::factory()->create();
Address::factory()->create([
'address_pool_id' => $pool->id,
'server_id' => $server->id,
]);
Address::factory()->create([
'address_pool_id' => $pool->id,
'server_id' => null,
]);
Backup::factory()->for($server)->create([
'is_successful' => true,
'completed_at' => now(),
]);
Backup::factory()->for($server)->create([
'is_successful' => false,
'completed_at' => now(),
]);
ISO::factory()->for($node)->create([
'is_successful' => false,
]);
$response = $this->actingAs($admin)->getJson('/api/admin/overview');
$response->assertOk()
->assertJsonPath('data.summary.servers', 2)
->assertJsonPath('data.summary.nodes', 1)
->assertJsonPath('data.summary.failed_servers', 1)
->assertJsonPath('data.servers.ready', 1)
->assertJsonPath('data.servers.failed', 1)
->assertJsonPath('data.addresses.total', 2)
->assertJsonPath('data.addresses.assigned', 1)
->assertJsonPath('data.backups.total', 2)
->assertJsonPath('data.backups.successful', 1)
->assertJsonPath('data.backups.failed', 1)
->assertJsonPath('data.isos.pending', 1)
->assertJsonPath('data.nodes.0.servers', 2)
->assertJsonPath('data.capacity.memory.allocated', 12 * 1024 * 1024 * 1024)
->assertJsonPath('data.capacity.disk.allocated', 48 * 1024 * 1024 * 1024)
->assertJsonPath('data.nodes.0.memory.allocated', 12 * 1024 * 1024 * 1024)
->assertJsonPath('data.nodes.0.disk.allocated', 48 * 1024 * 1024 * 1024);
});
it('buckets restoring servers across both restore statuses', function () {
$admin = User::factory()->create(['root_admin' => true]);
$location = Location::factory()->create();
$node = Node::factory()->for($location)->create();
Server::factory()->for($node)->for($admin)->create([
'status' => Status::RESTORING_BACKUP->value,
]);
Server::factory()->for($node)->for($admin)->create([
'status' => Status::RESTORING_SNAPSHOT->value,
]);
$this->actingAs($admin)->getJson('/api/admin/overview')
->assertOk()
->assertJsonPath('data.servers.restoring', 2);
});
it('requires an admin user', function () {
$user = User::factory()->create([
'root_admin' => false,
]);
$this->actingAs($user)->getJson('/api/admin/overview')
->assertForbidden();
});