Files
coolify/tests/Feature/TrafficAnalytics/LiveRefreshTest.php
T
Aditya Tripathi a74eaa49a6 feat(traffic): replace world map with interactive globe visualization
Swap the static world-map partial for a cobe-based 3D globe
(traffic-globe.js) driven by per-country marker data. Extract shared
sparkline/device/geo derivations into BuildsTrafficChartPayload and
reuse it across global, dashboard, and per-application analytics
components. Add device-donut, hosts-list, and sparkline partials, and
extend breakdown dimensions with protocol/cache/status. Default live
refresh to off until manually armed.
2026-08-12 09:31:05 +00:00

102 lines
3.0 KiB
PHP

<?php
use App\Livewire\Analytics;
use App\Models\PrivateKey;
use App\Models\Server;
use App\Models\Team;
use App\Models\User;
use App\Services\SentinelTrafficClient;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Livewire\Livewire;
uses(RefreshDatabase::class);
class FakeLiveTrafficClient extends SentinelTrafficClient
{
protected function raw(string $url): string
{
if (str_contains($url, '/traffic/overview')) {
return json_encode([
'requests' => 1000,
'bytes_in' => 5000,
'bytes_out' => 25000,
'status' => ['s2xx' => 900, 's3xx' => 50, 's4xx' => 40, 's5xx' => 10],
'latency' => ['p50' => 12.5, 'p95' => 45.2, 'p99' => 90.1],
'unique_visitors' => 320,
]);
}
return '[]';
}
}
function bootLiveServer(): Server
{
Server::flushIdentityMap();
$team = Team::factory()->create();
$user = User::factory()->create();
$team->members()->attach($user->id, ['role' => 'owner']);
test()->actingAs($user);
session(['currentTeam' => $team]);
$privateKey = PrivateKey::factory()->create(['team_id' => $team->id]);
$server = Server::factory()->create([
'team_id' => $team->id,
'private_key_id' => $privateKey->id,
]);
$server->settings->is_traffic_analytics_enabled = true;
$server->settings->save();
app()->bind(SentinelTrafficClient::class, fn () => new FakeLiveTrafficClient($server));
return $server;
}
it('is paused by default and does not poll until Live is turned on', function () {
$server = bootLiveServer();
Livewire::test(Analytics::class)
->assertOk()
->assertSet('live', false)
->assertDontSeeHtml('wire:poll.60s')
->assertSee('Paused');
});
it('starts polling when live is toggled on at the 24h range', function () {
$server = bootLiveServer();
Livewire::test(Analytics::class)
->assertDontSeeHtml('wire:poll.60s')
->call('toggleLive')
->assertSet('live', true)
->assertSeeHtml('wire:poll.60s')
->assertSee('Live');
});
it('disables realtime polling for the 7d and 30d ranges', function () {
$server = bootLiveServer();
$component = Livewire::test(Analytics::class)
->call('setRange', '7d')
->assertDontSeeHtml('wire:poll.60s');
expect($component->instance()->isLivePollable())->toBeFalse();
// toggleLive is a no-op outside the 24h range.
$component->call('toggleLive')->assertDontSeeHtml('wire:poll.60s');
$component->call('setRange', '30d')->assertDontSeeHtml('wire:poll.60s');
});
it('re-arms polling when returning to the 24h range after live was turned on', function () {
$server = bootLiveServer();
Livewire::test(Analytics::class)
->call('toggleLive') // arm live at 24h
->assertSeeHtml('wire:poll.60s')
->call('setRange', '7d')
->assertDontSeeHtml('wire:poll.60s')
->call('setRange', '24h')
->assertSeeHtml('wire:poll.60s');
});