1115 lines
44 KiB
PHP
1115 lines
44 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
use App\Models\Application;
|
||
|
|
use App\Models\ApplicationPreview;
|
||
|
|
use App\Models\ApplicationSetting;
|
||
|
|
use App\Models\CloudProviderToken;
|
||
|
|
use App\Models\Environment;
|
||
|
|
use App\Models\GithubApp;
|
||
|
|
use App\Models\Project;
|
||
|
|
use App\Models\ProjectSetting;
|
||
|
|
use App\Models\ScheduledDatabaseBackup;
|
||
|
|
use App\Models\ScheduledDatabaseBackupExecution;
|
||
|
|
use App\Models\ScheduledTask;
|
||
|
|
use App\Models\ScheduledTaskExecution;
|
||
|
|
use App\Models\Server;
|
||
|
|
use App\Models\ServerSetting;
|
||
|
|
use App\Models\Service;
|
||
|
|
use App\Models\ServiceApplication;
|
||
|
|
use App\Models\ServiceDatabase;
|
||
|
|
use App\Models\StandaloneClickhouse;
|
||
|
|
use App\Models\StandaloneDocker;
|
||
|
|
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\Subscription;
|
||
|
|
use App\Models\SwarmDocker;
|
||
|
|
use App\Models\Tag;
|
||
|
|
use App\Models\Team;
|
||
|
|
use App\Models\User;
|
||
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||
|
|
|
||
|
|
uses(RefreshDatabase::class);
|
||
|
|
|
||
|
|
beforeEach(function () {
|
||
|
|
$this->team = Team::factory()->create();
|
||
|
|
$this->server = Server::factory()->create(['team_id' => $this->team->id]);
|
||
|
|
$this->destination = $this->server->standaloneDockers()->firstOrFail();
|
||
|
|
$this->project = Project::factory()->create(['team_id' => $this->team->id]);
|
||
|
|
$this->environment = Environment::factory()->create(['project_id' => $this->project->id]);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates User with all fillable attributes', function () {
|
||
|
|
$user = User::create([
|
||
|
|
'name' => 'Test User',
|
||
|
|
'email' => 'fillable-test@example.com',
|
||
|
|
'password' => bcrypt('password123'),
|
||
|
|
'force_password_reset' => true,
|
||
|
|
'marketing_emails' => false,
|
||
|
|
'pending_email' => 'newemail@example.com',
|
||
|
|
'email_change_code' => 'ABC123',
|
||
|
|
'email_change_code_expires_at' => now()->addHour(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($user->exists)->toBeTrue();
|
||
|
|
expect($user->name)->toBe('Test User');
|
||
|
|
expect($user->email)->toBe('fillable-test@example.com');
|
||
|
|
expect($user->force_password_reset)->toBeTrue();
|
||
|
|
expect($user->marketing_emails)->toBeFalse();
|
||
|
|
expect($user->pending_email)->toBe('newemail@example.com');
|
||
|
|
expect($user->email_change_code)->toBe('ABC123');
|
||
|
|
expect($user->email_change_code_expires_at)->not->toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Server with all fillable attributes', function () {
|
||
|
|
$cloudToken = CloudProviderToken::create([
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'provider' => 'hetzner',
|
||
|
|
'token' => 'test-token',
|
||
|
|
'name' => 'test-cloud',
|
||
|
|
]);
|
||
|
|
|
||
|
|
$server = Server::create([
|
||
|
|
'name' => 'fillable-test-server',
|
||
|
|
'ip' => '10.0.0.99',
|
||
|
|
'port' => 2222,
|
||
|
|
'user' => 'deployer',
|
||
|
|
'description' => 'A test server with all fillable attrs',
|
||
|
|
'private_key_id' => $this->server->private_key_id,
|
||
|
|
'cloud_provider_token_id' => $cloudToken->id,
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'hetzner_server_id' => 'htz-12345',
|
||
|
|
'hetzner_server_status' => 'running',
|
||
|
|
'is_validating' => false,
|
||
|
|
'detected_traefik_version' => 'v2.10.0',
|
||
|
|
'traefik_outdated_info' => 'Up to date',
|
||
|
|
'server_metadata' => '{"region":"eu-central"}',
|
||
|
|
'ip_previous' => '10.0.0.1',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($server->exists)->toBeTrue();
|
||
|
|
expect((string) $server->name)->toBe('fillable-test-server');
|
||
|
|
expect((string) $server->ip)->toBe('10.0.0.99');
|
||
|
|
expect($server->port)->toBe(2222);
|
||
|
|
expect((string) $server->user)->toBe('deployer');
|
||
|
|
expect((string) $server->description)->toBe('A test server with all fillable attrs');
|
||
|
|
expect($server->private_key_id)->toBe($this->server->private_key_id);
|
||
|
|
expect($server->cloud_provider_token_id)->toBe($cloudToken->id);
|
||
|
|
expect($server->hetzner_server_id)->toBe('htz-12345');
|
||
|
|
expect($server->hetzner_server_status)->toBe('running');
|
||
|
|
expect($server->ip_previous)->toBe('10.0.0.1');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Project with all fillable attributes', function () {
|
||
|
|
$project = Project::create([
|
||
|
|
'name' => 'Fillable Test Project',
|
||
|
|
'description' => 'Testing all fillable attrs',
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'uuid' => 'custom-project-uuid',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($project->exists)->toBeTrue();
|
||
|
|
expect($project->name)->toBe('Fillable Test Project');
|
||
|
|
expect($project->description)->toBe('Testing all fillable attrs');
|
||
|
|
expect($project->team_id)->toBe($this->team->id);
|
||
|
|
expect($project->uuid)->toBe('custom-project-uuid');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Environment with all fillable attributes', function () {
|
||
|
|
$env = Environment::create([
|
||
|
|
'name' => 'staging',
|
||
|
|
'description' => 'Staging environment',
|
||
|
|
'project_id' => $this->project->id,
|
||
|
|
'uuid' => 'custom-env-uuid',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($env->exists)->toBeTrue();
|
||
|
|
expect($env->name)->toBe('staging');
|
||
|
|
expect($env->description)->toBe('Staging environment');
|
||
|
|
expect($env->project_id)->toBe($this->project->id);
|
||
|
|
expect($env->uuid)->toBe('custom-env-uuid');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ProjectSetting with all fillable attributes', function () {
|
||
|
|
$setting = ProjectSetting::create([
|
||
|
|
'project_id' => $this->project->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($setting->exists)->toBeTrue();
|
||
|
|
expect($setting->project_id)->toBe($this->project->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Application with all fillable attributes', function () {
|
||
|
|
$application = Application::create([
|
||
|
|
'uuid' => 'custom-app-uuid',
|
||
|
|
'name' => 'Full Fillable App',
|
||
|
|
'description' => 'App with every fillable attr set',
|
||
|
|
'fqdn' => 'https://app.example.com',
|
||
|
|
'git_repository' => 'https://github.com/coollabsio/coolify',
|
||
|
|
'git_branch' => 'main',
|
||
|
|
'git_commit_sha' => 'abc123def456',
|
||
|
|
'git_full_url' => 'https://github.com/coollabsio/coolify.git',
|
||
|
|
'docker_registry_image_name' => 'ghcr.io/coollabsio/coolify',
|
||
|
|
'docker_registry_image_tag' => 'latest',
|
||
|
|
'build_pack' => 'nixpacks',
|
||
|
|
'static_image' => 'nginx:alpine',
|
||
|
|
'install_command' => 'npm install',
|
||
|
|
'build_command' => 'npm run build',
|
||
|
|
'start_command' => 'npm start',
|
||
|
|
'ports_exposes' => '3000',
|
||
|
|
'ports_mappings' => '3000:3000',
|
||
|
|
'base_directory' => '/',
|
||
|
|
'publish_directory' => '/dist',
|
||
|
|
'health_check_enabled' => true,
|
||
|
|
'health_check_path' => '/health',
|
||
|
|
'health_check_port' => '3000',
|
||
|
|
'health_check_host' => 'localhost',
|
||
|
|
'health_check_method' => 'GET',
|
||
|
|
'health_check_return_code' => 200,
|
||
|
|
'health_check_scheme' => 'http',
|
||
|
|
'health_check_response_text' => 'ok',
|
||
|
|
'health_check_interval' => 30,
|
||
|
|
'health_check_timeout' => 5,
|
||
|
|
'health_check_retries' => 3,
|
||
|
|
'health_check_start_period' => 10,
|
||
|
|
'health_check_type' => 'http',
|
||
|
|
'health_check_command' => 'curl -f http://localhost:3000/health',
|
||
|
|
'limits_memory' => '512m',
|
||
|
|
'limits_memory_swap' => '1g',
|
||
|
|
'limits_memory_swappiness' => 60,
|
||
|
|
'limits_memory_reservation' => '256m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 1024,
|
||
|
|
'status' => 'running',
|
||
|
|
'preview_url_template' => '{{pr_id}}.{{domain}}',
|
||
|
|
'dockerfile' => 'FROM node:18\nRUN npm install',
|
||
|
|
'dockerfile_location' => '/Dockerfile',
|
||
|
|
'dockerfile_target_build' => 'production',
|
||
|
|
'custom_labels' => 'traefik.enable=true',
|
||
|
|
'custom_docker_run_options' => '--cap-add=NET_ADMIN',
|
||
|
|
'post_deployment_command' => 'php artisan migrate',
|
||
|
|
'post_deployment_command_container' => 'app',
|
||
|
|
'pre_deployment_command' => 'php artisan down',
|
||
|
|
'pre_deployment_command_container' => 'app',
|
||
|
|
'manual_webhook_secret_github' => 'gh-secret-123',
|
||
|
|
'manual_webhook_secret_gitlab' => 'gl-secret-456',
|
||
|
|
'manual_webhook_secret_bitbucket' => 'bb-secret-789',
|
||
|
|
'manual_webhook_secret_gitea' => 'gt-secret-012',
|
||
|
|
'docker_compose_location' => '/docker-compose.yml',
|
||
|
|
'docker_compose' => 'services: {}',
|
||
|
|
'docker_compose_raw' => 'services:\n app:\n image: nginx',
|
||
|
|
'docker_compose_domains' => '{"app":"https://app.example.com"}',
|
||
|
|
'docker_compose_custom_start_command' => 'docker compose up -d',
|
||
|
|
'docker_compose_custom_build_command' => 'docker compose build',
|
||
|
|
'swarm_replicas' => 3,
|
||
|
|
'swarm_placement_constraints' => 'node.role==worker',
|
||
|
|
'watch_paths' => 'src/**,package.json',
|
||
|
|
'redirect' => 'www',
|
||
|
|
'compose_parsing_version' => '2',
|
||
|
|
'custom_nginx_configuration' => 'location / { proxy_pass http://localhost:3000; }',
|
||
|
|
'custom_network_aliases' => 'app-alias',
|
||
|
|
'custom_healthcheck_found' => false,
|
||
|
|
// Note: nixpkgsarchive, connect_to_docker_network, force_domain_override,
|
||
|
|
// is_container_label_escape_enabled, use_build_server are in $fillable but
|
||
|
|
// their migration columns may not exist in the test SQLite schema yet.
|
||
|
|
'is_http_basic_auth_enabled' => false,
|
||
|
|
'http_basic_auth_username' => 'admin',
|
||
|
|
'http_basic_auth_password' => 'secret',
|
||
|
|
'config_hash' => 'sha256:abc123',
|
||
|
|
'last_online_at' => now()->subMinutes(5)->toISOString(),
|
||
|
|
'restart_count' => 2,
|
||
|
|
'last_restart_at' => now()->subHour()->toISOString(),
|
||
|
|
'last_restart_type' => 'manual',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'source_id' => null,
|
||
|
|
'source_type' => null,
|
||
|
|
'repository_project_id' => null,
|
||
|
|
'private_key_id' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($application->exists)->toBeTrue();
|
||
|
|
expect($application->uuid)->toBe('custom-app-uuid');
|
||
|
|
expect($application->name)->toBe('Full Fillable App');
|
||
|
|
expect((string) $application->git_repository)->toBe('https://github.com/coollabsio/coolify');
|
||
|
|
expect($application->build_pack)->toBe('nixpacks');
|
||
|
|
expect($application->ports_exposes)->toBe('3000');
|
||
|
|
expect($application->environment_id)->toBe($this->environment->id);
|
||
|
|
expect($application->destination_id)->toBe($this->destination->id);
|
||
|
|
expect($application->health_check_enabled)->toBeTrue();
|
||
|
|
expect($application->limits_memory)->toBe('512m');
|
||
|
|
expect($application->swarm_replicas)->toBe(3);
|
||
|
|
expect($application->restart_count)->toBe(2);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ApplicationSetting with all fillable attributes', function () {
|
||
|
|
$app = Application::create([
|
||
|
|
'name' => 'settings-test-app',
|
||
|
|
'git_repository' => 'https://github.com/test/repo',
|
||
|
|
'git_branch' => 'main',
|
||
|
|
'build_pack' => 'nixpacks',
|
||
|
|
'ports_exposes' => '3000',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
// Delete auto-created setting so we can create one with all attrs
|
||
|
|
ApplicationSetting::where('application_id', $app->id)->delete();
|
||
|
|
|
||
|
|
$setting = ApplicationSetting::create([
|
||
|
|
'application_id' => $app->id,
|
||
|
|
'is_static' => true,
|
||
|
|
'is_git_submodules_enabled' => true,
|
||
|
|
'is_git_lfs_enabled' => true,
|
||
|
|
'is_auto_deploy_enabled' => false,
|
||
|
|
'is_force_https_enabled' => true,
|
||
|
|
'is_debug_enabled' => true,
|
||
|
|
'is_preview_deployments_enabled' => false,
|
||
|
|
'is_log_drain_enabled' => true,
|
||
|
|
'is_gpu_enabled' => true,
|
||
|
|
'gpu_driver' => 'nvidia',
|
||
|
|
'gpu_count' => '2',
|
||
|
|
'gpu_device_ids' => 'GPU-abc,GPU-def',
|
||
|
|
'gpu_options' => '--gpus all',
|
||
|
|
'is_include_timestamps' => true,
|
||
|
|
'is_swarm_only_worker_nodes' => false,
|
||
|
|
'is_raw_compose_deployment_enabled' => false,
|
||
|
|
'is_build_server_enabled' => false,
|
||
|
|
'is_consistent_container_name_enabled' => true,
|
||
|
|
'is_gzip_enabled' => true,
|
||
|
|
'is_stripprefix_enabled' => true,
|
||
|
|
'connect_to_docker_network' => false,
|
||
|
|
'custom_internal_name' => 'my-custom-app',
|
||
|
|
'is_container_label_escape_enabled' => true,
|
||
|
|
'is_env_sorting_enabled' => true,
|
||
|
|
'is_container_label_readonly_enabled' => false,
|
||
|
|
'is_preserve_repository_enabled' => false,
|
||
|
|
'disable_build_cache' => false,
|
||
|
|
'is_spa' => true,
|
||
|
|
'is_git_shallow_clone_enabled' => true,
|
||
|
|
'is_pr_deployments_public_enabled' => false,
|
||
|
|
'use_build_secrets' => false,
|
||
|
|
'inject_build_args_to_dockerfile' => true,
|
||
|
|
'include_source_commit_in_build' => true,
|
||
|
|
'docker_images_to_keep' => 5,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($setting->exists)->toBeTrue();
|
||
|
|
expect($setting->application_id)->toBe($app->id);
|
||
|
|
expect($setting->is_static)->toBeTrue();
|
||
|
|
expect($setting->is_gpu_enabled)->toBeTrue();
|
||
|
|
expect($setting->gpu_driver)->toBe('nvidia');
|
||
|
|
expect($setting->custom_internal_name)->toBe('my-custom-app');
|
||
|
|
expect($setting->is_spa)->toBeTrue();
|
||
|
|
expect($setting->docker_images_to_keep)->toBe(5);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ServerSetting with all fillable attributes', function () {
|
||
|
|
// Delete auto-created setting
|
||
|
|
ServerSetting::where('server_id', $this->server->id)->delete();
|
||
|
|
|
||
|
|
$setting = ServerSetting::create([
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'is_swarm_manager' => false,
|
||
|
|
'is_jump_server' => false,
|
||
|
|
'is_build_server' => true,
|
||
|
|
'is_reachable' => true,
|
||
|
|
'is_usable' => true,
|
||
|
|
'wildcard_domain' => '*.example.com',
|
||
|
|
'is_cloudflare_tunnel' => false,
|
||
|
|
'is_logdrain_newrelic_enabled' => true,
|
||
|
|
'logdrain_newrelic_license_key' => 'nr-license-key-123',
|
||
|
|
'logdrain_newrelic_base_uri' => 'https://log-api.newrelic.com',
|
||
|
|
'is_logdrain_highlight_enabled' => false,
|
||
|
|
'logdrain_highlight_project_id' => 'hl-proj-123',
|
||
|
|
'is_logdrain_axiom_enabled' => true,
|
||
|
|
'logdrain_axiom_dataset_name' => 'coolify-logs',
|
||
|
|
'logdrain_axiom_api_key' => 'axiom-key-456',
|
||
|
|
'is_swarm_worker' => false,
|
||
|
|
'is_logdrain_custom_enabled' => false,
|
||
|
|
'logdrain_custom_config' => '{"endpoint":"https://logs.example.com"}',
|
||
|
|
'logdrain_custom_config_parser' => 'json',
|
||
|
|
'concurrent_builds' => 4,
|
||
|
|
'dynamic_timeout' => 600,
|
||
|
|
'force_disabled' => false,
|
||
|
|
'is_metrics_enabled' => true,
|
||
|
|
'generate_exact_labels' => true,
|
||
|
|
'force_docker_cleanup' => false,
|
||
|
|
'docker_cleanup_frequency' => '0 2 * * *',
|
||
|
|
'docker_cleanup_threshold' => 80,
|
||
|
|
'server_timezone' => 'UTC',
|
||
|
|
'delete_unused_volumes' => true,
|
||
|
|
'delete_unused_networks' => true,
|
||
|
|
'is_sentinel_enabled' => true,
|
||
|
|
'sentinel_token' => 'sentinel-token-789',
|
||
|
|
'sentinel_metrics_refresh_rate_seconds' => 30,
|
||
|
|
'sentinel_metrics_history_days' => 7,
|
||
|
|
'sentinel_push_interval_seconds' => 60,
|
||
|
|
'sentinel_custom_url' => 'https://sentinel.example.com',
|
||
|
|
'server_disk_usage_notification_threshold' => 90,
|
||
|
|
'is_sentinel_debug_enabled' => false,
|
||
|
|
'server_disk_usage_check_frequency' => '*/5 * * * *',
|
||
|
|
'is_terminal_enabled' => true,
|
||
|
|
'deployment_queue_limit' => 10,
|
||
|
|
'disable_application_image_retention' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($setting->exists)->toBeTrue();
|
||
|
|
expect($setting->server_id)->toBe($this->server->id);
|
||
|
|
expect($setting->is_build_server)->toBeTrue();
|
||
|
|
expect($setting->wildcard_domain)->toBe('*.example.com');
|
||
|
|
expect($setting->concurrent_builds)->toBe(4);
|
||
|
|
expect($setting->sentinel_token)->toBe('sentinel-token-789');
|
||
|
|
expect($setting->deployment_queue_limit)->toBe(10);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Service with all fillable attributes', function () {
|
||
|
|
$service = Service::create([
|
||
|
|
'uuid' => 'custom-service-uuid',
|
||
|
|
'name' => 'Full Fillable Service',
|
||
|
|
'description' => 'Service with all fillable attrs',
|
||
|
|
'docker_compose_raw' => "services:\n app:\n image: nginx",
|
||
|
|
'docker_compose' => "services:\n app:\n image: nginx",
|
||
|
|
'connect_to_docker_network' => true,
|
||
|
|
'service_type' => 'test-service',
|
||
|
|
'config_hash' => 'sha256:svc123',
|
||
|
|
'compose_parsing_version' => '2',
|
||
|
|
'is_container_label_escape_enabled' => true,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($service->exists)->toBeTrue();
|
||
|
|
expect($service->uuid)->toBe('custom-service-uuid');
|
||
|
|
expect($service->name)->toBe('Full Fillable Service');
|
||
|
|
expect($service->docker_compose_raw)->not->toBeNull();
|
||
|
|
expect($service->service_type)->toBe('test-service');
|
||
|
|
expect($service->environment_id)->toBe($this->environment->id);
|
||
|
|
expect($service->server_id)->toBe($this->server->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ApplicationPreview with all fillable attributes', function () {
|
||
|
|
$app = Application::create([
|
||
|
|
'name' => 'preview-test-app',
|
||
|
|
'git_repository' => 'https://github.com/test/repo',
|
||
|
|
'git_branch' => 'main',
|
||
|
|
'build_pack' => 'nixpacks',
|
||
|
|
'ports_exposes' => '3000',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$preview = ApplicationPreview::create([
|
||
|
|
'uuid' => 'custom-preview-uuid',
|
||
|
|
'application_id' => $app->id,
|
||
|
|
'pull_request_id' => 42,
|
||
|
|
'pull_request_html_url' => 'https://github.com/test/repo/pull/42',
|
||
|
|
'pull_request_issue_comment_id' => 12345,
|
||
|
|
'fqdn' => 'https://pr-42.app.example.com',
|
||
|
|
'status' => 'queued',
|
||
|
|
'git_type' => 'github',
|
||
|
|
'docker_compose_domains' => '{"app":"https://pr-42.example.com"}',
|
||
|
|
'docker_registry_image_tag' => 'pr-42',
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($preview->exists)->toBeTrue();
|
||
|
|
expect($preview->uuid)->toBe('custom-preview-uuid');
|
||
|
|
expect($preview->application_id)->toBe($app->id);
|
||
|
|
expect($preview->pull_request_id)->toBe(42);
|
||
|
|
expect($preview->fqdn)->toBe('https://pr-42.app.example.com');
|
||
|
|
expect($preview->git_type)->toBe('github');
|
||
|
|
expect($preview->docker_registry_image_tag)->toBe('pr-42');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ServiceApplication with all fillable attributes', function () {
|
||
|
|
$service = Service::create([
|
||
|
|
'docker_compose_raw' => 'services: {}',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$svcApp = ServiceApplication::create([
|
||
|
|
'service_id' => $service->id,
|
||
|
|
'name' => 'web',
|
||
|
|
'human_name' => 'Web Server',
|
||
|
|
'description' => 'Main web application',
|
||
|
|
'fqdn' => 'https://web.example.com',
|
||
|
|
'ports' => '80,443',
|
||
|
|
'exposes' => '80',
|
||
|
|
'status' => 'running',
|
||
|
|
'exclude_from_status' => false,
|
||
|
|
'required_fqdn' => true,
|
||
|
|
'image' => 'nginx:latest',
|
||
|
|
'is_log_drain_enabled' => true,
|
||
|
|
'is_include_timestamps' => true,
|
||
|
|
'is_gzip_enabled' => true,
|
||
|
|
'is_stripprefix_enabled' => true,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'is_migrated' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($svcApp->exists)->toBeTrue();
|
||
|
|
expect($svcApp->service_id)->toBe($service->id);
|
||
|
|
expect($svcApp->name)->toBe('web');
|
||
|
|
expect($svcApp->human_name)->toBe('Web Server');
|
||
|
|
expect($svcApp->image)->toBe('nginx:latest');
|
||
|
|
expect($svcApp->is_log_drain_enabled)->toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ServiceDatabase with all fillable attributes', function () {
|
||
|
|
$service = Service::create([
|
||
|
|
'docker_compose_raw' => 'services: {}',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$svcDb = ServiceDatabase::create([
|
||
|
|
'service_id' => $service->id,
|
||
|
|
'name' => 'postgres',
|
||
|
|
'human_name' => 'PostgreSQL',
|
||
|
|
'description' => 'Main database',
|
||
|
|
'ports' => '5432',
|
||
|
|
'exposes' => '5432',
|
||
|
|
'status' => 'running',
|
||
|
|
'exclude_from_status' => false,
|
||
|
|
'image' => 'postgres:16',
|
||
|
|
'public_port' => 15432,
|
||
|
|
'is_public' => true,
|
||
|
|
'is_log_drain_enabled' => true,
|
||
|
|
'is_include_timestamps' => true,
|
||
|
|
'is_gzip_enabled' => false,
|
||
|
|
'is_stripprefix_enabled' => false,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'is_migrated' => false,
|
||
|
|
'custom_type' => 'postgresql',
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($svcDb->exists)->toBeTrue();
|
||
|
|
expect($svcDb->service_id)->toBe($service->id);
|
||
|
|
expect($svcDb->name)->toBe('postgres');
|
||
|
|
expect($svcDb->public_port)->toBe(15432);
|
||
|
|
expect($svcDb->is_public)->toBeTrue();
|
||
|
|
expect($svcDb->custom_type)->toBe('postgresql');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandalonePostgresql with all fillable attributes', function () {
|
||
|
|
$db = StandalonePostgresql::create([
|
||
|
|
'uuid' => 'custom-pg-uuid',
|
||
|
|
'name' => 'Full Fillable Postgres',
|
||
|
|
'description' => 'PG with all attrs',
|
||
|
|
'postgres_user' => 'testuser',
|
||
|
|
'postgres_password' => 'testpass123',
|
||
|
|
'postgres_db' => 'testdb',
|
||
|
|
'postgres_initdb_args' => '--encoding=UTF8',
|
||
|
|
'postgres_host_auth_method' => 'scram-sha-256',
|
||
|
|
'postgres_conf' => 'max_connections=200',
|
||
|
|
'init_scripts' => 'CREATE TABLE test (id int);',
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'postgres:16-alpine',
|
||
|
|
'is_public' => true,
|
||
|
|
'public_port' => 25432,
|
||
|
|
'ports_mappings' => '25432:5432',
|
||
|
|
'limits_memory' => '1g',
|
||
|
|
'limits_memory_swap' => '2g',
|
||
|
|
'limits_memory_swappiness' => 50,
|
||
|
|
'limits_memory_reservation' => '512m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 1024,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 1,
|
||
|
|
'last_restart_at' => now()->subHours(6)->toISOString(),
|
||
|
|
'last_restart_type' => 'manual',
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 7200,
|
||
|
|
'enable_ssl' => true,
|
||
|
|
'ssl_mode' => 'verify-full',
|
||
|
|
'is_log_drain_enabled' => true,
|
||
|
|
'is_include_timestamps' => true,
|
||
|
|
'custom_docker_run_options' => '--shm-size=256m',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-pg-uuid');
|
||
|
|
expect($db->postgres_user)->toBe('testuser');
|
||
|
|
expect($db->postgres_db)->toBe('testdb');
|
||
|
|
expect($db->is_public)->toBeTrue();
|
||
|
|
expect($db->public_port)->toBe(25432);
|
||
|
|
expect($db->enable_ssl)->toBeTrue();
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneMysql with all fillable attributes', function () {
|
||
|
|
$db = StandaloneMysql::create([
|
||
|
|
'uuid' => 'custom-mysql-uuid',
|
||
|
|
'name' => 'Full Fillable MySQL',
|
||
|
|
'description' => 'MySQL with all attrs',
|
||
|
|
'mysql_root_password' => 'rootpass123',
|
||
|
|
'mysql_user' => 'testuser',
|
||
|
|
'mysql_password' => 'testpass123',
|
||
|
|
'mysql_database' => 'testdb',
|
||
|
|
'mysql_conf' => '[mysqld]\nmax_connections=200',
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'mysql:8.0',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 23306,
|
||
|
|
'ports_mappings' => '23306:3306',
|
||
|
|
'limits_memory' => '1g',
|
||
|
|
'limits_memory_swap' => '2g',
|
||
|
|
'limits_memory_swappiness' => 50,
|
||
|
|
'limits_memory_reservation' => '512m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 1024,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => true,
|
||
|
|
'ssl_mode' => 'REQUIRED',
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'custom_docker_run_options' => '--ulimit nofile=65535:65535',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-mysql-uuid');
|
||
|
|
expect($db->mysql_root_password)->toBe('rootpass123');
|
||
|
|
expect($db->mysql_database)->toBe('testdb');
|
||
|
|
expect($db->enable_ssl)->toBeTrue();
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneMariadb with all fillable attributes', function () {
|
||
|
|
$db = StandaloneMariadb::create([
|
||
|
|
'uuid' => 'custom-maria-uuid',
|
||
|
|
'name' => 'Full Fillable MariaDB',
|
||
|
|
'description' => 'MariaDB with all attrs',
|
||
|
|
'mariadb_root_password' => 'rootpass123',
|
||
|
|
'mariadb_user' => 'testuser',
|
||
|
|
'mariadb_password' => 'testpass123',
|
||
|
|
'mariadb_database' => 'testdb',
|
||
|
|
'mariadb_conf' => '[mysqld]\nmax_connections=200',
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'mariadb:11',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 23307,
|
||
|
|
'ports_mappings' => '23307:3306',
|
||
|
|
'limits_memory' => '1g',
|
||
|
|
'limits_memory_swap' => '2g',
|
||
|
|
'limits_memory_swappiness' => 50,
|
||
|
|
'limits_memory_reservation' => '512m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 1024,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => false,
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-maria-uuid');
|
||
|
|
expect($db->mariadb_root_password)->toBe('rootpass123');
|
||
|
|
expect($db->mariadb_database)->toBe('testdb');
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneMongodb with all fillable attributes', function () {
|
||
|
|
$db = StandaloneMongodb::create([
|
||
|
|
'uuid' => 'custom-mongo-uuid',
|
||
|
|
'name' => 'Full Fillable MongoDB',
|
||
|
|
'description' => 'MongoDB with all attrs',
|
||
|
|
'mongo_conf' => '{"storage":{"dbPath":"/data/db"}}',
|
||
|
|
'mongo_initdb_root_username' => 'mongoadmin',
|
||
|
|
'mongo_initdb_root_password' => 'mongopass123',
|
||
|
|
'mongo_initdb_database' => 'testdb',
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'mongo:7',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 27018,
|
||
|
|
'ports_mappings' => '27018:27017',
|
||
|
|
'limits_memory' => '2g',
|
||
|
|
'limits_memory_swap' => '4g',
|
||
|
|
'limits_memory_swappiness' => 60,
|
||
|
|
'limits_memory_reservation' => '1g',
|
||
|
|
'limits_cpus' => '4',
|
||
|
|
'limits_cpuset' => '0-3',
|
||
|
|
'limits_cpu_shares' => 2048,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => false,
|
||
|
|
'ssl_mode' => 'prefer',
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-mongo-uuid');
|
||
|
|
expect($db->mongo_initdb_root_username)->toBe('mongoadmin');
|
||
|
|
expect($db->mongo_initdb_database)->toBe('testdb');
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneRedis with all fillable attributes', function () {
|
||
|
|
$db = StandaloneRedis::create([
|
||
|
|
'uuid' => 'custom-redis-uuid',
|
||
|
|
'name' => 'Full Fillable Redis',
|
||
|
|
'description' => 'Redis with all attrs',
|
||
|
|
'redis_conf' => 'maxmemory 256mb\nmaxmemory-policy allkeys-lru',
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'redis:7-alpine',
|
||
|
|
'is_public' => true,
|
||
|
|
'public_port' => 26379,
|
||
|
|
'ports_mappings' => '26379:6379',
|
||
|
|
'limits_memory' => '512m',
|
||
|
|
'limits_memory_swap' => '1g',
|
||
|
|
'limits_memory_swappiness' => 30,
|
||
|
|
'limits_memory_reservation' => '256m',
|
||
|
|
'limits_cpus' => '1',
|
||
|
|
'limits_cpuset' => '0',
|
||
|
|
'limits_cpu_shares' => 512,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => false,
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-redis-uuid');
|
||
|
|
expect($db->redis_conf)->toContain('maxmemory');
|
||
|
|
expect($db->is_public)->toBeTrue();
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneKeydb with all fillable attributes', function () {
|
||
|
|
$db = StandaloneKeydb::create([
|
||
|
|
'uuid' => 'custom-keydb-uuid',
|
||
|
|
'name' => 'Full Fillable KeyDB',
|
||
|
|
'description' => 'KeyDB with all attrs',
|
||
|
|
'keydb_password' => 'keydbpass123',
|
||
|
|
'keydb_conf' => 'server-threads 4',
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'eqalpha/keydb:latest',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 26380,
|
||
|
|
'ports_mappings' => '26380:6379',
|
||
|
|
'limits_memory' => '512m',
|
||
|
|
'limits_memory_swap' => '1g',
|
||
|
|
'limits_memory_swappiness' => 30,
|
||
|
|
'limits_memory_reservation' => '256m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 512,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => false,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-keydb-uuid');
|
||
|
|
expect($db->keydb_password)->toBe('keydbpass123');
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneDragonfly with all fillable attributes', function () {
|
||
|
|
$db = StandaloneDragonfly::create([
|
||
|
|
'uuid' => 'custom-dragonfly-uuid',
|
||
|
|
'name' => 'Full Fillable Dragonfly',
|
||
|
|
'description' => 'Dragonfly with all attrs',
|
||
|
|
'dragonfly_password' => 'dragonflypass123',
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'docker.dragonflydb.io/dragonflydb/dragonfly:latest',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 26381,
|
||
|
|
'ports_mappings' => '26381:6379',
|
||
|
|
'limits_memory' => '1g',
|
||
|
|
'limits_memory_swap' => '2g',
|
||
|
|
'limits_memory_swappiness' => 30,
|
||
|
|
'limits_memory_reservation' => '512m',
|
||
|
|
'limits_cpus' => '2',
|
||
|
|
'limits_cpuset' => '0-1',
|
||
|
|
'limits_cpu_shares' => 512,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'enable_ssl' => false,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-dragonfly-uuid');
|
||
|
|
expect($db->dragonfly_password)->toBe('dragonflypass123');
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneClickhouse with all fillable attributes', function () {
|
||
|
|
$db = StandaloneClickhouse::create([
|
||
|
|
'uuid' => 'custom-ch-uuid',
|
||
|
|
'name' => 'Full Fillable ClickHouse',
|
||
|
|
'description' => 'ClickHouse with all attrs',
|
||
|
|
'clickhouse_admin_user' => 'chadmin',
|
||
|
|
'clickhouse_admin_password' => 'chpass123',
|
||
|
|
'is_log_drain_enabled' => false,
|
||
|
|
'is_include_timestamps' => false,
|
||
|
|
'status' => 'running',
|
||
|
|
'image' => 'clickhouse/clickhouse-server:latest',
|
||
|
|
'is_public' => false,
|
||
|
|
'public_port' => 28123,
|
||
|
|
'ports_mappings' => '28123:8123',
|
||
|
|
'limits_memory' => '2g',
|
||
|
|
'limits_memory_swap' => '4g',
|
||
|
|
'limits_memory_swappiness' => 30,
|
||
|
|
'limits_memory_reservation' => '1g',
|
||
|
|
'limits_cpus' => '4',
|
||
|
|
'limits_cpuset' => '0-3',
|
||
|
|
'limits_cpu_shares' => 2048,
|
||
|
|
'started_at' => now()->subDay()->toISOString(),
|
||
|
|
'restart_count' => 0,
|
||
|
|
'last_restart_at' => null,
|
||
|
|
'last_restart_type' => null,
|
||
|
|
'last_online_at' => now()->toISOString(),
|
||
|
|
'public_port_timeout' => 3600,
|
||
|
|
'custom_docker_run_options' => '',
|
||
|
|
'clickhouse_db' => 'testdb',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($db->exists)->toBeTrue();
|
||
|
|
expect($db->uuid)->toBe('custom-ch-uuid');
|
||
|
|
expect($db->clickhouse_admin_user)->toBe('chadmin');
|
||
|
|
expect($db->clickhouse_db)->toBe('testdb');
|
||
|
|
expect($db->environment_id)->toBe($this->environment->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates SwarmDocker with all fillable attributes', function () {
|
||
|
|
$swarm = SwarmDocker::create([
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'name' => 'swarm-dest',
|
||
|
|
'network' => 'coolify-swarm',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($swarm->exists)->toBeTrue();
|
||
|
|
expect($swarm->server_id)->toBe($this->server->id);
|
||
|
|
expect($swarm->name)->toBe('swarm-dest');
|
||
|
|
expect($swarm->network)->toBe('coolify-swarm');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates StandaloneDocker with all fillable attributes', function () {
|
||
|
|
$docker = StandaloneDocker::create([
|
||
|
|
'server_id' => $this->server->id,
|
||
|
|
'name' => 'standalone-dest',
|
||
|
|
'network' => 'coolify-standalone',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($docker->exists)->toBeTrue();
|
||
|
|
expect($docker->server_id)->toBe($this->server->id);
|
||
|
|
expect($docker->name)->toBe('standalone-dest');
|
||
|
|
expect($docker->network)->toBe('coolify-standalone');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ScheduledTask with all fillable attributes', function () {
|
||
|
|
$app = Application::create([
|
||
|
|
'name' => 'task-test-app',
|
||
|
|
'git_repository' => 'https://github.com/test/repo',
|
||
|
|
'git_branch' => 'main',
|
||
|
|
'build_pack' => 'nixpacks',
|
||
|
|
'ports_exposes' => '3000',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
|
||
|
|
$task = ScheduledTask::create([
|
||
|
|
'uuid' => 'custom-task-uuid',
|
||
|
|
'enabled' => true,
|
||
|
|
'name' => 'Full Fillable Task',
|
||
|
|
'command' => 'php artisan schedule:run',
|
||
|
|
'frequency' => '* * * * *',
|
||
|
|
'container' => 'app',
|
||
|
|
'timeout' => 300,
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'application_id' => $app->id,
|
||
|
|
'service_id' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($task->exists)->toBeTrue();
|
||
|
|
expect($task->uuid)->toBe('custom-task-uuid');
|
||
|
|
expect($task->name)->toBe('Full Fillable Task');
|
||
|
|
expect($task->command)->toBe('php artisan schedule:run');
|
||
|
|
expect($task->frequency)->toBe('* * * * *');
|
||
|
|
expect($task->container)->toBe('app');
|
||
|
|
expect($task->timeout)->toBe(300);
|
||
|
|
expect($task->team_id)->toBe($this->team->id);
|
||
|
|
expect($task->application_id)->toBe($app->id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ScheduledDatabaseBackup with all fillable attributes', function () {
|
||
|
|
$db = StandalonePostgresql::create([
|
||
|
|
'name' => 'backup-test-pg',
|
||
|
|
'postgres_user' => 'user',
|
||
|
|
'postgres_password' => 'pass',
|
||
|
|
'postgres_db' => 'testdb',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$backup = ScheduledDatabaseBackup::create([
|
||
|
|
'uuid' => 'custom-backup-uuid',
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'description' => 'Full fillable backup',
|
||
|
|
'enabled' => true,
|
||
|
|
'save_s3' => false,
|
||
|
|
'frequency' => '0 2 * * *',
|
||
|
|
'database_backup_retention_amount_locally' => 10,
|
||
|
|
'database_type' => $db->getMorphClass(),
|
||
|
|
'database_id' => $db->id,
|
||
|
|
's3_storage_id' => null,
|
||
|
|
'databases_to_backup' => 'testdb',
|
||
|
|
'dump_all' => false,
|
||
|
|
'database_backup_retention_days_locally' => 30,
|
||
|
|
'database_backup_retention_max_storage_locally' => 5000,
|
||
|
|
'database_backup_retention_amount_s3' => 20,
|
||
|
|
'database_backup_retention_days_s3' => 60,
|
||
|
|
'database_backup_retention_max_storage_s3' => 10000,
|
||
|
|
'timeout' => 600,
|
||
|
|
'disable_local_backup' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($backup->exists)->toBeTrue();
|
||
|
|
expect($backup->uuid)->toBe('custom-backup-uuid');
|
||
|
|
expect($backup->frequency)->toBe('0 2 * * *');
|
||
|
|
expect($backup->database_backup_retention_amount_locally)->toBe(10);
|
||
|
|
expect($backup->databases_to_backup)->toBe('testdb');
|
||
|
|
expect($backup->timeout)->toBe(600);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ScheduledDatabaseBackupExecution with all fillable attributes', function () {
|
||
|
|
$db = StandalonePostgresql::create([
|
||
|
|
'name' => 'exec-test-pg',
|
||
|
|
'postgres_user' => 'user',
|
||
|
|
'postgres_password' => 'pass',
|
||
|
|
'postgres_db' => 'testdb',
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
]);
|
||
|
|
$backup = ScheduledDatabaseBackup::create([
|
||
|
|
'frequency' => '0 2 * * *',
|
||
|
|
'database_type' => $db->getMorphClass(),
|
||
|
|
'database_id' => $db->id,
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$execution = ScheduledDatabaseBackupExecution::create([
|
||
|
|
'uuid' => 'custom-exec-uuid',
|
||
|
|
'scheduled_database_backup_id' => $backup->id,
|
||
|
|
'status' => 'success',
|
||
|
|
'message' => 'Backup completed successfully',
|
||
|
|
'size' => 1048576,
|
||
|
|
'filename' => 'backup-2026-03-31.sql.gz',
|
||
|
|
'database_name' => 'testdb',
|
||
|
|
'finished_at' => now()->toISOString(),
|
||
|
|
'local_storage_deleted' => false,
|
||
|
|
's3_storage_deleted' => false,
|
||
|
|
's3_uploaded' => false,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($execution->exists)->toBeTrue();
|
||
|
|
expect($execution->uuid)->toBe('custom-exec-uuid');
|
||
|
|
expect($execution->status)->toBe('success');
|
||
|
|
expect($execution->filename)->toBe('backup-2026-03-31.sql.gz');
|
||
|
|
expect($execution->database_name)->toBe('testdb');
|
||
|
|
expect($execution->size)->toBe(1048576);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates ScheduledTaskExecution with all fillable attributes', function () {
|
||
|
|
$app = Application::create([
|
||
|
|
'name' => 'task-exec-app',
|
||
|
|
'git_repository' => 'https://github.com/test/repo',
|
||
|
|
'git_branch' => 'main',
|
||
|
|
'build_pack' => 'nixpacks',
|
||
|
|
'ports_exposes' => '3000',
|
||
|
|
'environment_id' => $this->environment->id,
|
||
|
|
'destination_id' => $this->destination->id,
|
||
|
|
'destination_type' => $this->destination->getMorphClass(),
|
||
|
|
]);
|
||
|
|
$task = ScheduledTask::create([
|
||
|
|
'name' => 'exec-test-task',
|
||
|
|
'command' => 'echo hello',
|
||
|
|
'frequency' => '* * * * *',
|
||
|
|
'timeout' => 60,
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'application_id' => $app->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$execution = ScheduledTaskExecution::create([
|
||
|
|
'scheduled_task_id' => $task->id,
|
||
|
|
'status' => 'success',
|
||
|
|
'message' => 'Task completed successfully',
|
||
|
|
'finished_at' => now()->toISOString(),
|
||
|
|
'started_at' => now()->subMinute()->toISOString(),
|
||
|
|
'retry_count' => 0,
|
||
|
|
'duration' => 60,
|
||
|
|
'error_details' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($execution->exists)->toBeTrue();
|
||
|
|
expect($execution->scheduled_task_id)->toBe($task->id);
|
||
|
|
expect($execution->status)->toBe('success');
|
||
|
|
expect((float) $execution->duration)->toBe(60.0);
|
||
|
|
expect($execution->retry_count)->toBe(0);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates GithubApp with all fillable attributes', function () {
|
||
|
|
$githubApp = GithubApp::create([
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'private_key_id' => $this->server->private_key_id,
|
||
|
|
'name' => 'Full Fillable GH App',
|
||
|
|
'organization' => 'coollabsio',
|
||
|
|
'api_url' => 'https://api.github.com',
|
||
|
|
'html_url' => 'https://github.com',
|
||
|
|
'custom_user' => 'git',
|
||
|
|
'custom_port' => 22,
|
||
|
|
'app_id' => 12345,
|
||
|
|
'installation_id' => 67890,
|
||
|
|
'client_id' => 'Iv1.abc123',
|
||
|
|
'client_secret' => 'secret-456',
|
||
|
|
'webhook_secret' => 'whsec-789',
|
||
|
|
'is_system_wide' => false,
|
||
|
|
'is_public' => false,
|
||
|
|
'contents' => 'read',
|
||
|
|
'metadata' => 'read',
|
||
|
|
'pull_requests' => 'write',
|
||
|
|
'administration' => 'read',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($githubApp->exists)->toBeTrue();
|
||
|
|
expect($githubApp->name)->toBe('Full Fillable GH App');
|
||
|
|
expect($githubApp->organization)->toBe('coollabsio');
|
||
|
|
expect($githubApp->app_id)->toBe(12345);
|
||
|
|
expect($githubApp->installation_id)->toBe(67890);
|
||
|
|
expect($githubApp->client_id)->toBe('Iv1.abc123');
|
||
|
|
expect($githubApp->team_id)->toBe($this->team->id);
|
||
|
|
expect($githubApp->private_key_id)->toBe($this->server->private_key_id);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Subscription with all fillable attributes', function () {
|
||
|
|
$sub = Subscription::create([
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'stripe_invoice_paid' => true,
|
||
|
|
'stripe_subscription_id' => 'sub_1234567890',
|
||
|
|
'stripe_customer_id' => 'cus_1234567890',
|
||
|
|
'stripe_cancel_at_period_end' => false,
|
||
|
|
'stripe_plan_id' => 'price_1234567890',
|
||
|
|
'stripe_feedback' => 'Great service',
|
||
|
|
'stripe_comment' => 'Will renew',
|
||
|
|
'stripe_trial_already_ended' => true,
|
||
|
|
'stripe_past_due' => false,
|
||
|
|
'stripe_refunded_at' => null,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($sub->exists)->toBeTrue();
|
||
|
|
expect($sub->team_id)->toBe($this->team->id);
|
||
|
|
expect($sub->stripe_subscription_id)->toBe('sub_1234567890');
|
||
|
|
expect($sub->stripe_customer_id)->toBe('cus_1234567890');
|
||
|
|
expect($sub->stripe_plan_id)->toBe('price_1234567890');
|
||
|
|
expect($sub->stripe_invoice_paid)->toBeTrue();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates CloudProviderToken with all fillable attributes', function () {
|
||
|
|
$token = CloudProviderToken::create([
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
'provider' => 'hetzner',
|
||
|
|
'token' => 'hcloud-token-abc123',
|
||
|
|
'name' => 'My Hetzner Token',
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($token->exists)->toBeTrue();
|
||
|
|
expect($token->team_id)->toBe($this->team->id);
|
||
|
|
expect($token->provider)->toBe('hetzner');
|
||
|
|
expect($token->token)->toBe('hcloud-token-abc123');
|
||
|
|
expect($token->name)->toBe('My Hetzner Token');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates Tag with all fillable attributes', function () {
|
||
|
|
$tag = Tag::create([
|
||
|
|
'name' => 'production',
|
||
|
|
'team_id' => $this->team->id,
|
||
|
|
]);
|
||
|
|
|
||
|
|
expect($tag->exists)->toBeTrue();
|
||
|
|
expect($tag->name)->toBe('production');
|
||
|
|
expect($tag->team_id)->toBe($this->team->id);
|
||
|
|
});
|