2025-11-18 22:24:11 +00:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Unit tests to verify that applications and services with all containers
|
|
|
|
|
* excluded from health checks (exclude_from_hc: true) show correct status.
|
|
|
|
|
*
|
|
|
|
|
* These tests verify the fix for the issue where services with all containers
|
2025-11-19 09:54:51 +00:00
|
|
|
* excluded would show incorrect status, causing broken UI state.
|
|
|
|
|
*
|
|
|
|
|
* The fix now returns status with :excluded suffix to show real container state
|
|
|
|
|
* while indicating monitoring is disabled (e.g., "running:excluded").
|
2025-11-18 22:24:11 +00:00
|
|
|
*/
|
2025-11-19 09:54:51 +00:00
|
|
|
it('ensures ComplexStatusCheck returns excluded status when all containers excluded', function () {
|
2025-11-18 22:24:11 +00:00
|
|
|
$complexStatusCheckFile = file_get_contents(__DIR__.'/../../app/Actions/Shared/ComplexStatusCheck.php');
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
// Check that when all containers are excluded, ComplexStatusCheck uses the trait
|
2025-11-18 22:24:11 +00:00
|
|
|
expect($complexStatusCheckFile)
|
2025-11-19 09:54:51 +00:00
|
|
|
->toContain('// If all containers are excluded, calculate status from excluded containers')
|
|
|
|
|
->toContain('// but mark it with :excluded to indicate monitoring is disabled')
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('if ($relevantContainers->isEmpty()) {')
|
|
|
|
|
->toContain('return $this->calculateExcludedStatus($containers, $excludedContainers);');
|
|
|
|
|
|
|
|
|
|
// Check that the trait uses ContainerStatusAggregator and appends :excluded suffix
|
|
|
|
|
$traitFile = file_get_contents(__DIR__.'/../../app/Traits/CalculatesExcludedStatus.php');
|
|
|
|
|
expect($traitFile)
|
|
|
|
|
->toContain('ContainerStatusAggregator')
|
|
|
|
|
->toContain('appendExcludedSuffix')
|
|
|
|
|
->toContain('$aggregator->aggregateFromContainers($excludedOnly)')
|
2025-11-19 09:54:51 +00:00
|
|
|
->toContain("return 'degraded:excluded';")
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain("return 'paused:excluded';")
|
|
|
|
|
->toContain("return 'exited:excluded';")
|
|
|
|
|
->toContain('return "$status:excluded";'); // For running:healthy:excluded
|
2025-11-18 22:24:11 +00:00
|
|
|
});
|
|
|
|
|
|
2025-11-19 09:54:51 +00:00
|
|
|
it('ensures Service model returns excluded status when all services excluded', function () {
|
2025-11-18 22:24:11 +00:00
|
|
|
$serviceModelFile = file_get_contents(__DIR__.'/../../app/Models/Service.php');
|
|
|
|
|
|
|
|
|
|
// Check that when all services are excluded from status checks,
|
2025-11-19 09:54:51 +00:00
|
|
|
// the Service model calculates real status and returns it with :excluded suffix
|
|
|
|
|
expect($serviceModelFile)
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('exclude_from_status')
|
|
|
|
|
->toContain(':excluded')
|
|
|
|
|
->toContain('CalculatesExcludedStatus');
|
2025-11-19 09:54:51 +00:00
|
|
|
});
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
it('ensures Service model returns unknown:unknown:excluded when no containers exist', function () {
|
2025-11-19 09:54:51 +00:00
|
|
|
$serviceModelFile = file_get_contents(__DIR__.'/../../app/Models/Service.php');
|
|
|
|
|
|
|
|
|
|
// Check that when a service has no applications or databases at all,
|
2025-11-20 16:31:07 +00:00
|
|
|
// the Service model returns 'unknown:unknown:excluded' instead of 'exited:unhealthy:excluded'
|
2025-11-19 09:54:51 +00:00
|
|
|
// This prevents misleading status display when containers don't exist
|
2025-11-18 22:24:11 +00:00
|
|
|
expect($serviceModelFile)
|
2025-11-19 09:54:51 +00:00
|
|
|
->toContain('// If no status was calculated at all (no containers exist), return unknown')
|
|
|
|
|
->toContain('if ($excludedStatus === null && $excludedHealth === null) {')
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain("return 'unknown:unknown:excluded';");
|
2025-11-18 22:24:11 +00:00
|
|
|
});
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
it('ensures GetContainersStatus calculates excluded status when all containers excluded', function () {
|
2025-11-18 22:24:11 +00:00
|
|
|
$getContainersStatusFile = file_get_contents(__DIR__.'/../../app/Actions/Docker/GetContainersStatus.php');
|
|
|
|
|
|
|
|
|
|
// Check that when all containers are excluded, the aggregateApplicationStatus
|
2025-11-20 16:31:07 +00:00
|
|
|
// method calculates and returns status with :excluded suffix
|
2025-11-18 22:24:11 +00:00
|
|
|
expect($getContainersStatusFile)
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('// If all containers are excluded, calculate status from excluded containers')
|
|
|
|
|
->toContain('if ($relevantStatuses->isEmpty()) {')
|
|
|
|
|
->toContain('return $this->calculateExcludedStatusFromStrings($containerStatuses);');
|
2025-11-18 22:24:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('ensures exclude_from_hc flag is properly checked in ComplexStatusCheck', function () {
|
|
|
|
|
$complexStatusCheckFile = file_get_contents(__DIR__.'/../../app/Actions/Shared/ComplexStatusCheck.php');
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
// Verify that exclude_from_hc is parsed using trait helper
|
2025-11-18 22:24:11 +00:00
|
|
|
expect($complexStatusCheckFile)
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('$excludedContainers = $this->getExcludedContainersFromDockerCompose($dockerComposeRaw);');
|
2025-11-18 22:24:11 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('ensures exclude_from_hc flag is properly checked in GetContainersStatus', function () {
|
|
|
|
|
$getContainersStatusFile = file_get_contents(__DIR__.'/../../app/Actions/Docker/GetContainersStatus.php');
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
// Verify that exclude_from_hc is parsed using trait helper
|
2025-11-18 22:24:11 +00:00
|
|
|
expect($getContainersStatusFile)
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('$excludedContainers = $this->getExcludedContainersFromDockerCompose($dockerComposeRaw);');
|
2025-11-18 22:24:11 +00:00
|
|
|
});
|
2025-11-19 09:54:51 +00:00
|
|
|
|
|
|
|
|
it('ensures UI displays excluded status correctly in status component', function () {
|
|
|
|
|
$servicesStatusFile = file_get_contents(__DIR__.'/../../resources/views/components/status/services.blade.php');
|
|
|
|
|
|
2025-11-20 16:31:07 +00:00
|
|
|
// Verify that the status component transforms :excluded suffix to (excluded) for better display
|
2025-11-19 09:54:51 +00:00
|
|
|
expect($servicesStatusFile)
|
|
|
|
|
->toContain('$isExcluded = str($complexStatus)->endsWith(\':excluded\');')
|
2025-11-20 16:31:07 +00:00
|
|
|
->toContain('$parts = explode(\':\', $complexStatus);')
|
|
|
|
|
->toContain('// Has health status: running:unhealthy:excluded → Running (unhealthy, excluded)')
|
|
|
|
|
->toContain('// No health status: exited:excluded → Exited (excluded)');
|
2025-11-19 09:54:51 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('ensures UI handles excluded status in service heading buttons', function () {
|
|
|
|
|
$headingFile = file_get_contents(__DIR__.'/../../resources/views/livewire/project/service/heading.blade.php');
|
|
|
|
|
|
|
|
|
|
// Verify that the heading properly handles running/degraded/exited status with :excluded suffix
|
|
|
|
|
// The logic should use contains() to match the base status (running, degraded, exited)
|
|
|
|
|
// which will work for both regular statuses and :excluded suffixed ones
|
|
|
|
|
expect($headingFile)
|
|
|
|
|
->toContain('str($service->status)->contains(\'running\')')
|
|
|
|
|
->toContain('str($service->status)->contains(\'degraded\')')
|
|
|
|
|
->toContain('str($service->status)->contains(\'exited\')');
|
|
|
|
|
});
|