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.
142 lines
5.4 KiB
PHP
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,
|
|
]);
|
|
});
|