Files
coolify/tests/Feature/PushServerUpdateJobOptimizationTest.php

232 lines
7.5 KiB
PHP

<?php
use App\Actions\Proxy\CheckProxy;
use App\Actions\Proxy\StartProxy;
use App\Events\ProxyStatusChanged;
use App\Events\ServiceChecked;
use App\Jobs\PushServerUpdateJob;
use App\Jobs\ServerStorageCheckJob;
use App\Listeners\ProxyStatusChangedNotification;
use App\Models\Server;
use App\Models\Team;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Process;
use Illuminate\Support\Facades\Queue;
uses(RefreshDatabase::class);
beforeEach(function () {
Queue::fake();
Cache::flush();
});
it('dispatches storage check when disk percentage changes above threshold', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
// Default notification threshold is 80%.
$data = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 85],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
return $job->server->id === $server->id && $job->percentage === 85;
});
});
it('does not dispatch storage check when disk usage is below threshold', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
// 45% is well below the default 80% notification threshold — nothing to do.
$data = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 45],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
Queue::assertNotPushed(ServerStorageCheckJob::class);
});
it('clears stale storage cache when disk usage drops below threshold', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$storageCacheKey = 'storage-check:'.$server->id;
Cache::put($storageCacheKey, 85, 600);
$belowThresholdData = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 45],
];
$job = new PushServerUpdateJob($server, $belowThresholdData);
$job->handle();
Queue::assertNotPushed(ServerStorageCheckJob::class);
expect(Cache::missing($storageCacheKey))->toBeTrue();
Queue::fake();
$aboveThresholdData = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 85],
];
$job = new PushServerUpdateJob($server, $aboveThresholdData);
$job->handle();
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
return $job->server->id === $server->id && $job->percentage === 85;
});
});
it('does not dispatch storage check when disk percentage is unchanged', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
// Simulate a previous push that cached the percentage (above threshold).
Cache::put('storage-check:'.$server->id, 85, 600);
$data = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 85],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
Queue::assertNotPushed(ServerStorageCheckJob::class);
});
it('dispatches storage check when disk percentage changes from cached value', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
// Simulate a previous push that cached 85% (above threshold).
Cache::put('storage-check:'.$server->id, 85, 600);
$data = [
'containers' => [],
'filesystem_usage_root' => ['used_percentage' => 90],
];
$job = new PushServerUpdateJob($server, $data);
$job->handle();
Queue::assertPushed(ServerStorageCheckJob::class, function ($job) use ($server) {
return $job->server->id === $server->id && $job->percentage === 90;
});
});
it('does not dispatch proxy network work when the proxy is running', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
$data = [
'containers' => [
[
'name' => 'coolify-proxy',
'state' => 'running',
'health_status' => 'healthy',
'labels' => ['coolify.managed' => true],
],
],
'filesystem_usage_root' => ['used_percentage' => 10],
];
Event::fake([ServiceChecked::class]);
(new PushServerUpdateJob($server, $data))->handle();
Queue::assertNothingPushed();
});
it('marks a stale proxy status as running when sentinel reports a running proxy', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
$server->proxy->set('type', 'TRAEFIK');
$server->proxy->set('status', 'exited');
$server->save();
$data = [
'containers' => [
[
'name' => 'coolify-proxy',
'state' => 'running',
'health_status' => 'healthy',
'labels' => ['coolify.managed' => true],
],
],
'filesystem_usage_root' => ['used_percentage' => 10],
];
Event::fake([ServiceChecked::class]);
(new PushServerUpdateJob($server, $data))->handle();
expect($server->fresh()->proxy->get('status'))->toBe('running');
});
it('does not start a second proxy while a proxy restart runs', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
$server->proxy->set('type', 'TRAEFIK');
$server->proxy->set('status', 'restarting');
$server->save();
$server->markProxyTransitionStarted();
CheckProxy::shouldNotRun();
StartProxy::shouldNotRun();
Event::fake([ServiceChecked::class]);
(new PushServerUpdateJob($server, ['containers' => [], 'filesystem_usage_root' => ['used_percentage' => 10]]))->handle();
expect($server->fresh()->proxy->get('status'))->toBe('restarting');
});
it('starts a missing proxy when no proxy restart runs', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
$server->proxy->set('type', 'TRAEFIK');
$server->save();
CheckProxy::shouldRun()->once()->andReturn(true);
StartProxy::shouldRun()->once();
Event::fake([ServiceChecked::class]);
(new PushServerUpdateJob($server, ['containers' => [], 'filesystem_usage_root' => ['used_percentage' => 10]]))->handle();
});
it('ends the proxy restart when the proxy status listener runs', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$server->markProxyTransitionStarted();
Process::fake();
(new ProxyStatusChangedNotification)->handle(new ProxyStatusChanged(ProxyStatusChanged::dataFor($server)));
expect($server->isProxyTransitionInProgress())->toBeFalse();
});
it('uses default queue for PushServerUpdateJob', function () {
$team = Team::factory()->create();
$server = Server::factory()->create(['team_id' => $team->id]);
$job = new PushServerUpdateJob($server, ['containers' => []]);
expect($job->queue)->toBeNull();
});