feat(tests): add comprehensive tests for ContainerStatusAggregator and serverStatus accessor

- 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.
This commit is contained in:
Andras Bacsai committed 2025-11-20 17:31:07 +01:00
1 parent 70fb4c6869
commit ae6eef3cdb
23 files changed
+1589 -911

No files matched your search

@@ -1,15 +1,33 @@
@props([
'status' => 'Degraded',
])
@php
// Handle both colon format (backend) and parentheses format (from services.blade.php)
// degraded:unhealthy → Degraded (unhealthy)
// degraded (unhealthy) → degraded (unhealthy) (already formatted, display as-is)
if (str($status)->contains('(')) {
// Already in parentheses format from services.blade.php - use as-is
$displayStatus = $status;
$healthStatus = str($status)->after('(')->before(')')->trim()->value();
} elseif (str($status)->contains(':') && !str($status)->startsWith('Proxy')) {
// Colon format from backend - transform it
$parts = explode(':', $status);
$displayStatus = str($parts[0])->headline();
$healthStatus = $parts[1] ?? null;
} else {
// Simple status without health
$displayStatus = str($status)->headline();
$healthStatus = null;
}
@endphp
<div class="flex items-center" >
<x-loading wire:loading.delay.longer />
<span wire:loading.remove.delay.longer class="flex items-center">
<div class="badge badge-warning"></div>
<div class="pl-2 pr-1 text-xs font-bold dark:text-warning">
{{ str($status)->before(':')->headline() }}
</div>
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
<div class="text-xs dark:text-warning">({{ str($status)->after(':') }})</div>
<div class="pl-2 pr-1 text-xs font-bold dark:text-warning">{{ $displayStatus }}</div>
@if ($healthStatus && !str($displayStatus)->contains('('))
<div class="text-xs dark:text-warning">({{ $healthStatus }})</div>
@endif
</span>
</div>
@@ -5,9 +5,9 @@
])
@if (str($resource->status)->startsWith('running'))
<x-status.running :status="$resource->status" :title="$title" :lastDeploymentLink="$lastDeploymentLink" />
@elseif(str($resource->status)->startsWith('restarting') ||
str($resource->status)->startsWith('starting') ||
str($resource->status)->startsWith('degraded'))
@elseif(str($resource->status)->startsWith('degraded'))
<x-status.degraded :status="$resource->status" :title="$title" :lastDeploymentLink="$lastDeploymentLink" />
@elseif(str($resource->status)->startsWith('restarting') || str($resource->status)->startsWith('starting'))
<x-status.restarting :status="$resource->status" :title="$title" :lastDeploymentLink="$lastDeploymentLink" />
@else
<x-status.stopped :status="$resource->status" />
@@ -4,6 +4,26 @@
'lastDeploymentLink' => null,
'noLoading' => false,
])
@php
// Handle both colon format (backend) and parentheses format (from services.blade.php)
// starting:unknown → Starting (unknown)
// starting (unknown) → starting (unknown) (already formatted, display as-is)
if (str($status)->contains('(')) {
// Already in parentheses format from services.blade.php - use as-is
$displayStatus = $status;
$healthStatus = str($status)->after('(')->before(')')->trim()->value();
} elseif (str($status)->contains(':') && !str($status)->startsWith('Proxy')) {
// Colon format from backend - transform it
$parts = explode(':', $status);
$displayStatus = str($parts[0])->headline();
$healthStatus = $parts[1] ?? null;
} else {
// Simple status without health
$displayStatus = str($status)->headline();
$healthStatus = null;
}
@endphp
<div class="flex items-center">
@if (!$noLoading)
<x-loading wire:loading.delay.longer />
@@ -13,14 +33,14 @@
<div class="pl-2 pr-1 text-xs font-bold dark:text-warning" @if($title) title="{{$title}}" @endif>
@if ($lastDeploymentLink)
<a href="{{ $lastDeploymentLink }}" target="_blank" class="underline cursor-pointer">
{{ str($status)->before(':')->headline() }}
{{ $displayStatus }}
</a>
@else
{{ str($status)->before(':')->headline() }}
{{ $displayStatus }}
@endif
</div>
@if (!str($status)->startsWith('Proxy') && !str($status)->contains('('))
<div class="text-xs dark:text-warning">({{ str($status)->after(':') }})</div>
@if ($healthStatus && !str($displayStatus)->contains('('))
<div class="text-xs dark:text-warning">({{ $healthStatus }})</div>
@endif
</span>
</div>
@@ -1,21 +1,38 @@
@php
// Transform colon format to human-readable format for UI display
// running:healthy → Running (healthy)
// running:unhealthy:excluded → Running (unhealthy, excluded)
// exited:excluded → Exited (excluded)
$isExcluded = str($complexStatus)->endsWith(':excluded');
$displayStatus = $isExcluded ? str($complexStatus)->beforeLast(':excluded') : $complexStatus;
$parts = explode(':', $complexStatus);
if ($isExcluded) {
if (count($parts) === 3) {
// Has health status: running:unhealthy:excluded → Running (unhealthy, excluded)
$displayStatus = str($parts[0])->headline() . ' (' . $parts[1] . ', excluded)';
} else {
// No health status: exited:excluded → Exited (excluded)
$displayStatus = str($parts[0])->headline() . ' (excluded)';
}
} elseif (count($parts) >= 2 && !str($complexStatus)->startsWith('Proxy')) {
// Regular colon format: running:healthy → Running (healthy)
$displayStatus = str($parts[0])->headline() . ' (' . $parts[1] . ')';
} else {
// No transformation needed (simple status or already in parentheses format)
$displayStatus = str($complexStatus)->headline();
}
@endphp
@if (str($displayStatus)->contains('running'))
@if (str($displayStatus)->lower()->contains('running'))
<x-status.running :status="$displayStatus" />
@elseif(str($displayStatus)->contains('starting'))
@elseif(str($displayStatus)->lower()->contains('starting'))
<x-status.restarting :status="$displayStatus" />
@elseif(str($displayStatus)->contains('restarting'))
@elseif(str($displayStatus)->lower()->contains('restarting'))
<x-status.restarting :status="$displayStatus" />
@elseif(str($displayStatus)->contains('degraded'))
@elseif(str($displayStatus)->lower()->contains('degraded'))
<x-status.degraded :status="$displayStatus" />
@else
<x-status.stopped :status="$displayStatus" />
@endif
@if ($isExcluded)
<span class="text-xs text-neutral-500 dark:text-neutral-400" title="All containers excluded from health monitoring">(Monitoring Disabled)</span>
@endif
@if (!str($complexStatus)->contains('exited') && $showRefreshButton)
<button wire:loading.remove.delay.shortest wire:target="manualCheckStatus" title="Refresh Status" wire:click='manualCheckStatus'
class="mx-1 dark:hover:fill-white fill-black dark:fill-warning">
@@ -2,12 +2,35 @@
'status' => 'Stopped',
'noLoading' => false,
])
@php
// Handle both colon format (backend) and parentheses format (from services.blade.php)
// exited:unhealthy → Exited (unhealthy)
// exited (unhealthy) → exited (unhealthy) (already formatted, display as-is)
if (str($status)->contains('(')) {
// Already in parentheses format from services.blade.php - use as-is
$displayStatus = $status;
$healthStatus = str($status)->after('(')->before(')')->trim()->value();
} elseif (str($status)->contains(':')) {
// Colon format from backend - transform it
$parts = explode(':', $status);
$displayStatus = str($parts[0])->headline();
$healthStatus = $parts[1] ?? null;
} else {
// Simple status without health
$displayStatus = str($status)->headline();
$healthStatus = null;
}
@endphp
<div class="flex items-center">
@if (!$noLoading)
<x-loading wire:loading.delay.longer />
@endif
<span wire:loading.remove.delay.longer class="flex items-center">
<div class="badge badge-error "></div>
<div class="pl-2 pr-1 text-xs font-bold text-error">{{ str($status)->before(':')->headline() }}</div>
<div class="pl-2 pr-1 text-xs font-bold text-error">{{ $displayStatus }}</div>
@if ($healthStatus && !str($displayStatus)->contains('('))
<div class="text-xs text-error">({{ $healthStatus }})</div>
@endif
</span>
</div>
@@ -30,15 +30,15 @@
@endif
<a class="menu-item flex items-center gap-2" wire:current.exact="menu-item-active"
href="{{ route('project.application.servers', ['project_uuid' => $project->uuid, 'environment_uuid' => $environment->uuid, 'application_uuid' => $application->uuid]) }}">Servers
@if (str($application->status)->contains('degraded'))
<span title="Some servers are unavailable">
@if ($application->server_status == false)
<span title="One or more servers are unreachable or misconfigured.">
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
</svg>
</span>
@elseif ($application->server_status == false)
<span title="The underlying server(s) has problems.">
@elseif ($application->additional_servers()->exists() && str($application->status)->contains('degraded'))
<span title="Application is in degraded state across multiple servers.">
<svg class="w-4 h-4 text-error" viewBox="0 0 256 256" xmlns="http://www.w3.org/2000/svg">
<path fill="currentColor"
d="M240.26 186.1L152.81 34.23a28.74 28.74 0 0 0-49.62 0L15.74 186.1a27.45 27.45 0 0 0 0 27.71A28.31 28.31 0 0 0 40.55 228h174.9a28.31 28.31 0 0 0 24.79-14.19a27.45 27.45 0 0 0 .02-27.71m-20.8 15.7a4.46 4.46 0 0 1-4 2.2H40.55a4.46 4.46 0 0 1-4-2.2a3.56 3.56 0 0 1 0-3.73L124 46.2a4.77 4.77 0 0 1 8 0l87.44 151.87a3.56 3.56 0 0 1 .02 3.73M116 136v-32a12 12 0 0 1 24 0v32a12 12 0 0 1-24 0m28 40a16 16 0 1 1-16-16a16 16 0 0 1 16 16" />
@@ -118,7 +118,7 @@
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
<template x-if="item.server_status == false">
<div class="px-4 text-xs font-bold text-error">The underlying server has problems
<div class="px-4 text-xs font-bold text-error">Server is unreachable or misconfigured
</div>
</template>
</div>
@@ -167,7 +167,7 @@
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
<template x-if="item.server_status == false">
<div class="px-4 text-xs font-bold text-error">The underlying server has problems
<div class="px-4 text-xs font-bold text-error">Server is unreachable or misconfigured
</div>
</template>
</div>
@@ -216,7 +216,7 @@
<div class="max-w-full px-4 truncate box-description" x-text="item.description"></div>
<div class="max-w-full px-4 truncate box-description" x-text="item.fqdn"></div>
<template x-if="item.server_status == false">
<div class="px-4 text-xs font-bold text-error">The underlying server has problems
<div class="px-4 text-xs font-bold text-error">Server is unreachable or misconfigured
</div>
</template>
</div>
@@ -90,7 +90,31 @@
@endcan
</span>
@endif
<div class="pt-2 text-xs">{{ $application->status }}</div>
@php
// Transform colon format to human-readable format
// running:healthy → Running (healthy)
// running:unhealthy:excluded → Running (unhealthy, excluded)
$appStatus = $application->status;
$isExcluded = str($appStatus)->endsWith(':excluded');
$parts = explode(':', $appStatus);
if ($isExcluded) {
if (count($parts) === 3) {
// Has health status: running:unhealthy:excluded → Running (unhealthy, excluded)
$appStatus = str($parts[0])->headline() . ' (' . $parts[1] . ', excluded)';
} else {
// No health status: exited:excluded → Exited (excluded)
$appStatus = str($parts[0])->headline() . ' (excluded)';
}
} elseif (count($parts) >= 2 && !str($appStatus)->startsWith('Proxy')) {
// Regular colon format: running:healthy → Running (healthy)
$appStatus = str($parts[0])->headline() . ' (' . $parts[1] . ')';
} else {
// Simple status or already in parentheses format
$appStatus = str($appStatus)->headline();
}
@endphp
<div class="pt-2 text-xs">{{ $appStatus }}</div>
</div>
<div class="flex items-center px-4">
<a class="mx-4 text-xs font-bold hover:underline"
@@ -139,7 +163,31 @@
@if ($database->description)
<span class="text-xs">{{ Str::limit($database->description, 60) }}</span>
@endif
<div class="text-xs">{{ $database->status }}</div>
@php
// Transform colon format to human-readable format
// running:healthy → Running (healthy)
// running:unhealthy:excluded → Running (unhealthy, excluded)
$dbStatus = $database->status;
$isExcluded = str($dbStatus)->endsWith(':excluded');
$parts = explode(':', $dbStatus);
if ($isExcluded) {
if (count($parts) === 3) {
// Has health status: running:unhealthy:excluded → Running (unhealthy, excluded)
$dbStatus = str($parts[0])->headline() . ' (' . $parts[1] . ', excluded)';
} else {
// No health status: exited:excluded → Exited (excluded)
$dbStatus = str($parts[0])->headline() . ' (excluded)';
}
} elseif (count($parts) >= 2 && !str($dbStatus)->startsWith('Proxy')) {
// Regular colon format: running:healthy → Running (healthy)
$dbStatus = str($parts[0])->headline() . ' (' . $parts[1] . ')';
} else {
// Simple status or already in parentheses format
$dbStatus = str($dbStatus)->headline();
}
@endphp
<div class="text-xs">{{ $dbStatus }}</div>
</div>
<div class="flex items-center px-4">
@if ($database->isBackupSolutionAvailable() || $database->is_migrated)