mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-20 02:17:09 +00:00
Merge remote-tracking branch 'origin/next' into 8649-parseaddr-bug
This commit is contained in:
commit
2ad7df2dea
63 files changed
+4608
-929
No files matched your search
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\ApplicationSetting;
|
||||
|
||||
describe('Application Rollback', function () {
|
||||
beforeEach(function () {
|
||||
$this->application = new Application;
|
||||
$this->application->forceFill([
|
||||
'uuid' => 'test-app-uuid',
|
||||
'git_commit_sha' => 'HEAD',
|
||||
]);
|
||||
|
||||
$settings = new ApplicationSetting;
|
||||
$settings->is_git_shallow_clone_enabled = false;
|
||||
$settings->is_git_submodules_enabled = false;
|
||||
$settings->is_git_lfs_enabled = false;
|
||||
$this->application->setRelation('settings', $settings);
|
||||
});
|
||||
|
||||
test('setGitImportSettings uses passed commit instead of application git_commit_sha', function () {
|
||||
$rollbackCommit = 'abc123def456abc123def456abc123def456abc1';
|
||||
|
||||
$result = $this->application->setGitImportSettings(
|
||||
deployment_uuid: 'test-uuid',
|
||||
git_clone_command: 'git clone',
|
||||
public: true,
|
||||
commit: $rollbackCommit
|
||||
);
|
||||
|
||||
expect($result)->toContain($rollbackCommit);
|
||||
});
|
||||
|
||||
test('setGitImportSettings with shallow clone fetches specific commit', function () {
|
||||
$this->application->settings->is_git_shallow_clone_enabled = true;
|
||||
|
||||
$rollbackCommit = 'abc123def456abc123def456abc123def456abc1';
|
||||
|
||||
$result = $this->application->setGitImportSettings(
|
||||
deployment_uuid: 'test-uuid',
|
||||
git_clone_command: 'git clone',
|
||||
public: true,
|
||||
commit: $rollbackCommit
|
||||
);
|
||||
|
||||
expect($result)
|
||||
->toContain('git fetch --depth=1 origin')
|
||||
->toContain($rollbackCommit);
|
||||
});
|
||||
|
||||
test('setGitImportSettings falls back to git_commit_sha when no commit passed', function () {
|
||||
$this->application->git_commit_sha = 'def789abc012def789abc012def789abc012def7';
|
||||
|
||||
$result = $this->application->setGitImportSettings(
|
||||
deployment_uuid: 'test-uuid',
|
||||
git_clone_command: 'git clone',
|
||||
public: true,
|
||||
);
|
||||
|
||||
expect($result)->toContain('def789abc012def789abc012def789abc012def7');
|
||||
});
|
||||
|
||||
test('setGitImportSettings escapes shell metacharacters in commit parameter', function () {
|
||||
$maliciousCommit = 'abc123; rm -rf /';
|
||||
|
||||
$result = $this->application->setGitImportSettings(
|
||||
deployment_uuid: 'test-uuid',
|
||||
git_clone_command: 'git clone',
|
||||
public: true,
|
||||
commit: $maliciousCommit
|
||||
);
|
||||
|
||||
// escapeshellarg wraps the value in single quotes, neutralizing metacharacters
|
||||
expect($result)
|
||||
->toContain("checkout 'abc123; rm -rf /'")
|
||||
->not->toContain('checkout abc123; rm -rf /');
|
||||
});
|
||||
|
||||
test('setGitImportSettings does not append checkout when commit is HEAD', function () {
|
||||
$result = $this->application->setGitImportSettings(
|
||||
deployment_uuid: 'test-uuid',
|
||||
git_clone_command: 'git clone',
|
||||
public: true,
|
||||
);
|
||||
|
||||
expect($result)->not->toContain('advice.detachedHead=false checkout');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,283 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->user = User::factory()->create();
|
||||
$this->team = Team::factory()->create();
|
||||
$this->team->members()->attach($this->user, ['role' => 'owner']);
|
||||
$this->application = Application::factory()->create([
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
|
||||
$this->actingAs($this->user);
|
||||
});
|
||||
|
||||
test('environment variable can be created with comment', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => 'This is a test environment variable',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBe('This is a test environment variable');
|
||||
expect($env->key)->toBe('TEST_VAR');
|
||||
expect($env->value)->toBe('test_value');
|
||||
});
|
||||
|
||||
test('environment variable comment is optional', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBeNull();
|
||||
expect($env->key)->toBe('TEST_VAR');
|
||||
});
|
||||
|
||||
test('environment variable comment can be updated', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => 'Initial comment',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
$env->comment = 'Updated comment';
|
||||
$env->save();
|
||||
|
||||
$env->refresh();
|
||||
expect($env->comment)->toBe('Updated comment');
|
||||
});
|
||||
|
||||
test('environment variable comment is preserved when updating value', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'initial_value',
|
||||
'comment' => 'Important variable for testing',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
$env->value = 'new_value';
|
||||
$env->save();
|
||||
|
||||
$env->refresh();
|
||||
expect($env->value)->toBe('new_value');
|
||||
expect($env->comment)->toBe('Important variable for testing');
|
||||
});
|
||||
|
||||
test('environment variable comment is copied to preview environment', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => 'Test comment',
|
||||
'is_preview' => false,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// The model's created() event listener automatically creates a preview version
|
||||
$previewEnv = EnvironmentVariable::where('key', 'TEST_VAR')
|
||||
->where('resourceable_id', $this->application->id)
|
||||
->where('is_preview', true)
|
||||
->first();
|
||||
|
||||
expect($previewEnv)->not->toBeNull();
|
||||
expect($previewEnv->comment)->toBe('Test comment');
|
||||
});
|
||||
|
||||
test('parseEnvFormatToArray preserves values without inline comments', function () {
|
||||
$input = "KEY1=value1\nKEY2=value2";
|
||||
$result = parseEnvFormatToArray($input);
|
||||
|
||||
expect($result)->toBe([
|
||||
'KEY1' => ['value' => 'value1', 'comment' => null],
|
||||
'KEY2' => ['value' => 'value2', 'comment' => null],
|
||||
]);
|
||||
});
|
||||
|
||||
test('developer view format does not break with comment-like values', function () {
|
||||
// Values that contain # but shouldn't be treated as comments when quoted
|
||||
$env1 = EnvironmentVariable::create([
|
||||
'key' => 'HASH_VAR',
|
||||
'value' => 'value_with_#_in_it',
|
||||
'comment' => 'Contains hash symbol',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env1->value)->toBe('value_with_#_in_it');
|
||||
expect($env1->comment)->toBe('Contains hash symbol');
|
||||
});
|
||||
|
||||
test('environment variable comment can store up to 256 characters', function () {
|
||||
$comment = str_repeat('a', 256);
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => $comment,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBe($comment);
|
||||
expect(strlen($env->comment))->toBe(256);
|
||||
});
|
||||
|
||||
test('environment variable comment cannot exceed 256 characters via Livewire', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
$longComment = str_repeat('a', 257);
|
||||
|
||||
Livewire::test(\App\Livewire\Project\Shared\EnvironmentVariable\Show::class, ['env' => $env, 'type' => 'application'])
|
||||
->set('comment', $longComment)
|
||||
->call('submit')
|
||||
->assertHasErrors(['comment' => 'max']);
|
||||
});
|
||||
|
||||
test('bulk update preserves existing comments when no inline comment provided', function () {
|
||||
// Create existing variable with a manually-entered comment
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'DATABASE_URL',
|
||||
'value' => 'postgres://old-host',
|
||||
'comment' => 'Production database',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// User switches to Developer view and pastes new value without inline comment
|
||||
$bulkContent = "DATABASE_URL=postgres://new-host\nOTHER_VAR=value";
|
||||
|
||||
Livewire::test(\App\Livewire\Project\Shared\EnvironmentVariable\All::class, [
|
||||
'resource' => $this->application,
|
||||
'type' => 'application',
|
||||
])
|
||||
->set('variables', $bulkContent)
|
||||
->call('submit');
|
||||
|
||||
// Refresh the environment variable
|
||||
$env->refresh();
|
||||
|
||||
// The value should be updated
|
||||
expect($env->value)->toBe('postgres://new-host');
|
||||
|
||||
// The manually-entered comment should be PRESERVED
|
||||
expect($env->comment)->toBe('Production database');
|
||||
});
|
||||
|
||||
test('bulk update overwrites existing comments when inline comment provided', function () {
|
||||
// Create existing variable with a comment
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'API_KEY',
|
||||
'value' => 'old-key',
|
||||
'comment' => 'Old comment',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// User pastes new value WITH inline comment
|
||||
$bulkContent = 'API_KEY=new-key #Updated production key';
|
||||
|
||||
Livewire::test(\App\Livewire\Project\Shared\EnvironmentVariable\All::class, [
|
||||
'resource' => $this->application,
|
||||
'type' => 'application',
|
||||
])
|
||||
->set('variables', $bulkContent)
|
||||
->call('submit');
|
||||
|
||||
// Refresh the environment variable
|
||||
$env->refresh();
|
||||
|
||||
// The value should be updated
|
||||
expect($env->value)->toBe('new-key');
|
||||
|
||||
// The comment should be OVERWRITTEN with the inline comment
|
||||
expect($env->comment)->toBe('Updated production key');
|
||||
});
|
||||
|
||||
test('bulk update handles mixed inline and stored comments correctly', function () {
|
||||
// Create two variables with comments
|
||||
$env1 = EnvironmentVariable::create([
|
||||
'key' => 'VAR_WITH_COMMENT',
|
||||
'value' => 'value1',
|
||||
'comment' => 'Existing comment 1',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
$env2 = EnvironmentVariable::create([
|
||||
'key' => 'VAR_WITHOUT_COMMENT',
|
||||
'value' => 'value2',
|
||||
'comment' => 'Existing comment 2',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// Bulk paste: one with inline comment, one without
|
||||
$bulkContent = "VAR_WITH_COMMENT=new_value1 #New inline comment\nVAR_WITHOUT_COMMENT=new_value2";
|
||||
|
||||
Livewire::test(\App\Livewire\Project\Shared\EnvironmentVariable\All::class, [
|
||||
'resource' => $this->application,
|
||||
'type' => 'application',
|
||||
])
|
||||
->set('variables', $bulkContent)
|
||||
->call('submit');
|
||||
|
||||
// Refresh both variables
|
||||
$env1->refresh();
|
||||
$env2->refresh();
|
||||
|
||||
// First variable: comment should be overwritten with inline comment
|
||||
expect($env1->value)->toBe('new_value1');
|
||||
expect($env1->comment)->toBe('New inline comment');
|
||||
|
||||
// Second variable: comment should be preserved
|
||||
expect($env2->value)->toBe('new_value2');
|
||||
expect($env2->comment)->toBe('Existing comment 2');
|
||||
});
|
||||
|
||||
test('bulk update creates new variables with inline comments', function () {
|
||||
// Bulk paste creates new variables, some with inline comments
|
||||
$bulkContent = "NEW_VAR1=value1 #Comment for var1\nNEW_VAR2=value2\nNEW_VAR3=value3 #Comment for var3";
|
||||
|
||||
Livewire::test(\App\Livewire\Project\Shared\EnvironmentVariable\All::class, [
|
||||
'resource' => $this->application,
|
||||
'type' => 'application',
|
||||
])
|
||||
->set('variables', $bulkContent)
|
||||
->call('submit');
|
||||
|
||||
// Check that variables were created with correct comments
|
||||
$var1 = EnvironmentVariable::where('key', 'NEW_VAR1')
|
||||
->where('resourceable_id', $this->application->id)
|
||||
->first();
|
||||
$var2 = EnvironmentVariable::where('key', 'NEW_VAR2')
|
||||
->where('resourceable_id', $this->application->id)
|
||||
->first();
|
||||
$var3 = EnvironmentVariable::where('key', 'NEW_VAR3')
|
||||
->where('resourceable_id', $this->application->id)
|
||||
->first();
|
||||
|
||||
expect($var1->value)->toBe('value1');
|
||||
expect($var1->comment)->toBe('Comment for var1');
|
||||
|
||||
expect($var2->value)->toBe('value2');
|
||||
expect($var2->comment)->toBeNull();
|
||||
|
||||
expect($var3->value)->toBe('value3');
|
||||
expect($var3->comment)->toBe('Comment for var3');
|
||||
});
|
||||
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\EnvironmentVariable;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
|
||||
beforeEach(function () {
|
||||
$this->user = User::factory()->create();
|
||||
$this->team = Team::factory()->create();
|
||||
$this->team->members()->attach($this->user, ['role' => 'owner']);
|
||||
$this->application = Application::factory()->create();
|
||||
|
||||
$this->actingAs($this->user);
|
||||
});
|
||||
|
||||
test('all fillable fields can be mass assigned', function () {
|
||||
$data = [
|
||||
'key' => 'TEST_KEY',
|
||||
'value' => 'test_value',
|
||||
'comment' => 'Test comment',
|
||||
'is_literal' => true,
|
||||
'is_multiline' => true,
|
||||
'is_preview' => false,
|
||||
'is_runtime' => true,
|
||||
'is_buildtime' => false,
|
||||
'is_shown_once' => false,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
];
|
||||
|
||||
$env = EnvironmentVariable::create($data);
|
||||
|
||||
expect($env->key)->toBe('TEST_KEY');
|
||||
expect($env->value)->toBe('test_value');
|
||||
expect($env->comment)->toBe('Test comment');
|
||||
expect($env->is_literal)->toBeTrue();
|
||||
expect($env->is_multiline)->toBeTrue();
|
||||
expect($env->is_preview)->toBeFalse();
|
||||
expect($env->is_runtime)->toBeTrue();
|
||||
expect($env->is_buildtime)->toBeFalse();
|
||||
expect($env->is_shown_once)->toBeFalse();
|
||||
expect($env->resourceable_type)->toBe(Application::class);
|
||||
expect($env->resourceable_id)->toBe($this->application->id);
|
||||
});
|
||||
|
||||
test('comment field can be mass assigned with null', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => null,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBeNull();
|
||||
});
|
||||
|
||||
test('comment field can be mass assigned with empty string', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => '',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBe('');
|
||||
});
|
||||
|
||||
test('comment field can be mass assigned with long text', function () {
|
||||
$comment = str_repeat('This is a long comment. ', 10);
|
||||
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'comment' => $comment,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->comment)->toBe($comment);
|
||||
expect(strlen($env->comment))->toBe(strlen($comment));
|
||||
});
|
||||
|
||||
test('all boolean fields default correctly when not provided', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// Boolean fields can be null or false depending on database defaults
|
||||
expect($env->is_multiline)->toBeIn([false, null]);
|
||||
expect($env->is_preview)->toBeIn([false, null]);
|
||||
expect($env->is_runtime)->toBeIn([false, null]);
|
||||
expect($env->is_buildtime)->toBeIn([false, null]);
|
||||
expect($env->is_shown_once)->toBeIn([false, null]);
|
||||
});
|
||||
|
||||
test('value field is properly encrypted when mass assigned', function () {
|
||||
$plainValue = 'secret_value_123';
|
||||
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'SECRET_KEY',
|
||||
'value' => $plainValue,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// Value should be decrypted when accessed via model
|
||||
expect($env->value)->toBe($plainValue);
|
||||
|
||||
// Verify it's actually encrypted in the database
|
||||
$rawValue = \DB::table('environment_variables')
|
||||
->where('id', $env->id)
|
||||
->value('value');
|
||||
|
||||
expect($rawValue)->not->toBe($plainValue);
|
||||
expect($rawValue)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('key field is trimmed and spaces replaced with underscores', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => ' TEST KEY WITH SPACES ',
|
||||
'value' => 'test_value',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->key)->toBe('TEST_KEY_WITH_SPACES');
|
||||
});
|
||||
|
||||
test('version field can be mass assigned', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'version' => '1.2.3',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// The booted() method sets version automatically, so it will be the current version
|
||||
expect($env->version)->not->toBeNull();
|
||||
});
|
||||
|
||||
test('mass assignment works with update method', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'initial_value',
|
||||
'comment' => 'Initial comment',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
$env->update([
|
||||
'value' => 'updated_value',
|
||||
'comment' => 'Updated comment',
|
||||
'is_literal' => true,
|
||||
]);
|
||||
|
||||
$env->refresh();
|
||||
|
||||
expect($env->value)->toBe('updated_value');
|
||||
expect($env->comment)->toBe('Updated comment');
|
||||
expect($env->is_literal)->toBeTrue();
|
||||
});
|
||||
|
||||
test('protected attributes cannot be mass assigned', function () {
|
||||
$customDate = '2020-01-01 00:00:00';
|
||||
|
||||
$env = EnvironmentVariable::create([
|
||||
'id' => 999999,
|
||||
'uuid' => 'custom-uuid',
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
'created_at' => $customDate,
|
||||
'updated_at' => $customDate,
|
||||
]);
|
||||
|
||||
// id should be auto-generated, not 999999
|
||||
expect($env->id)->not->toBe(999999);
|
||||
|
||||
// uuid should be auto-generated, not 'custom-uuid'
|
||||
expect($env->uuid)->not->toBe('custom-uuid');
|
||||
|
||||
// Timestamps should be current, not 2020
|
||||
expect($env->created_at->year)->toBe(now()->year);
|
||||
});
|
||||
|
||||
test('order field can be mass assigned', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'order' => 5,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
expect($env->order)->toBe(5);
|
||||
});
|
||||
|
||||
test('is_shared field can be mass assigned', function () {
|
||||
$env = EnvironmentVariable::create([
|
||||
'key' => 'TEST_VAR',
|
||||
'value' => 'test_value',
|
||||
'is_shared' => true,
|
||||
'resourceable_type' => Application::class,
|
||||
'resourceable_id' => $this->application->id,
|
||||
]);
|
||||
|
||||
// Note: is_shared is also computed via accessor, but can be mass assigned
|
||||
expect($env->is_shared)->not->toBeNull();
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
use App\Models\Application;
|
||||
use App\Models\Environment;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\Project;
|
||||
use App\Models\ScheduledTask;
|
||||
use App\Models\ScheduledTaskExecution;
|
||||
@@ -15,6 +16,9 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
// ApiAllowed middleware requires InstanceSettings with id=0
|
||||
InstanceSettings::create(['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']);
|
||||
@@ -25,12 +29,14 @@ beforeEach(function () {
|
||||
$this->bearerToken = $this->token->plainTextToken;
|
||||
|
||||
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->destination = StandaloneDocker::factory()->create(['server_id' => $this->server->id]);
|
||||
// Server::booted() auto-creates a StandaloneDocker, reuse it
|
||||
$this->destination = StandaloneDocker::where('server_id', $this->server->id)->first();
|
||||
// Project::booted() auto-creates a 'production' Environment, reuse it
|
||||
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||||
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||||
$this->environment = $this->project->environments()->first();
|
||||
});
|
||||
|
||||
function authHeaders($bearerToken): array
|
||||
function scheduledTaskAuthHeaders($bearerToken): array
|
||||
{
|
||||
return [
|
||||
'Authorization' => 'Bearer '.$bearerToken,
|
||||
@@ -46,7 +52,7 @@ describe('GET /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$application->uuid}/scheduled-tasks");
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -66,7 +72,7 @@ describe('GET /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'name' => 'Test Task',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$application->uuid}/scheduled-tasks");
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -75,7 +81,7 @@ describe('GET /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
});
|
||||
|
||||
test('returns 404 for unknown application uuid', function () {
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson('/api/v1/applications/nonexistent-uuid/scheduled-tasks');
|
||||
|
||||
$response->assertStatus(404);
|
||||
@@ -90,7 +96,7 @@ describe('POST /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/applications/{$application->uuid}/scheduled-tasks", [
|
||||
'name' => 'Backup',
|
||||
'command' => 'php artisan backup',
|
||||
@@ -116,7 +122,7 @@ describe('POST /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/applications/{$application->uuid}/scheduled-tasks", [
|
||||
'command' => 'echo test',
|
||||
'frequency' => '* * * * *',
|
||||
@@ -132,7 +138,7 @@ describe('POST /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/applications/{$application->uuid}/scheduled-tasks", [
|
||||
'name' => 'Test',
|
||||
'command' => 'echo test',
|
||||
@@ -150,7 +156,7 @@ describe('POST /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/applications/{$application->uuid}/scheduled-tasks", [
|
||||
'name' => 'Test',
|
||||
'command' => 'echo test',
|
||||
@@ -168,7 +174,7 @@ describe('POST /api/v1/applications/{uuid}/scheduled-tasks', function () {
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/applications/{$application->uuid}/scheduled-tasks", [
|
||||
'name' => 'Test',
|
||||
'command' => 'echo test',
|
||||
@@ -199,7 +205,7 @@ describe('PATCH /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}', functi
|
||||
'name' => 'Old Name',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/{$task->uuid}", [
|
||||
'name' => 'New Name',
|
||||
]);
|
||||
@@ -215,7 +221,7 @@ describe('PATCH /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}', functi
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->patchJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/nonexistent", [
|
||||
'name' => 'Test',
|
||||
]);
|
||||
@@ -237,7 +243,7 @@ describe('DELETE /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}', funct
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->deleteJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/{$task->uuid}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -253,7 +259,7 @@ describe('DELETE /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}', funct
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->deleteJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/nonexistent");
|
||||
|
||||
$response->assertStatus(404);
|
||||
@@ -279,7 +285,7 @@ describe('GET /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}/executions
|
||||
'message' => 'OK',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/{$task->uuid}/executions");
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -294,7 +300,7 @@ describe('GET /api/v1/applications/{uuid}/scheduled-tasks/{task_uuid}/executions
|
||||
'destination_type' => $this->destination->getMorphClass(),
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/applications/{$application->uuid}/scheduled-tasks/nonexistent/executions");
|
||||
|
||||
$response->assertStatus(404);
|
||||
@@ -316,7 +322,7 @@ describe('Service scheduled tasks API', function () {
|
||||
'name' => 'Service Task',
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->getJson("/api/v1/services/{$service->uuid}/scheduled-tasks");
|
||||
|
||||
$response->assertStatus(200);
|
||||
@@ -332,7 +338,7 @@ describe('Service scheduled tasks API', function () {
|
||||
'environment_id' => $this->environment->id,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->postJson("/api/v1/services/{$service->uuid}/scheduled-tasks", [
|
||||
'name' => 'Service Backup',
|
||||
'command' => 'pg_dump',
|
||||
@@ -356,7 +362,7 @@ describe('Service scheduled tasks API', function () {
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
|
||||
$response = $this->withHeaders(authHeaders($this->bearerToken))
|
||||
$response = $this->withHeaders(scheduledTaskAuthHeaders($this->bearerToken))
|
||||
->deleteJson("/api/v1/services/{$service->uuid}/scheduled-tasks/{$task->uuid}");
|
||||
|
||||
$response->assertStatus(200);
|
||||
|
||||
Reference in new issue
Block a user