Files
api/services/nginx/app/tests/Unit/Selfserve/EdgeGatewayManagerHeartbeatStatusTest.php
T
Jeppe Bundgaard d30a006457 Add delivery metadata support, preferred channels, and enhanced agent validation
This commit introduces delivery metadata tracking for gateway commands, updates, and shells. Adds preferred delivery channel handling, refined validation for edge agents, improved relay management logic, and broker presence reporting. Includes schema changes, enhanced shell handling, and test coverage.
2026-04-16 13:44:28 +02:00

142 lines
5.4 KiB
PHP

<?php
app_require('classes/edge_gateway_manager.php');
use classes\edge_gateway_manager;
it('derives effective gateway status from heartbeat freshness', function (
string $reportedStatus,
?string $lastHeartbeatAt,
string $currentTime,
string $expectedStatus
): void {
$effectiveStatus = edge_gateway_manager::resolveGatewayStatus(
$reportedStatus,
$lastHeartbeatAt,
strtotime($currentTime)
);
expect($effectiveStatus)->toBe($expectedStatus);
})->with([
'recent heartbeat stays online' => [
edge_gateway_manager::STATUS_ONLINE,
'2026-04-08 10:00:00',
'2026-04-08 10:00:59',
edge_gateway_manager::STATUS_ONLINE,
],
'late heartbeat degrades after one minute' => [
edge_gateway_manager::STATUS_ONLINE,
'2026-04-08 10:00:00',
'2026-04-08 10:01:00',
edge_gateway_manager::STATUS_DEGRADED,
],
'stale heartbeat goes offline after five minutes' => [
edge_gateway_manager::STATUS_ONLINE,
'2026-04-08 10:00:00',
'2026-04-08 10:05:00',
edge_gateway_manager::STATUS_OFFLINE,
],
'explicit offline reports stay offline even when heartbeat is fresh' => [
edge_gateway_manager::STATUS_OFFLINE,
'2026-04-08 10:04:55',
'2026-04-08 10:05:00',
edge_gateway_manager::STATUS_OFFLINE,
],
'missing heartbeat is treated as offline' => [
edge_gateway_manager::STATUS_ONLINE,
null,
'2026-04-08 10:05:00',
edge_gateway_manager::STATUS_OFFLINE,
],
]);
it('marks ready discovery as stale when the gateway heartbeat has expired', function (): void {
$gateway = edge_gateway_manager::deriveGatewayRuntimeState([
'status' => edge_gateway_manager::STATUS_ONLINE,
'last_heartbeat_at' => '2026-04-08 10:00:00',
'discovery_status' => 'READY',
], strtotime('2026-04-08 21:00:00'));
expect($gateway['status'])->toBe(edge_gateway_manager::STATUS_OFFLINE);
expect($gateway['discovery_status'])->toBe('STALE');
});
it('merges incoming heartbeat metadata with existing gateway metadata', function (): void {
$source = file_get_contents(app_path('classes/edge_gateway_manager.php'));
expect($source)->toContain("\$existingMetadata = (array)(\$gateway->metadata_json->value() ?? []);");
expect($source)->toContain("\$gateway->metadata_json->set(array_merge(\$existingMetadata, (array)(\$payload['metadata'] ?? [])));");
});
it('derives relay fallback and transport health details for degraded hybrid control planes', function (): void {
$gateway = edge_gateway_manager::deriveGatewayRuntimeState([
'status' => edge_gateway_manager::STATUS_ONLINE,
'last_heartbeat_at' => '2026-04-08 10:04:30',
'department_transport_mode' => edge_gateway_manager::TRANSPORT_MODE_GATEWAY,
'metadata' => [
'broker_presence' => [
'connected' => false,
'last_seen_at' => '2026-04-08 10:03:00',
'last_error' => 'broker timeout',
],
],
'operational_snapshot' => [
'command_backlog' => 2,
'shell_backlog' => 1,
'update_backlog' => 1,
],
'bindings' => [
[
'id' => 1,
'relay_id' => 'M-7',
'device_id' => 'shelly-plus-01',
'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL,
],
[
'id' => 2,
'relay_id' => 'M-7-CANARY',
'device_id' => 'missing-device',
'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_LOCAL_ONLY,
],
],
'inventory' => [
[
'device_id' => 'shelly-plus-01',
'online' => true,
'last_seen_at' => '2026-04-08 09:55:00',
],
],
'recent_commands' => [
[
'status' => 'COMPLETED',
'command_type' => 'DISCOVER_SHELLY',
'completed_at' => '2026-04-08 10:02:00',
],
],
'recent_shell_sessions' => [
[
'opened_at' => '2026-04-08 10:03:00',
],
],
], strtotime('2026-04-08 10:05:00'));
expect($gateway['channel_status']['command']['active'])->toBe(edge_gateway_manager::DELIVERY_CHANNEL_API);
expect($gateway['channel_status']['broker']['state'])->toBe(edge_gateway_manager::STATUS_OFFLINE);
expect($gateway['relay_health'][0]['execution_path'])->toBe('cloud');
expect($gateway['relay_health'][0]['reason'])->toBe('device_stale');
expect($gateway['relay_health'][0]['recommended_action'])->toBe('retry_discovery');
expect($gateway['relay_health'][1]['execution_path'])->toBe('local');
expect($gateway['relay_health'][1]['reason'])->toBe('device_missing');
expect($gateway['fallback_summary']['cloud_relays'])->toBe(1);
expect($gateway['fallback_summary']['local_only_relays'])->toBe(1);
expect($gateway['transport_health']['status'])->toBe(edge_gateway_manager::STATUS_DEGRADED);
expect($gateway['transport_health']['recommended_action'])->toBe('retry_discovery');
expect($gateway['last_successful_discovery_at'])->toBe('2026-04-08 10:02:00');
expect($gateway['last_successful_shell_at'])->toBe('2026-04-08 10:03:00');
expect($gateway['backlog_depth'])->toBe([
'commands' => 2,
'shell_actions' => 1,
'updates' => 1,
]);
});