mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
518 lines
22 KiB
PHP
518 lines
22 KiB
PHP
<?php
|
||
|
||
use App\Actions\Server\StartSentinel;
|
||
use App\Livewire\Fleet\Metrics;
|
||
use App\Models\Application;
|
||
use App\Models\Environment;
|
||
use App\Models\InstanceSettings;
|
||
use App\Models\PrivateKey;
|
||
use App\Models\Project;
|
||
use App\Models\Server;
|
||
use App\Models\ServerSetting;
|
||
use App\Models\Team;
|
||
use App\Models\User;
|
||
use App\Services\SentinelMetricsClient;
|
||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
use Illuminate\Support\Facades\DB;
|
||
use Illuminate\Support\Facades\Queue;
|
||
use Livewire\Livewire;
|
||
use Tests\Support\ConcurrentMetricsClient;
|
||
|
||
uses(RefreshDatabase::class);
|
||
|
||
class StubMetricsClient extends SentinelMetricsClient
|
||
{
|
||
/** @var array<string, ?array> serverUuid => summary */
|
||
public static array $summaries = [];
|
||
|
||
/** @var array<string, array> serverUuid => containers */
|
||
public static array $containers = [];
|
||
|
||
public static array $histories = [];
|
||
|
||
public static array $failingMetrics = [];
|
||
|
||
public function summary(): ?array
|
||
{
|
||
if (in_array('summary', self::$failingMetrics, true)) {
|
||
throw new RuntimeException('summary unavailable');
|
||
}
|
||
|
||
return self::$summaries[$this->server->uuid] ?? null;
|
||
}
|
||
|
||
public function containersCurrent(): array
|
||
{
|
||
return self::$containers[$this->server->uuid] ?? [];
|
||
}
|
||
|
||
public function history(string $metric, string $range): array
|
||
{
|
||
if (in_array($metric, self::$failingMetrics, true)) {
|
||
throw new RuntimeException('metric unavailable');
|
||
}
|
||
|
||
return self::$histories[$this->server->uuid][$metric] ?? [];
|
||
}
|
||
|
||
public function networkHistory(string $range): array
|
||
{
|
||
return ['rx' => [], 'tx' => []];
|
||
}
|
||
}
|
||
|
||
class TestableMetrics extends Metrics
|
||
{
|
||
protected function metricsClient(Server $server): SentinelMetricsClient
|
||
{
|
||
return new StubMetricsClient($server);
|
||
}
|
||
}
|
||
|
||
class ProcessTestableMetrics extends Metrics
|
||
{
|
||
protected function metricsClient(Server $server): SentinelMetricsClient
|
||
{
|
||
return new ConcurrentMetricsClient($server);
|
||
}
|
||
}
|
||
|
||
beforeEach(function () {
|
||
// The settings hook starts Sentinel when metrics turn on. A sync queue would run it and make a real SSH call.
|
||
Queue::fake();
|
||
StubMetricsClient::$summaries = [];
|
||
StubMetricsClient::$containers = [];
|
||
StubMetricsClient::$histories = [];
|
||
StubMetricsClient::$failingMetrics = [];
|
||
|
||
InstanceSettings::unguarded(fn () => InstanceSettings::query()->create(['id' => 0]));
|
||
$user = User::factory()->create();
|
||
$team = Team::factory()->create();
|
||
$user->teams()->attach($team, ['role' => 'owner']);
|
||
$this->actingAs($user);
|
||
session(['currentTeam' => $team]);
|
||
$this->privateKey = PrivateKey::factory()->create(['team_id' => $team->id]);
|
||
$this->team = $team;
|
||
});
|
||
|
||
function metricsServer($team, $privateKey, bool $reachable = true): Server
|
||
{
|
||
$server = Server::factory()->create(['team_id' => $team->id, 'private_key_id' => $privateKey->id]);
|
||
$server->settings->update(['is_metrics_enabled' => true]);
|
||
$server->settings->forceFill(['is_reachable' => $reachable, 'is_usable' => $reachable])->saveQuietly();
|
||
|
||
return $server;
|
||
}
|
||
|
||
function onlineSummary(array $overrides = []): array
|
||
{
|
||
return array_merge([
|
||
'online' => true, 'cpu' => 30.0, 'memUsed' => 100, 'memTotal' => 200,
|
||
'diskUsed' => 10, 'diskTotal' => 100, 'diskPercent' => 10.0,
|
||
'load1' => 0.4, 'netRx' => 5.0, 'netTx' => 1.0,
|
||
], $overrides);
|
||
}
|
||
|
||
it('keeps an unreachable server in the table but excludes it from KPIs', function () {
|
||
$ok = metricsServer($this->team, $this->privateKey);
|
||
$dead = metricsServer($this->team, $this->privateKey, reachable: false);
|
||
|
||
StubMetricsClient::$summaries = [
|
||
$ok->uuid => onlineSummary(['cpu' => 30.0]),
|
||
$dead->uuid => null,
|
||
];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
expect($component->get('serverRows'))->toHaveCount(2);
|
||
|
||
$deadRow = collect($component->get('serverRows'))->firstWhere('uuid', $dead->uuid);
|
||
expect($deadRow['online'])->toBeFalse();
|
||
expect($deadRow['cpu'])->toBeNull();
|
||
|
||
expect($component->get('kpis')['serversOnline'])->toBe(1);
|
||
expect($component->get('kpis')['cpuAvg'])->toBe(30.0);
|
||
});
|
||
|
||
it('counts a reachable server as online when Sentinel returns no summary', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
$server->update(['name' => 'web-1']);
|
||
StubMetricsClient::$summaries = [$server->uuid => null];
|
||
|
||
$component = Livewire::test(TestableMetrics::class)
|
||
->assertSee('No metrics')
|
||
->assertDontSee('Offline');
|
||
|
||
$row = collect($component->get('serverRows'))->firstWhere('uuid', $server->uuid);
|
||
expect($row['online'])->toBeTrue();
|
||
expect($row['reporting'])->toBeFalse();
|
||
expect($component->get('kpis')['serversOnline'])->toBe(1);
|
||
expect($component->get('kpis')['serversReporting'])->toBe(0);
|
||
});
|
||
|
||
it('ranks containers across servers by the selected metric', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary(['diskPercent' => 50.0])];
|
||
StubMetricsClient::$containers = [$s->uuid => [
|
||
['id' => 'low', 'cpu' => 5.0, 'memUsed' => 10, 'memPercent' => 1.0, 'diskBytes' => 1, 'net' => 0.0],
|
||
['id' => 'high', 'cpu' => 88.0, 'memUsed' => 20, 'memPercent' => 2.0, 'diskBytes' => 2, 'net' => 0.0],
|
||
]];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
expect(array_column($component->get('topContainers'), 'id')[0])->toBe('high');
|
||
});
|
||
|
||
it('shows the KPI tiles, a stale badge for offline servers, and the container toggle', function () {
|
||
$ok = metricsServer($this->team, $this->privateKey);
|
||
$ok->update(['name' => 'web-1']);
|
||
$dead = metricsServer($this->team, $this->privateKey, reachable: false);
|
||
$dead->update(['name' => 'web-2']);
|
||
|
||
StubMetricsClient::$summaries = [
|
||
$ok->uuid => onlineSummary(['cpu' => 95.0, 'diskPercent' => 95.0]),
|
||
$dead->uuid => null,
|
||
];
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->assertSee('web-1')
|
||
->assertSee('web-2')
|
||
->assertSee('Fleet CPU')
|
||
->assertSee('healthy')
|
||
->assertSee('attention')
|
||
->assertSee('Offline');
|
||
});
|
||
|
||
it('re-ranks containers on metric switch without re-fetching', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$s->uuid => [
|
||
['id' => 'a', 'cpu' => 5.0, 'memUsed' => 900, 'memPercent' => 9.0, 'diskBytes' => 1, 'net' => 0.0],
|
||
['id' => 'b', 'cpu' => 90.0, 'memUsed' => 100, 'memPercent' => 1.0, 'diskBytes' => 9, 'net' => 0.0],
|
||
]];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
expect(array_column($component->get('topContainers'), 'id')[0])->toBe('b'); // cpu
|
||
|
||
// Empty the stub so a re-fetch would change nothing/ break; the re-rank must use cached rows.
|
||
StubMetricsClient::$containers = [];
|
||
$component->set('containerMetric', 'memory');
|
||
|
||
expect(array_column($component->get('topContainers'), 'id')[0])->toBe('a'); // memory, still ranked
|
||
});
|
||
|
||
it('cannot see another team’s servers', function () {
|
||
$otherTeam = Team::factory()->create();
|
||
// Reuse the existing key id: the factory's private key is hardcoded, so a second
|
||
// PrivateKey::create collides on fingerprint. Only the server's team_id matters here.
|
||
$server = Server::factory()->create(['team_id' => $otherTeam->id, 'private_key_id' => $this->privateKey->id]);
|
||
$server->settings->update(['is_metrics_enabled' => true]);
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
expect($component->get('serverRows'))->toHaveCount(0);
|
||
});
|
||
|
||
it('drops the "Fleet" tile prefix when scoped to a single server', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->assertSee('Fleet CPU')
|
||
->set('serverUuid', $s->uuid)
|
||
->assertDontSee('Fleet')
|
||
->assertSee('for the selected server');
|
||
});
|
||
|
||
it('renders container metric values without leaking a blade directive', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$s->uuid => [
|
||
['id' => 'a', 'cpu' => 5.0, 'memUsed' => 900, 'memPercent' => 9.0, 'diskBytes' => 1, 'net' => 2048.0],
|
||
]];
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->set('containerMetric', 'network')
|
||
->assertDontSee('@break')
|
||
->assertSee('/s');
|
||
});
|
||
|
||
it('falls back to cpu for an unknown container metric', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$s->uuid => [
|
||
['id' => 'a', 'cpu' => 5.0, 'memUsed' => 900, 'memPercent' => 9.0, 'diskBytes' => 1, 'net' => 0.0],
|
||
]];
|
||
|
||
Livewire::withQueryParams(['cmetric' => 'foo'])
|
||
->test(TestableMetrics::class)
|
||
->assertOk()
|
||
->assertSet('containerMetric', 'cpu')
|
||
->set('containerMetric', 'bar')
|
||
->assertSet('containerMetric', 'cpu');
|
||
});
|
||
|
||
it('keys container rows by server so shared container ids do not collide', function () {
|
||
$one = metricsServer($this->team, $this->privateKey);
|
||
$two = metricsServer($this->team, $this->privateKey);
|
||
$proxy = ['id' => 'coolify-proxy', 'cpu' => 1.0, 'memUsed' => 10, 'memPercent' => 1.0, 'diskBytes' => 1, 'net' => 0.0];
|
||
|
||
StubMetricsClient::$summaries = [$one->uuid => onlineSummary(), $two->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$one->uuid => [$proxy], $two->uuid => [$proxy]];
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->assertSeeHtml('wire:key="fleet-container-'.$one->uuid.'-coolify-proxy"')
|
||
->assertSeeHtml('wire:key="fleet-container-'.$two->uuid.'-coolify-proxy"');
|
||
});
|
||
|
||
it('passes the spark time axis to the KPI sparklines on first paint', function () {
|
||
$s = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$s->uuid => onlineSummary()];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
$component->set('chartData', ['sparkCategories' => [1700000100000], 'cpuSpark' => [12.5]]);
|
||
|
||
$component->assertSeeHtml('1700000100000');
|
||
});
|
||
|
||
it('removes a transferred server on refresh', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
expect($component->get('serverRows'))->toHaveCount(1);
|
||
|
||
$server->update(['team_id' => Team::factory()->create()->id]);
|
||
$component->call('loadData');
|
||
|
||
expect($component->get('serverRows'))->toBe([]);
|
||
expect($component->get('containersRaw'))->toBe([]);
|
||
expect($component->get('kpis')['serversTotal'])->toBe(0);
|
||
});
|
||
|
||
it('permits team members to read metrics', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
$member = User::factory()->create();
|
||
$member->teams()->attach($this->team, ['role' => 'member']);
|
||
$this->actingAs($member);
|
||
|
||
expect(Livewire::test(TestableMetrics::class)->get('kpis')['serversOnline'])->toBe(1);
|
||
});
|
||
|
||
it('does not query Sentinel on a server that Coolify cannot reach', function () {
|
||
$server = metricsServer($this->team, $this->privateKey, reachable: false);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
StubMetricsClient::$histories = [$server->uuid => ['cpu' => [[1000, 25.0]]]];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
$row = collect($component->get('serverRows'))->firstWhere('uuid', $server->uuid);
|
||
expect($row['online'])->toBeFalse();
|
||
expect($row['reporting'])->toBeFalse();
|
||
expect($component->get('chartData')['cpu'])->toBe([]);
|
||
});
|
||
|
||
it('shows the page when every Sentinel read fails', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$failingMetrics = ['summary', 'cpu', 'memory', 'disk', 'load'];
|
||
|
||
$component = Livewire::test(TestableMetrics::class)->assertOk();
|
||
|
||
$row = collect($component->get('serverRows'))->firstWhere('uuid', $server->uuid);
|
||
expect($row['reporting'])->toBeFalse();
|
||
expect($component->get('kpis')['serversReporting'])->toBe(0);
|
||
});
|
||
|
||
it('keeps host trends when an older Sentinel has no summary endpoint', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$histories = [$server->uuid => ['cpu' => [[1000, 25.0]], 'memory' => [[1000, 100.0]]]];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
expect($component->get('chartData')['cpu'])->toBe([[1000, 25.0]]);
|
||
expect($component->get('chartData')['memory'])->toBe([[1000, 100.0]]);
|
||
expect($component->get('topContainers'))->toBe([]);
|
||
});
|
||
|
||
it('keeps other metrics when one independent operation fails', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
StubMetricsClient::$histories = [$server->uuid => ['cpu' => [[1000, 25.0]]]];
|
||
StubMetricsClient::$failingMetrics = ['disk'];
|
||
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
expect($component->get('chartData')['cpu'])->toBe([[1000, 25.0]]);
|
||
expect($component->get('chartData')['disk'])->toBe([]);
|
||
expect($component->get('kpis')['serversOnline'])->toBe(1);
|
||
});
|
||
|
||
it('resolves container names for the instance team with id zero', function () {
|
||
$rootTeam = Team::factory()->create(['id' => 0]);
|
||
auth()->user()->teams()->attach($rootTeam, ['role' => 'owner']);
|
||
auth()->user()->unsetRelation('teams');
|
||
session(['currentTeam' => $rootTeam]);
|
||
$project = Project::factory()->create(['team_id' => 0]);
|
||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||
$application = Application::factory()->create(['environment_id' => $environment->id]);
|
||
|
||
$meta = Livewire::test(TestableMetrics::class)->instance()->containerMeta($application->uuid);
|
||
|
||
expect($meta['name'])->toBe($application->name);
|
||
expect($meta['link'])->toContain($application->uuid);
|
||
});
|
||
|
||
it('denies metrics refresh after team membership is removed', function () {
|
||
$this->withoutVite();
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
auth()->user()->teams()->detach($this->team);
|
||
auth()->user()->unsetRelation('teams');
|
||
|
||
$component->call('loadData')->assertForbidden();
|
||
});
|
||
|
||
it('clears stale container rankings after a server transfer', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$server->uuid => [['id' => 'private-container', 'cpu' => 10]]];
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
$server->update(['team_id' => Team::factory()->create()->id]);
|
||
$component->set('containerMetric', 'memory');
|
||
|
||
expect($component->get('topContainers'))->toBe([]);
|
||
expect($component->get('serverRows'))->toBe([]);
|
||
});
|
||
|
||
it('merges metrics of several servers and isolates endpoint failures', function () {
|
||
metricsServer($this->team, $this->privateKey);
|
||
metricsServer($this->team, $this->privateKey);
|
||
|
||
$component = Livewire::test(ProcessTestableMetrics::class);
|
||
|
||
expect($component->get('kpis')['serversOnline'])->toBe(2);
|
||
expect($component->get('topContainers'))->toHaveCount(2);
|
||
expect($component->get('chartData')['cpu'])->toBe([[1000, 25.0]]);
|
||
expect($component->get('chartData')['memory'])->toBe([[1000, 200.0]]);
|
||
expect($component->get('chartData')['disk'])->toBe([]);
|
||
expect($component->get('chartData')['networkRx'])->toBe([[1000, 10.0]]);
|
||
});
|
||
|
||
it('resolves container names with a fixed number of queries', function () {
|
||
$project = Project::factory()->create(['team_id' => $this->team->id]);
|
||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||
$apps = Application::factory()->count(3)->create(['environment_id' => $environment->id]);
|
||
$component = Livewire::test(TestableMetrics::class)->instance();
|
||
$component->containersRaw = $apps->map(fn ($app) => ['id' => $app->uuid])->push(['id' => 'coolify-proxy'])->all();
|
||
|
||
DB::enableQueryLog();
|
||
$names = $apps->map(fn ($app) => $component->containerMeta($app->uuid)['name']);
|
||
$proxy = $component->containerMeta('coolify-proxy');
|
||
|
||
expect($names->all())->toBe($apps->pluck('name')->all())
|
||
->and($proxy)->toBe(['name' => 'coolify-proxy', 'image' => null, 'link' => null])
|
||
->and(count(DB::getQueryLog()))->toBeLessThan(20);
|
||
});
|
||
|
||
it('does not show the name of another team’s resource for a container', function () {
|
||
$project = Project::factory()->create(['team_id' => Team::factory()->create()->id]);
|
||
$environment = Environment::factory()->create(['project_id' => $project->id]);
|
||
$foreign = Application::factory()->create(['environment_id' => $environment->id]);
|
||
|
||
$meta = Livewire::test(TestableMetrics::class)->instance()->containerMeta($foreign->uuid);
|
||
|
||
expect($meta)->toBe(['name' => $foreign->uuid, 'image' => null, 'link' => null]);
|
||
});
|
||
|
||
it('clears stale metrics after the last server is deleted', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
StubMetricsClient::$summaries = [$server->uuid => onlineSummary()];
|
||
StubMetricsClient::$containers = [$server->uuid => [['id' => 'deleted-container', 'cpu' => 10]]];
|
||
$component = Livewire::test(TestableMetrics::class);
|
||
|
||
$server->delete();
|
||
$component->set('containerMetric', 'memory');
|
||
|
||
expect($component->get('topContainers'))->toBe([]);
|
||
expect($component->get('serverRows'))->toBe([]);
|
||
});
|
||
|
||
it('repairs the Sentinel token before creating worker clients', function () {
|
||
$server = metricsServer($this->team, $this->privateKey);
|
||
$server->settings->updateQuietly(['sentinel_token' => 'invalid token']);
|
||
app()->bind(SentinelMetricsClient::class, fn ($app, $parameters) => new ConcurrentMetricsClient($parameters['server']));
|
||
|
||
loadLazy(Livewire::test(Metrics::class));
|
||
|
||
$token = $server->settings->fresh()->sentinel_token;
|
||
expect(ServerSetting::isValidSentinelToken($token))->toBeTrue();
|
||
expect($token)->not->toBe('invalid token');
|
||
});
|
||
|
||
/** A server that Coolify can reach, with metrics off. */
|
||
function reachableServerWithoutMetrics(int $teamId, PrivateKey $privateKey): Server
|
||
{
|
||
$server = Server::factory()->create(['team_id' => $teamId, 'private_key_id' => $privateKey->id]);
|
||
$server->settings->forceFill(['is_reachable' => true, 'is_usable' => true])->saveQuietly();
|
||
|
||
return $server;
|
||
}
|
||
|
||
it('enables metrics on each eligible server of the team from the empty state', function () {
|
||
$first = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
$second = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
$build = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
$build->settings->update(['server_role' => 'build', 'is_build_server' => true]);
|
||
$unreachable = Server::factory()->create(['team_id' => $this->team->id, 'private_key_id' => $this->privateKey->id]);
|
||
$foreign = reachableServerWithoutMetrics(Team::factory()->create()->id, $this->privateKey);
|
||
|
||
$component = Livewire::test(TestableMetrics::class)
|
||
->assertSee('Enable metrics on 2 servers')
|
||
->call('enableMetrics');
|
||
|
||
expect($first->settings->fresh()->is_metrics_enabled)->toBeTruthy();
|
||
expect($second->settings->fresh()->is_metrics_enabled)->toBeTruthy();
|
||
expect($build->settings->fresh()->is_metrics_enabled)->toBeFalsy();
|
||
expect($unreachable->settings->fresh()->is_metrics_enabled)->toBeFalsy();
|
||
expect($foreign->settings->fresh()->is_metrics_enabled)->toBeFalsy();
|
||
StartSentinel::assertPushed(2);
|
||
|
||
expect(array_column($component->get('serverRows'), 'uuid'))->toEqualCanonicalizing([$first->uuid, $second->uuid]);
|
||
$component->assertDontSee('No server metrics yet');
|
||
});
|
||
|
||
it('keeps enabling metrics on the other servers when one server fails', function () {
|
||
$broken = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
$healthy = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
DB::statement("CREATE TRIGGER fail_settings_update BEFORE UPDATE ON server_settings WHEN NEW.server_id = {$broken->id} BEGIN SELECT RAISE(ABORT, 'write failed'); END");
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->call('enableMetrics')
|
||
->assertDispatched('success', fn (string $event, array $params) => str_contains($params[0], 'Metrics enabled on 1 server.'))
|
||
->assertDispatched('error', fn (string $event, array $params) => str_contains($params[0], $broken->name));
|
||
|
||
expect($broken->settings->fresh()->is_metrics_enabled)->toBeFalsy();
|
||
expect($healthy->settings->fresh()->is_metrics_enabled)->toBeTruthy();
|
||
StartSentinel::assertPushed(1);
|
||
});
|
||
|
||
it('does not let a team member enable metrics', function () {
|
||
$server = reachableServerWithoutMetrics($this->team->id, $this->privateKey);
|
||
$member = User::factory()->create();
|
||
$member->teams()->attach($this->team, ['role' => 'member']);
|
||
$this->actingAs($member);
|
||
|
||
Livewire::test(TestableMetrics::class)
|
||
->assertSee('No server metrics yet')
|
||
->assertDontSee('Enable metrics on '.$server->name)
|
||
->call('enableMetrics');
|
||
|
||
expect($server->settings->fresh()->is_metrics_enabled)->toBeFalsy();
|
||
StartSentinel::assertNotPushed();
|
||
});
|