Files
coolify/tests/Unit/ApplicationDeploymentFailedContainerCleanupTest.php
Andras BacsaiandClaude Opus 5.5 1bebdffaff fix(deploy): keep the healthy new container when a rolling update fails late
Laravel runs failed() on a fresh copy of the job, so the in-memory
newVersionIsHealthy flag was always false there and a failure after the
old container stopped removed the only running container. Keep the flag
in the cache, keyed by the deployment UUID.

Closes #12253

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 23:03:33 +02:00

68 lines
2.8 KiB
PHP

<?php
use App\Jobs\ApplicationDeploymentJob;
use App\Models\Application;
use App\Models\ApplicationDeploymentQueue;
use App\Models\ApplicationSetting;
use Tests\TestCase;
uses(TestCase::class);
/**
* @return array<int, array<int|string, mixed>>
*/
function runFailedDeploymentCleanup(bool $consistentContainerName, ?string $customInternalName, int $pullRequestId): array
{
$settings = new ApplicationSetting;
$settings->setRawAttributes([
'is_consistent_container_name_enabled' => $consistentContainerName,
'custom_internal_name' => $customInternalName,
]);
$application = new Application;
$application->setRawAttributes(['build_pack' => 'dockerfile']);
$application->setRelation('settings', $settings);
$queue = Mockery::mock(ApplicationDeploymentQueue::class);
$queue->shouldReceive('addLogEntry')->andReturnNull();
$job = Mockery::mock(ApplicationDeploymentJob::class)->makePartial();
$job->shouldAllowMockingProtectedMethods();
$job->shouldReceive('failDeployment')->andReturnNull();
$remoteCommands = [];
$job->shouldReceive('execute_remote_command')->andReturnUsing(function (...$commands) use (&$remoteCommands) {
array_push($remoteCommands, ...$commands);
});
$reflection = new ReflectionClass(ApplicationDeploymentJob::class);
$reflection->getProperty('application_deployment_queue')->setValue($job, $queue);
$reflection->getProperty('application')->setValue($job, $application);
$reflection->getProperty('pull_request_id')->setValue($job, $pullRequestId);
$reflection->getProperty('deployment_uuid')->setValue($job, 'cleanup-test-deployment');
$reflection->getProperty('container_name')->setValue($job, 'new-container-20261001T120000');
$job->failed(new RuntimeException('Health check failed'));
return $remoteCommands;
}
it('removes the new rolling-update container when only a custom internal name is set', function () {
$remoteCommands = runFailedDeploymentCleanup(consistentContainerName: false, customInternalName: 'my-api', pullRequestId: 0);
expect($remoteCommands)->toHaveCount(1)
->and($remoteCommands[0][0])->toContain('new-container-20261001T120000');
});
it('removes the new rolling-update container when no custom name is set', function () {
expect(runFailedDeploymentCleanup(consistentContainerName: false, customInternalName: null, pullRequestId: 0))->toHaveCount(1);
});
it('keeps the running container when consistent container names are enabled', function () {
expect(runFailedDeploymentCleanup(consistentContainerName: true, customInternalName: 'my-api', pullRequestId: 0))->toBeEmpty();
});
it('keeps the running container for pull request deployments', function () {
expect(runFailedDeploymentCleanup(consistentContainerName: false, customInternalName: 'my-api', pullRequestId: 12))->toBeEmpty();
});