true]); Carbon::setTestNow(Carbon::create(2026, 5, 27, 0, 1, 0, 'UTC')); Queue::fake(); $team = Team::factory()->create(); $privateKey = PrivateKey::create([ 'name' => 'Test Key', 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== -----END OPENSSH PRIVATE KEY-----', 'team_id' => $team->id, ]); $server = Server::factory()->create([ 'team_id' => $team->id, 'private_key_id' => $privateKey->id, ]); $server->settings()->update([ 'is_reachable' => true, 'is_usable' => true, 'force_disabled' => false, 'docker_cleanup_frequency' => '0 * * * *', ]); $destination = StandaloneDocker::where('server_id', $server->id)->first() ?? StandaloneDocker::factory()->create(['server_id' => $server->id]); $project = Project::factory()->create(['team_id' => $team->id]); $environment = Environment::factory()->create(['project_id' => $project->id]); $application = Application::factory()->create([ 'environment_id' => $environment->id, 'destination_id' => $destination->id, 'destination_type' => StandaloneDocker::class, 'status' => 'running', ]); ScheduledTask::factory() ->count(101) ->create([ 'team_id' => $team->id, 'application_id' => $application->id, 'frequency' => '* * * * *', 'enabled' => true, ]); (new ScheduledJobManager)->handle(); Queue::assertPushed(ScheduledTaskJob::class, 101); }); it('skips expensive dispatch for non-due schedules while seeding dedup cache', function () { config(['constants.coolify.self_hosted' => true]); Carbon::setTestNow(Carbon::create(2026, 5, 27, 0, 1, 0, 'UTC')); Queue::fake(); $application = createScheduledTaskApplication(); $task = ScheduledTask::factory()->create([ 'team_id' => $application->environment->project->team_id, 'application_id' => $application->id, 'frequency' => '0 2 * * *', 'enabled' => true, ]); (new ScheduledJobManager)->handle(); Queue::assertNotPushed(ScheduledTaskJob::class); expect(Cache::get("scheduled-task:{$task->id}"))->not->toBeNull(); }); it('dispatches the instance coolify-db backup even when its id is zero', function () { config(['constants.coolify.self_hosted' => true]); Carbon::setTestNow(Carbon::create(2026, 5, 27, 0, 1, 0, 'UTC')); Queue::fake(); $database = createScheduledBackupDatabase(); $backup = createScheduledDatabaseBackup($database, [ 'id' => 0, 'frequency' => '* * * * *', ]); expect($backup->id)->toBe(0); (new ScheduledJobManager)->handle(); Queue::assertPushed(DatabaseBackupJob::class, 1); Queue::assertPushed(DatabaseBackupJob::class, fn (DatabaseBackupJob $job) => $job->backup->id === 0); }); it('dispatches zero-id schedules and continues with positive ids', function () { config(['constants.coolify.self_hosted' => true]); Carbon::setTestNow(Carbon::create(2026, 5, 27, 0, 1, 0, 'UTC')); Queue::fake(); $application = createScheduledTaskApplication(); $database = StandalonePostgresql::create([ 'name' => 'coolify-db', 'image' => 'postgres:16-alpine', 'postgres_user' => 'postgres', 'postgres_password' => 'password', 'postgres_db' => 'postgres', 'status' => 'running', 'environment_id' => $application->environment_id, 'destination_id' => $application->destination_id, 'destination_type' => $application->destination_type, ]); $zeroIdBackup = createScheduledDatabaseBackup($database, ['id' => 0]); $positiveIdBackup = createScheduledDatabaseBackup($database); $zeroIdTask = createScheduledApplicationTask($application, ['id' => 0]); $positiveIdTask = createScheduledApplicationTask($application); expect($zeroIdBackup->id)->toBe(0) ->and($positiveIdBackup->id)->toBeGreaterThan(0) ->and($zeroIdTask->id)->toBe(0) ->and($positiveIdTask->id)->toBeGreaterThan(0); (new ScheduledJobManager)->handle(); Queue::assertPushed(DatabaseBackupJob::class, 2); Queue::assertPushed(DatabaseBackupJob::class, fn (DatabaseBackupJob $job) => $job->backup->id === 0); Queue::assertPushed(DatabaseBackupJob::class, fn (DatabaseBackupJob $job) => $job->backup->id === $positiveIdBackup->id); Queue::assertPushed(ScheduledTaskJob::class, 2); Queue::assertPushed(ScheduledTaskJob::class, fn (ScheduledTaskJob $job) => $job->task->id === 0); Queue::assertPushed(ScheduledTaskJob::class, fn (ScheduledTaskJob $job) => $job->task->id === $positiveIdTask->id); }); it('does not query relationships when constructing scheduled task jobs', function () { $application = createScheduledTaskApplication(); $task = ScheduledTask::factory()->create([ 'team_id' => $application->environment->project->team_id, 'application_id' => $application->id, 'frequency' => '* * * * *', 'enabled' => true, ])->fresh(); DB::flushQueryLog(); DB::enableQueryLog(); $job = new ScheduledTaskJob($task); expect(DB::getQueryLog())->toBeEmpty() ->and($job->queue)->toBe(crons_queue()) ->and($job->timeout)->toBe(300); }); function createScheduledTaskApplication(): Application { $team = Team::factory()->create(); $privateKey = PrivateKey::create([ 'name' => 'Test Key', 'private_key' => '-----BEGIN OPENSSH PRIVATE KEY----- b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA== -----END OPENSSH PRIVATE KEY-----', 'team_id' => $team->id, ]); $server = Server::factory()->create([ 'team_id' => $team->id, 'private_key_id' => $privateKey->id, ]); $server->settings()->update([ 'is_reachable' => true, 'is_usable' => true, 'force_disabled' => false, 'docker_cleanup_frequency' => '0 * * * *', ]); $destination = StandaloneDocker::where('server_id', $server->id)->first() ?? StandaloneDocker::factory()->create(['server_id' => $server->id]); $project = Project::factory()->create(['team_id' => $team->id]); $environment = Environment::factory()->create(['project_id' => $project->id]); return Application::factory()->create([ 'environment_id' => $environment->id, 'destination_id' => $destination->id, 'destination_type' => StandaloneDocker::class, 'status' => 'running', ]); } function createScheduledBackupDatabase(): StandalonePostgresql { $application = createScheduledTaskApplication(); return StandalonePostgresql::create([ 'name' => 'coolify-db', 'image' => 'postgres:16-alpine', 'postgres_user' => 'postgres', 'postgres_password' => 'password', 'postgres_db' => 'postgres', 'status' => 'running', 'environment_id' => $application->environment_id, 'destination_id' => $application->destination_id, 'destination_type' => $application->destination_type, ]); } function createScheduledDatabaseBackup(StandalonePostgresql $database, array $overrides = []): ScheduledDatabaseBackup { $backup = new ScheduledDatabaseBackup; $backup->forceFill(array_merge([ 'enabled' => true, 'save_s3' => false, 'frequency' => '* * * * *', 'database_id' => $database->id, 'database_type' => $database->getMorphClass(), 'team_id' => $database->environment->project->team_id, ], $overrides)); $backup->save(); return $backup->fresh(); } function createScheduledApplicationTask(Application $application, array $overrides = []): ScheduledTask { $task = new ScheduledTask; $task->forceFill(array_merge([ 'name' => 'scheduled-task', 'command' => 'echo hello', 'frequency' => '* * * * *', 'timeout' => 300, 'enabled' => true, 'team_id' => $application->environment->project->team_id, 'application_id' => $application->id, ], $overrides)); $task->save(); return $task->fresh(); }