mirror of
https://github.com/coollabsio/coolify.git
synced 2025-12-28 05:34:50 +00:00
- Introduced tests for ContainerStatusAggregator to validate status aggregation logic across various container states. - Implemented tests to ensure serverStatus accessor correctly checks server infrastructure health without being affected by container status. - Updated ExcludeFromHealthCheckTest to verify excluded status handling in various components. - Removed obsolete PushServerUpdateJobStatusAggregationTest as its functionality is covered elsewhere. - Updated version number for sentinel to 0.0.17 in versions.json.
464 lines
14 KiB
PHP
464 lines
14 KiB
PHP
<?php
|
|
|
|
use App\Services\ContainerStatusAggregator;
|
|
|
|
beforeEach(function () {
|
|
$this->aggregator = new ContainerStatusAggregator;
|
|
});
|
|
|
|
describe('aggregateFromStrings', function () {
|
|
test('returns exited:unhealthy for empty collection', function () {
|
|
$result = $this->aggregator->aggregateFromStrings(collect());
|
|
|
|
expect($result)->toBe('exited:unhealthy');
|
|
});
|
|
|
|
test('returns running:healthy for single healthy running container', function () {
|
|
$statuses = collect(['running:healthy']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:healthy');
|
|
});
|
|
|
|
test('returns running:unhealthy for single unhealthy running container', function () {
|
|
$statuses = collect(['running:unhealthy']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('returns running:unknown for single running container with unknown health', function () {
|
|
$statuses = collect(['running:unknown']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unknown');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for restarting container', function () {
|
|
$statuses = collect(['restarting']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for mixed running and exited containers', function () {
|
|
$statuses = collect(['running:healthy', 'exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns running:unhealthy when one of multiple running containers is unhealthy', function () {
|
|
$statuses = collect(['running:healthy', 'running:unhealthy', 'running:healthy']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('returns running:unknown when running containers have unknown health', function () {
|
|
$statuses = collect(['running:unknown', 'running:healthy']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unknown');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for crash loop (exited with restart count)', function () {
|
|
$statuses = collect(['exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 5);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns exited:unhealthy for exited containers without restart count', function () {
|
|
$statuses = collect(['exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 0);
|
|
|
|
expect($result)->toBe('exited:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for dead container', function () {
|
|
$statuses = collect(['dead']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for removing container', function () {
|
|
$statuses = collect(['removing']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns paused:unknown for paused container', function () {
|
|
$statuses = collect(['paused']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('paused:unknown');
|
|
});
|
|
|
|
test('returns starting:unknown for starting container', function () {
|
|
$statuses = collect(['starting']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('starting:unknown');
|
|
});
|
|
|
|
test('returns starting:unknown for created container', function () {
|
|
$statuses = collect(['created']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('starting:unknown');
|
|
});
|
|
|
|
test('handles parentheses format input (backward compatibility)', function () {
|
|
$statuses = collect(['running (healthy)', 'running (unhealthy)']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('handles mixed colon and parentheses formats', function () {
|
|
$statuses = collect(['running:healthy', 'running (unhealthy)', 'running:healthy']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('prioritizes restarting over all other states', function () {
|
|
$statuses = collect(['restarting', 'running:healthy', 'paused', 'starting']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('prioritizes crash loop over running containers', function () {
|
|
$statuses = collect(['exited', 'exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 3);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('prioritizes mixed state over healthy running', function () {
|
|
$statuses = collect(['running:healthy', 'exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 0);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('prioritizes running over paused/starting/exited', function () {
|
|
$statuses = collect(['running:healthy', 'starting', 'paused']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:healthy');
|
|
});
|
|
|
|
test('prioritizes dead over paused/starting/exited', function () {
|
|
$statuses = collect(['dead', 'paused', 'starting']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('prioritizes paused over starting/exited', function () {
|
|
$statuses = collect(['paused', 'starting', 'exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('paused:unknown');
|
|
});
|
|
|
|
test('prioritizes starting over exited', function () {
|
|
$statuses = collect(['starting', 'exited']);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('starting:unknown');
|
|
});
|
|
});
|
|
|
|
describe('aggregateFromContainers', function () {
|
|
test('returns exited:unhealthy for empty collection', function () {
|
|
$result = $this->aggregator->aggregateFromContainers(collect());
|
|
|
|
expect($result)->toBe('exited:unhealthy');
|
|
});
|
|
|
|
test('returns running:healthy for single healthy running container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => (object) ['Status' => 'healthy'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('running:healthy');
|
|
});
|
|
|
|
test('returns running:unhealthy for single unhealthy running container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => (object) ['Status' => 'unhealthy'],
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('returns running:unknown for running container without health check', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => null,
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('running:unknown');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for restarting container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'restarting',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for mixed running and exited containers', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => (object) ['Status' => 'healthy'],
|
|
],
|
|
],
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'exited',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for crash loop (exited with restart count)', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'exited',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers, maxRestartCount: 5);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns exited:unhealthy for exited containers without restart count', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'exited',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers, maxRestartCount: 0);
|
|
|
|
expect($result)->toBe('exited:unhealthy');
|
|
});
|
|
|
|
test('returns degraded:unhealthy for dead container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'dead',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('returns paused:unknown for paused container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'paused',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('paused:unknown');
|
|
});
|
|
|
|
test('returns starting:unknown for starting container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'starting',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('starting:unknown');
|
|
});
|
|
|
|
test('returns starting:unknown for created container', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'created',
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('starting:unknown');
|
|
});
|
|
|
|
test('handles multiple containers with various states', function () {
|
|
$containers = collect([
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => (object) ['Status' => 'healthy'],
|
|
],
|
|
],
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => (object) ['Status' => 'unhealthy'],
|
|
],
|
|
],
|
|
(object) [
|
|
'State' => (object) [
|
|
'Status' => 'running',
|
|
'Health' => null,
|
|
],
|
|
],
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromContainers($containers);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
});
|
|
|
|
describe('state priority enforcement', function () {
|
|
test('restarting has highest priority', function () {
|
|
$statuses = collect([
|
|
'restarting',
|
|
'running:healthy',
|
|
'dead',
|
|
'paused',
|
|
'starting',
|
|
'exited',
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('crash loop has second highest priority', function () {
|
|
$statuses = collect([
|
|
'exited',
|
|
'running:healthy',
|
|
'paused',
|
|
'starting',
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 1);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('mixed state (running + exited) has third priority', function () {
|
|
$statuses = collect([
|
|
'running:healthy',
|
|
'exited',
|
|
'paused',
|
|
'starting',
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses, maxRestartCount: 0);
|
|
|
|
expect($result)->toBe('degraded:unhealthy');
|
|
});
|
|
|
|
test('running:unhealthy has priority over running:unknown', function () {
|
|
$statuses = collect([
|
|
'running:unknown',
|
|
'running:unhealthy',
|
|
'running:healthy',
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unhealthy');
|
|
});
|
|
|
|
test('running:unknown has priority over running:healthy', function () {
|
|
$statuses = collect([
|
|
'running:unknown',
|
|
'running:healthy',
|
|
]);
|
|
|
|
$result = $this->aggregator->aggregateFromStrings($statuses);
|
|
|
|
expect($result)->toBe('running:unknown');
|
|
});
|
|
});
|