mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-12 15:53:46 -04:00
- Delete Blade components, component classes, Livewire components and views that nothing uses after the redesign. - Delete tests that only compare Blade/CSS source text; keep the source checks for authorization gates, stable wire:key values and escaping. - Update stale tests to the current UI and behaviour. - Update DESIGN.md to the current navigation and layout. - Add a lesson: test behaviour, not source markup. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
218 lines
8.3 KiB
PHP
218 lines
8.3 KiB
PHP
<?php
|
|
|
|
use App\Actions\Docker\GetContainersStatus;
|
|
use App\Actions\Service\StopServiceApplication;
|
|
use App\Jobs\PushServerUpdateJob;
|
|
use App\Models\Application;
|
|
use App\Models\ApplicationPreview;
|
|
use App\Models\ServiceApplication;
|
|
use App\Models\ServiceDatabase;
|
|
use App\Models\StandaloneClickhouse;
|
|
use App\Models\StandaloneDragonfly;
|
|
use App\Models\StandaloneKeydb;
|
|
use App\Models\StandaloneMariadb;
|
|
use App\Models\StandaloneMongodb;
|
|
use App\Models\StandaloneMysql;
|
|
use App\Models\StandalonePostgresql;
|
|
use App\Models\StandaloneRedis;
|
|
use App\Models\StandaloneSqlite;
|
|
use App\Traits\HasRestartLimit;
|
|
use Illuminate\Database\Eloquent\Model;
|
|
use Illuminate\Database\Schema\Blueprint;
|
|
use Illuminate\Support\Facades\Blade;
|
|
use Illuminate\Support\Facades\Schema;
|
|
|
|
it('gives independently runnable application resources restart limit state', function (string $modelClass) {
|
|
expect(class_uses_recursive($modelClass))->toContain(HasRestartLimit::class);
|
|
|
|
$resource = new $modelClass;
|
|
|
|
expect($resource->getFillable())->toContain(
|
|
'restart_count',
|
|
'max_restart_count',
|
|
'restart_limit_reached',
|
|
'last_restart_at',
|
|
'last_restart_type',
|
|
)->and($resource->getCasts())->toMatchArray([
|
|
'restart_count' => 'integer',
|
|
'max_restart_count' => 'integer',
|
|
'restart_limit_reached' => 'boolean',
|
|
'last_restart_at' => 'datetime',
|
|
]);
|
|
})->with([
|
|
[ApplicationPreview::class],
|
|
[ServiceApplication::class],
|
|
]);
|
|
|
|
it('collects restart counts for preview and service containers from both status sources', function () {
|
|
$dockerStatus = file_get_contents(app_path('Actions/Docker/GetContainersStatus.php'));
|
|
$sentinelStatus = file_get_contents(app_path('Jobs/PushServerUpdateJob.php'));
|
|
|
|
expect($dockerStatus)
|
|
->toContain('previewContainerRestartCounts')
|
|
->toContain('serviceContainerRestartCounts')
|
|
->and($sentinelStatus)
|
|
->toContain('previewContainerRestartCounts')
|
|
->toContain('serviceContainerRestartCounts');
|
|
});
|
|
|
|
it('ignores docker compose one-off job containers in both status sources', function () {
|
|
$dockerStatus = file_get_contents(app_path('Actions/Docker/GetContainersStatus.php'));
|
|
$sentinelStatus = file_get_contents(app_path('Jobs/PushServerUpdateJob.php'));
|
|
|
|
expect($dockerStatus)
|
|
->toContain("filter_var(data_get(\$labels, 'com.docker.compose.oneoff'), FILTER_VALIDATE_BOOLEAN)")
|
|
->and($sentinelStatus)
|
|
->toContain("filter_var(\$labels->get('com.docker.compose.oneoff'), FILTER_VALIDATE_BOOLEAN)");
|
|
});
|
|
|
|
it('imports the application model used when claiming a restart limit', function () {
|
|
$statusAction = file_get_contents(app_path('Actions/Docker/GetContainersStatus.php'));
|
|
|
|
expect($statusAction)
|
|
->toContain('use App\\Models\\Application;')
|
|
->toContain('Application::query()');
|
|
});
|
|
|
|
it('limits restarts only for applications', function () {
|
|
$migrations = collect(glob(database_path('migrations/*.php')))
|
|
->map(fn (string $path): string => file_get_contents($path))
|
|
->implode("\n");
|
|
|
|
expect($migrations)
|
|
->toContain("'application_previews'")
|
|
->toContain("'service_applications'")
|
|
->toContain("'max_restart_count'")
|
|
->toContain("'restart_limit_reached'")
|
|
->toContain("dropColumn(['max_restart_count', 'restart_limit_reached'])");
|
|
|
|
$databaseModels = [
|
|
ServiceDatabase::class,
|
|
StandalonePostgresql::class,
|
|
StandaloneRedis::class,
|
|
StandaloneMongodb::class,
|
|
StandaloneMysql::class,
|
|
StandaloneMariadb::class,
|
|
StandaloneKeydb::class,
|
|
StandaloneDragonfly::class,
|
|
StandaloneClickhouse::class,
|
|
StandaloneSqlite::class,
|
|
];
|
|
|
|
foreach ($databaseModels as $databaseModel) {
|
|
expect(class_uses_recursive($databaseModel))->not->toContain(HasRestartLimit::class);
|
|
}
|
|
|
|
foreach ([
|
|
'service_databases',
|
|
'standalone_postgresqls',
|
|
'standalone_redis',
|
|
'standalone_mongodbs',
|
|
'standalone_mysqls',
|
|
'standalone_mariadbs',
|
|
'standalone_keydbs',
|
|
'standalone_dragonflies',
|
|
'standalone_clickhouses',
|
|
'standalone_sqlites',
|
|
] as $databaseTable) {
|
|
expect(Schema::hasColumn($databaseTable, 'max_restart_count'))->toBeFalse()
|
|
->and(Schema::hasColumn($databaseTable, 'restart_limit_reached'))->toBeFalse();
|
|
}
|
|
|
|
foreach ([GetContainersStatus::class, PushServerUpdateJob::class] as $statusUpdater) {
|
|
$source = file_get_contents((new ReflectionClass($statusUpdater))->getFileName());
|
|
|
|
expect($source)
|
|
->not->toContain('$database->trackRestartCount')
|
|
->not->toContain('$database->stoppedAfterRestartLimit()');
|
|
}
|
|
|
|
$stopServiceResource = file_get_contents((new ReflectionClass(StopServiceApplication::class))->getFileName());
|
|
|
|
expect($stopServiceResource)
|
|
->toContain('$resetRestartCount && $serviceApplication instanceof ServiceApplication');
|
|
});
|
|
|
|
it('makes restart limits opt in for new application resources', function () {
|
|
$migrationPaths = glob(database_path('migrations/*_make_restart_limits_opt_in.php'));
|
|
|
|
expect($migrationPaths)->toHaveCount(1);
|
|
|
|
$migration = file_get_contents($migrationPaths[0]);
|
|
|
|
expect($migration)
|
|
->toContain("['applications', 'application_previews', 'service_applications']")
|
|
->toContain("integer('max_restart_count')->default(0)->change()")
|
|
->toContain('public $withinTransaction = false;')
|
|
->toContain("->where('max_restart_count', 10)")
|
|
->toContain('->chunkById(5000')
|
|
->toContain("->whereIn('id', \$resources->pluck('id'))")
|
|
->toContain("'max_restart_count' => 0")
|
|
->toContain("'restart_limit_reached' => false");
|
|
|
|
$applicationSettings = file_get_contents(app_path('Livewire/Project/Application/Advanced.php'));
|
|
$serviceSettings = file_get_contents(app_path('Livewire/Project/Service/Index.php'));
|
|
|
|
expect($applicationSettings)
|
|
->toContain('public int $maxRestartCount = 0;')
|
|
->toContain('$this->application->max_restart_count ?? 0')
|
|
->and($serviceSettings)
|
|
->toContain('public mixed $maxRestartCount = 0;')
|
|
->toContain('$this->serviceApplication->max_restart_count ?? 0');
|
|
|
|
foreach ([Application::class, ApplicationPreview::class, ServiceApplication::class] as $modelClass) {
|
|
expect((new $modelClass)->max_restart_count)->toBe(0);
|
|
}
|
|
});
|
|
|
|
it('does not render restart limit warnings for service databases', function () {
|
|
$html = Blade::render(
|
|
'<x-application.restart-limit-warning :application="$database" />',
|
|
['database' => new ServiceDatabase],
|
|
);
|
|
|
|
expect(trim($html))->toBeEmpty();
|
|
});
|
|
|
|
it('atomically claims a resource restart limit once and can reset it', function () {
|
|
Schema::create('restart_limit_test_resources', function (Blueprint $table): void {
|
|
$table->id();
|
|
$table->string('status')->default('running');
|
|
$table->integer('restart_count')->default(0);
|
|
$table->integer('max_restart_count')->default(2);
|
|
$table->boolean('restart_limit_reached')->default(false);
|
|
$table->timestamp('last_restart_at')->nullable();
|
|
$table->string('last_restart_type')->nullable();
|
|
$table->timestamps();
|
|
});
|
|
|
|
$resource = new class extends Model
|
|
{
|
|
use HasRestartLimit;
|
|
|
|
protected $table = 'restart_limit_test_resources';
|
|
};
|
|
$resource->save();
|
|
$resource->refresh();
|
|
|
|
expect($resource->trackRestartCount(2))->toBeTrue()
|
|
->and($resource->fresh()->restart_limit_reached)->toBeTrue()
|
|
->and($resource->trackRestartCount(2))->toBeFalse();
|
|
|
|
$resource->resetRestartLimit();
|
|
|
|
expect($resource->fresh()->restart_count)->toBe(0)
|
|
->and($resource->restart_limit_reached)->toBeFalse();
|
|
|
|
$resourceWithExistingRestarts = $resource->newInstance();
|
|
$resourceWithExistingRestarts->max_restart_count = 0;
|
|
$resourceWithExistingRestarts->save();
|
|
expect($resourceWithExistingRestarts->trackRestartCount(17))->toBeFalse();
|
|
|
|
$resourceWithExistingRestarts->update(['max_restart_count' => 10]);
|
|
expect($resourceWithExistingRestarts->trackRestartCount(17))->toBeTrue()
|
|
->and($resourceWithExistingRestarts->fresh()->restart_limit_reached)->toBeTrue();
|
|
|
|
Schema::drop('restart_limit_test_resources');
|
|
});
|