mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-14 12:16:45 +00:00
Merge remote-tracking branch 'origin/next' into volume-backups-server-s3
This commit is contained in:
commit
b97a97e955
38 files changed
+379
-651
No files matched your search
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Server\ValidateServer;
|
||||
use App\Jobs\ValidateAndInstallServerJob;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Queue;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::updateOrCreate(['id' => 0], ['is_api_enabled' => true]);
|
||||
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user = User::factory()->create();
|
||||
$this->team->members()->attach($this->user->id, ['role' => 'owner']);
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->token = $this->user->createToken('server-validation', ['write'])->plainTextToken;
|
||||
|
||||
Queue::fake();
|
||||
});
|
||||
|
||||
function serverValidationHeaders(): array
|
||||
{
|
||||
return ['Authorization' => 'Bearer '.test()->token];
|
||||
}
|
||||
|
||||
it('validates without installing by default', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate");
|
||||
|
||||
$response->assertCreated()->assertJson(['message' => 'Validation started.']);
|
||||
Queue::assertNotPushed(ValidateAndInstallServerJob::class);
|
||||
});
|
||||
|
||||
it('validates and installs when explicitly requested', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate", ['install' => true]);
|
||||
|
||||
$response->assertCreated()->assertJson(['message' => 'Validation and installation started.']);
|
||||
Queue::assertPushed(
|
||||
ValidateAndInstallServerJob::class,
|
||||
fn (ValidateAndInstallServerJob $job): bool => $job->server->is($this->server)
|
||||
);
|
||||
Queue::assertNotPushed(ValidateServer::class);
|
||||
});
|
||||
|
||||
it('rejects an invalid install option', function () {
|
||||
$response = $this->withHeaders(serverValidationHeaders())
|
||||
->postJson("/api/v1/servers/{$this->server->uuid}/validate", ['install' => 'yes']);
|
||||
|
||||
$response->assertUnprocessable()->assertJsonValidationErrors('install');
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\OtherController;
|
||||
use Illuminate\Routing\Route;
|
||||
|
||||
it('uses POST for state changes and keeps GET as a non-mutating compatibility response', function (string $uri) {
|
||||
$routes = collect(app('router')->getRoutes()->getRoutes())
|
||||
->filter(fn (Route $route): bool => $route->uri() === $uri);
|
||||
|
||||
$postRoute = $routes->first(fn (Route $route): bool => $route->methods() === ['POST']);
|
||||
$getRoute = $routes->first(fn (Route $route): bool => $route->methods() === ['GET', 'HEAD']);
|
||||
|
||||
expect($postRoute)->not->toBeNull()
|
||||
->and($getRoute)->not->toBeNull()
|
||||
->and($getRoute->getActionName())->toBe(OtherController::class.'@post_required');
|
||||
})->with([
|
||||
'enable API' => 'api/v1/enable',
|
||||
'disable API' => 'api/v1/disable',
|
||||
'deploy' => 'api/v1/deploy',
|
||||
'validate server' => 'api/v1/servers/{uuid}/validate',
|
||||
'start application' => 'api/v1/applications/{uuid}/start',
|
||||
'restart application' => 'api/v1/applications/{uuid}/restart',
|
||||
'stop application' => 'api/v1/applications/{uuid}/stop',
|
||||
'start database' => 'api/v1/databases/{uuid}/start',
|
||||
'restart database' => 'api/v1/databases/{uuid}/restart',
|
||||
'stop database' => 'api/v1/databases/{uuid}/stop',
|
||||
'start service' => 'api/v1/services/{uuid}/start',
|
||||
'restart service' => 'api/v1/services/{uuid}/restart',
|
||||
'stop service' => 'api/v1/services/{uuid}/stop',
|
||||
'start service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/start',
|
||||
'restart service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/restart',
|
||||
'stop service application' => 'api/v1/services/{uuid}/applications/{app_uuid}/stop',
|
||||
]);
|
||||
|
||||
it('tells GET callers to use POST without changing state', function () {
|
||||
$response = app(OtherController::class)->post_required();
|
||||
|
||||
expect($response->getStatusCode())->toBe(405)
|
||||
->and($response->headers->get('Allow'))->toBe('POST')
|
||||
->and($response->getData(true))->toBe([
|
||||
'message' => 'This endpoint has changed to a POST request.',
|
||||
]);
|
||||
});
|
||||
@@ -77,13 +77,13 @@ describe('POST /api/v1/servers', function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe('GET /api/v1/servers/{uuid}/validate', function () {
|
||||
describe('POST /api/v1/servers/{uuid}/validate', function () {
|
||||
test('read-only token cannot trigger server validation', function () {
|
||||
$token = $this->user->createToken('read-only', ['read']);
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token->plainTextToken,
|
||||
])->getJson('/api/v1/servers/fake-uuid/validate');
|
||||
])->postJson('/api/v1/servers/fake-uuid/validate');
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
||||
@@ -397,7 +397,7 @@ describe('API mutation audit logging', function () {
|
||||
|
||||
$response = $this->withHeaders([
|
||||
'Authorization' => 'Bearer '.$token,
|
||||
])->getJson('/api/v1/enable');
|
||||
])->postJson('/api/v1/enable');
|
||||
|
||||
$response->assertStatus(403);
|
||||
});
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
it('documents optional installation during server validation', function () {
|
||||
$openApi = json_decode(
|
||||
file_get_contents(__DIR__.'/../../openapi.json'),
|
||||
true,
|
||||
flags: JSON_THROW_ON_ERROR,
|
||||
);
|
||||
|
||||
$operation = $openApi['paths']['/servers/{uuid}/validate']['post'];
|
||||
$install = $operation['requestBody']['content']['application/json']['schema']['properties']['install'];
|
||||
|
||||
expect($install)
|
||||
->toMatchArray([
|
||||
'type' => 'boolean',
|
||||
'default' => false,
|
||||
])
|
||||
->and($install['description'])->toContain('restart the Docker daemon');
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
<?php
|
||||
|
||||
it('documents both supported methods for service application actions', function () {
|
||||
it('documents POST for service application actions', function () {
|
||||
$openApi = json_decode(
|
||||
file_get_contents(__DIR__.'/../../openapi.json'),
|
||||
true,
|
||||
@@ -8,7 +8,6 @@ it('documents both supported methods for service application actions', function
|
||||
);
|
||||
|
||||
$actionPaths = [
|
||||
'/services/{uuid}/applications/{app_uuid}/logs',
|
||||
'/services/{uuid}/applications/{app_uuid}/start',
|
||||
'/services/{uuid}/applications/{app_uuid}/restart',
|
||||
'/services/{uuid}/applications/{app_uuid}/stop',
|
||||
@@ -16,17 +15,16 @@ it('documents both supported methods for service application actions', function
|
||||
|
||||
foreach ($actionPaths as $path) {
|
||||
expect($openApi['paths'][$path])
|
||||
->toHaveKeys(['get', 'post'])
|
||||
->toHaveKey('post')
|
||||
->not->toHaveKey('get')
|
||||
->and($openApi['paths'][$path]['post']['responses'])
|
||||
->toHaveKeys(['200', '400', '401', '404', '501']);
|
||||
}
|
||||
|
||||
expect($openApi['paths'][$actionPaths[0]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('logs')
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[1]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[2]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message')
|
||||
->and($openApi['paths'][$actionPaths[3]]['post']['responses']['200']['content']['application/json']['schema']['properties'])
|
||||
->toHaveKey('message');
|
||||
});
|
||||
Reference in new issue
Block a user