Files
coolify/tests/Feature/TrafficAnalytics/ChangeProxySentinelRestartTest.php
Andras BacsaiandClaude Opus 5.5 510a2d838b feat(analytics): log Compose resources on Caddy and add service analytics
Traffic analytics now covers every resource on both proxies:

- Caddy: Compose applications and services get access-log labels. The
  log_append key is {uuid}-{service}, the same key Sentinel reads from
  Traefik router names.
- A resource owns the Sentinel key {uuid} and every key that starts
  with {uuid}-. Application pages and the traffic card now merge all
  keys, so Compose applications and previews show data on Traefik too.
- Global analytics: the leaderboard groups the keys of one resource
  into one row, lists services, and never names keys of other teams.
- New service analytics page (project.service.analytics) with the same
  view and authorization as the application page.
- Switching the proxy type restarts Sentinel when analytics is on, so
  it reads the new log path. Sentinel gets no traffic mount when the
  proxy has no analytics support.
- Caddy servers show a note that resources log only after a redeploy.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 01:00:55 +02:00

96 lines
3.7 KiB
PHP

<?php
use App\Actions\Proxy\StartProxy;
use App\Actions\Server\StartSentinel;
use App\Enums\ProxyTypes;
use App\Models\Server;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
uses(RefreshDatabase::class);
beforeEach(function () {
Queue::fake();
Server::flushIdentityMap();
$this->team = User::factory()->create()->teams()->first();
});
afterEach(fn () => Server::flushIdentityMap());
function proxySwitchServer(object $test, string $proxyType, bool $sentinelEnabled, bool $analyticsEnabled): Server
{
$server = Server::factory()->create(['team_id' => $test->team->id]);
$server->proxy->set('type', $proxyType);
$server->save();
$server->settings->is_sentinel_enabled = $sentinelEnabled;
$server->settings->is_traffic_analytics_enabled = $analyticsEnabled;
$server->settings->save();
Server::flushIdentityMap();
return $server->fresh();
}
it('restarts Sentinel after a proxy switch when Sentinel and traffic analytics are on', function (string $from, string $to) {
$server = proxySwitchServer($this, $from, sentinelEnabled: true, analyticsEnabled: true);
$server->changeProxy($to, async: true);
StartSentinel::assertPushed(1);
})->with([
'Traefik to Caddy' => [ProxyTypes::TRAEFIK->value, ProxyTypes::CADDY->value],
'Caddy to Traefik' => [ProxyTypes::CADDY->value, ProxyTypes::TRAEFIK->value],
'Traefik to none' => [ProxyTypes::TRAEFIK->value, ProxyTypes::NONE->value],
'Caddy to Nginx' => [ProxyTypes::CADDY->value, ProxyTypes::NGINX->value],
]);
it('restarts Sentinel when the Livewire switch selects the proxy synchronously', function () {
StartProxy::shouldRun();
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
$server->changeProxy(ProxyTypes::CADDY->value, async: false);
StartSentinel::assertPushed(1);
});
it('does not restart Sentinel after a proxy switch when nothing relevant is on', function (bool $sentinelEnabled, bool $analyticsEnabled) {
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, $sentinelEnabled, $analyticsEnabled);
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
StartSentinel::assertNotPushed();
expect((bool) $server->fresh()->settings->is_sentinel_enabled)->toBe($sentinelEnabled);
})->with([
'analytics off' => [true, false],
'Sentinel off' => [false, true],
'both off' => [false, false],
]);
it('does not restart Sentinel when the proxy type does not change', function () {
$server = proxySwitchServer($this, ProxyTypes::CADDY->value, sentinelEnabled: true, analyticsEnabled: true);
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
StartSentinel::assertNotPushed();
});
it('points Sentinel at the Caddy access log after a switch from Traefik', function () {
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
expect(StartSentinel::trafficLogDirectory($server))->toBe('/data/coolify/proxy');
$server->changeProxy(ProxyTypes::CADDY->value, async: true);
$server = $server->fresh();
expect(StartSentinel::trafficLogDirectory($server))->toBe('/data/coolify/proxy/caddy')
->and(StartSentinel::sentinelTrafficEnvironment($server)['TRAFFIC_ACCESS_LOG_PATH'])
->toBe('/data/coolify/proxy/caddy/access.log');
});
it('gives Sentinel no traffic environment after a switch to a proxy without analytics support', function () {
$server = proxySwitchServer($this, ProxyTypes::TRAEFIK->value, sentinelEnabled: true, analyticsEnabled: true);
$server->changeProxy(ProxyTypes::NONE->value, async: true);
expect(StartSentinel::sentinelTrafficEnvironment($server->fresh()))->toBe([]);
});