Files
coolify/tests/Feature/TrafficAnalytics/ApplicationTrafficOverviewTest.php
T
Aditya Tripathi 19752da6af feat(analytics): lazy-load traffic pages and batch Sentinel calls
- Mark Analytics, Dashboard\TrafficAnalytics and Project\Application\Analytics
  Livewire components #[Lazy] with skeleton placeholder() views so pages render
  instantly while Sentinel data loads in a deferred request
- Add SentinelTrafficClient::prefetchServerWide() to warm all server-wide
  endpoints in a single batched docker exec instead of ~14-15 serial SSH
  round-trips, and refactor overview/paths/breakdown URL building into shared
  helpers
- Cap per-app leaderboard overviews fetched via MAX_LEADERBOARD_APPS to bound
  requests on servers with many apps
- Add hasRequestSeries() to detect empty request charts and render a no-data
  state instead of a blank chart
- Gate ServerSetting's automatic Sentinel restart on is_sentinel_enabled so
  tuning geoip/traffic settings no longer silently enables Sentinel
- Add reusable skeleton UI components (skeleton, skeleton/table, skeleton/tiles)
  for the new placeholder views
2026-08-22 11:13:49 +00:00

122 lines
4.2 KiB
PHP

<?php
use App\Livewire\Project\Application\TrafficOverview;
use App\Models\Application;
use App\Models\Environment;
use App\Models\PrivateKey;
use App\Models\Project;
use App\Models\Server;
use App\Models\StandaloneDocker;
use App\Models\Team;
use App\Models\User;
use App\Services\SentinelTrafficClient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Livewire\Livewire;
uses(RefreshDatabase::class);
class FakeAppOverviewTrafficClient extends SentinelTrafficClient
{
public array $responses = [];
protected function raw(string $url): string
{
foreach ($this->responses as $needle => $response) {
if (str_contains($url, $needle)) {
return $response;
}
}
return '{}';
}
}
beforeEach(function () {
// Servers are memoized via once()/cache; clear both so DB-id reuse across tests doesn't bleed a stale enabled server.
Cache::flush();
Server::flushIdentityMap();
$this->team = Team::factory()->create();
$this->user = User::factory()->create();
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
$this->actingAs($this->user);
session(['currentTeam' => $this->team]);
$this->privateKey = PrivateKey::factory()->create(['team_id' => $this->team->id]);
});
function makeAppOnServer(bool $analyticsEnabled): Application
{
$server = Server::factory()->create([
'team_id' => test()->team->id,
'private_key_id' => test()->privateKey->id,
]);
$server->settings->is_traffic_analytics_enabled = $analyticsEnabled;
$server->settings->save();
$project = Project::factory()->create(['team_id' => test()->team->id]);
$environment = Environment::factory()->create(['project_id' => $project->id]);
$destination = StandaloneDocker::where('server_id', $server->id)->first()
?? StandaloneDocker::factory()->create(['server_id' => $server->id, 'network' => 'coolify-test']);
return Application::factory()->create([
'name' => 'Widget App',
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => StandaloneDocker::class,
]);
}
it('shows last-24h KPIs and a link to full analytics when enabled with data', function () {
$application = makeAppOnServer(true);
app()->bind(SentinelTrafficClient::class, function ($app, $params) {
$client = new FakeAppOverviewTrafficClient($params['server']);
$client->responses = [
'/traffic/overview' => json_encode([
'requests' => 4200,
'bytes_in' => 5000,
'bytes_out' => 25000,
'status' => ['s2xx' => 4000, 's3xx' => 100, 's4xx' => 80, 's5xx' => 20],
'latency' => ['p50' => 12.5, 'p95' => 45.2, 'p99' => 90.1],
'unique_visitors' => 1234,
]),
];
return $client;
});
$component = loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application]));
$component->assertOk()
->assertSet('enabled', true)
->assertSee('Traffic (last 24h)')
->assertSee('4,200')
->assertSee('1,234')
->assertSee('View full analytics');
});
it('shows the muted no-data note when enabled but no traffic recorded', function () {
$application = makeAppOnServer(true);
app()->bind(SentinelTrafficClient::class, function ($app, $params) {
$client = new FakeAppOverviewTrafficClient($params['server']);
$client->responses = ['/traffic/overview' => json_encode(['requests' => 0])];
return $client;
});
loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application]))
->assertOk()
->assertSee('No traffic recorded in the last 24h yet');
});
it('shows the enable nudge when analytics is disabled on an eligible server', function () {
$application = makeAppOnServer(false);
loadLazy(Livewire::test(TrafficOverview::class, ['application' => $application]))
->assertOk()
->assertSet('enabled', false)
->assertSee('Traffic analytics')
->assertSee('Server settings')
->assertDontSee('Traffic (last 24h)');
});