219 lines
8.5 KiB
PHP
219 lines
8.5 KiB
PHP
<?php
|
|
|
|
app_require('classes/edge_gateway_manager.php');
|
|
app_require('classes/edge_gateway_view_cache.php');
|
|
|
|
use classes\edge_gateway_manager;
|
|
use classes\edge_gateway_view_cache;
|
|
|
|
if (!class_exists('EdgeGatewayViewCacheRedisFake')) {
|
|
class EdgeGatewayViewCacheRedisFake
|
|
{
|
|
/** @var array<string,string> */
|
|
public array $store = [];
|
|
|
|
public function get(string $key): ?string
|
|
{
|
|
return $this->store[$key] ?? null;
|
|
}
|
|
|
|
public function setEx(string $key, string $value, int $ttl): void
|
|
{
|
|
$this->store[$key] = $value;
|
|
}
|
|
|
|
public function clear_keys(string $pattern): void
|
|
{
|
|
$regex = '/^' . str_replace('\*', '.*', preg_quote($pattern, '/')) . '$/';
|
|
foreach (array_keys($this->store) as $key) {
|
|
if (preg_match($regex, $key) === 1) {
|
|
unset($this->store[$key]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
beforeEach(function (): void {
|
|
$this->oldTtl = getenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
|
|
$this->redis = new EdgeGatewayViewCacheRedisFake();
|
|
edge_gateway_view_cache::setAdapterForTests($this->redis);
|
|
});
|
|
|
|
afterEach(function (): void {
|
|
edge_gateway_view_cache::setAdapterForTests(null);
|
|
|
|
if ($this->oldTtl === false) {
|
|
putenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
|
|
return;
|
|
}
|
|
|
|
putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=' . $this->oldTtl);
|
|
});
|
|
|
|
function edgeGatewayCacheGatewayRow(
|
|
int $gatewayId,
|
|
int $departmentId,
|
|
string $status,
|
|
array $inventorySummary = ['total' => 0, 'online' => 0, 'offline' => 0],
|
|
array $bindingSummary = ['total' => 0, 'fallback_overrides' => 0],
|
|
array $fallbackSummary = ['cloud_only_relays' => 0, 'local_only_relays' => 0]
|
|
): array {
|
|
return [
|
|
'id' => $gatewayId,
|
|
'department_id' => $departmentId,
|
|
'label' => 'Gateway ' . $gatewayId,
|
|
'status' => $status,
|
|
'version_drift' => ['is_drifted' => false],
|
|
'channel_status' => ['broker' => ['connected' => $status === edge_gateway_manager::STATUS_ONLINE]],
|
|
'active_operation' => null,
|
|
'recent_operations_summary' => ['pending' => 0, 'in_progress' => 0],
|
|
'backlog_depth' => ['operations' => 0, 'commands' => 0],
|
|
'metadata' => [
|
|
'system_metrics' => [
|
|
'latency_ms' => 100,
|
|
'cpu_usage_pct' => 10,
|
|
'memory_usage_pct' => 20,
|
|
'disk_usage_pct' => 30,
|
|
],
|
|
],
|
|
'inventory_summary' => $inventorySummary,
|
|
'binding_summary' => $bindingSummary,
|
|
'fallback_summary' => $fallbackSummary,
|
|
'inventory' => [],
|
|
'bindings' => [],
|
|
'recent_commands' => [],
|
|
'audit_logs' => [],
|
|
'operations' => [],
|
|
];
|
|
}
|
|
|
|
it('uses sane ttl defaults and deterministic cache keys', function (): void {
|
|
putenv('EDGE_GATEWAY_VIEW_CACHE_TTL');
|
|
expect(edge_gateway_view_cache::getTtl())->toBe(15);
|
|
|
|
putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=25');
|
|
expect(edge_gateway_view_cache::getTtl())->toBe(25);
|
|
|
|
putenv('EDGE_GATEWAY_VIEW_CACHE_TTL=-5');
|
|
expect(edge_gateway_view_cache::getTtl())->toBe(0);
|
|
|
|
expect(edge_gateway_view_cache::listKey(null, false))->toBe('edge_gateway:view:v1:list:department:all:detail:0');
|
|
expect(edge_gateway_view_cache::detailKey(701))->toBe('edge_gateway:view:v1:detail:701');
|
|
});
|
|
|
|
it('stores and retrieves cached list and detail payloads', function (): void {
|
|
$gateway = edgeGatewayCacheGatewayRow(701, 1, edge_gateway_manager::STATUS_ONLINE, ['total' => 2, 'online' => 1, 'offline' => 1]);
|
|
$payload = [
|
|
'gateways' => [$gateway],
|
|
'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$gateway]),
|
|
];
|
|
|
|
edge_gateway_view_cache::storeListPayload(null, false, $payload, 30);
|
|
edge_gateway_view_cache::storeDetailPayload(701, $gateway, 30);
|
|
|
|
expect(edge_gateway_view_cache::getListPayload(null, false))->toBe($payload);
|
|
expect(edge_gateway_view_cache::getDetailPayload(701))->toBe($gateway);
|
|
});
|
|
|
|
it('syncs gateway snapshots into cached list payloads and refreshes fleet usage', function (): void {
|
|
$staleGateway = edgeGatewayCacheGatewayRow(
|
|
701,
|
|
1,
|
|
edge_gateway_manager::STATUS_OFFLINE,
|
|
['total' => 1, 'online' => 0, 'offline' => 1],
|
|
['total' => 1, 'fallback_overrides' => 0],
|
|
['cloud_only_relays' => 0, 'local_only_relays' => 0]
|
|
);
|
|
$otherGateway = edgeGatewayCacheGatewayRow(
|
|
702,
|
|
2,
|
|
edge_gateway_manager::STATUS_ONLINE,
|
|
['total' => 1, 'online' => 1, 'offline' => 0],
|
|
['total' => 1, 'fallback_overrides' => 1],
|
|
['cloud_only_relays' => 1, 'local_only_relays' => 0]
|
|
);
|
|
|
|
edge_gateway_view_cache::storeListPayload(null, false, [
|
|
'gateways' => [$staleGateway, $otherGateway],
|
|
'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$staleGateway, $otherGateway]),
|
|
]);
|
|
edge_gateway_view_cache::storeListPayload(1, false, [
|
|
'gateways' => [$staleGateway],
|
|
'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$staleGateway]),
|
|
]);
|
|
|
|
$freshGateway = [
|
|
'id' => 701,
|
|
'department_id' => 1,
|
|
'label' => 'Gateway 701',
|
|
'status' => edge_gateway_manager::STATUS_ONLINE,
|
|
'version_drift' => ['is_drifted' => false],
|
|
'channel_status' => ['broker' => ['connected' => true]],
|
|
'active_operation' => null,
|
|
'recent_operations_summary' => ['pending' => 0, 'in_progress' => 0],
|
|
'backlog_depth' => ['operations' => 0, 'commands' => 0],
|
|
'metadata' => [
|
|
'system_metrics' => [
|
|
'latency_ms' => 150,
|
|
'cpu_usage_pct' => 15,
|
|
'memory_usage_pct' => 25,
|
|
'disk_usage_pct' => 35,
|
|
],
|
|
],
|
|
'inventory' => [
|
|
['id' => 1, 'online' => true],
|
|
['id' => 2, 'online' => true],
|
|
],
|
|
'bindings' => [
|
|
['id' => 1, 'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL],
|
|
['id' => 2, 'fallback_mode' => edge_gateway_manager::RELAY_FALLBACK_CLOUD_ONLY],
|
|
],
|
|
'recent_commands' => [],
|
|
'audit_logs' => [],
|
|
'operations' => [],
|
|
'fallback_summary' => ['cloud_only_relays' => 1, 'local_only_relays' => 0],
|
|
];
|
|
|
|
edge_gateway_view_cache::syncGateway($freshGateway);
|
|
|
|
$allGatewaysPayload = edge_gateway_view_cache::getListPayload(null, false);
|
|
$departmentPayload = edge_gateway_view_cache::getListPayload(1, false);
|
|
$detailPayload = edge_gateway_view_cache::getDetailPayload(701);
|
|
|
|
expect($detailPayload)->not->toBeNull();
|
|
expect($detailPayload['inventory_summary'])->toBe(['total' => 2, 'online' => 2, 'offline' => 0]);
|
|
expect($detailPayload['binding_summary'])->toBe(['total' => 2, 'fallback_overrides' => 1]);
|
|
|
|
expect($allGatewaysPayload)->not->toBeNull();
|
|
expect($allGatewaysPayload['gateways'][0]['status'])->toBe(edge_gateway_manager::STATUS_ONLINE);
|
|
expect($allGatewaysPayload['gateways'][0]['inventory'])->toBe([]);
|
|
expect($allGatewaysPayload['fleet_usage']['gateways']['online'])->toBe(2);
|
|
expect($allGatewaysPayload['fleet_usage']['inventory']['online'])->toBe(3);
|
|
expect($allGatewaysPayload['fleet_usage']['bindings']['cloud_only'])->toBe(2);
|
|
|
|
expect($departmentPayload)->not->toBeNull();
|
|
expect($departmentPayload['gateways'][0]['status'])->toBe(edge_gateway_manager::STATUS_ONLINE);
|
|
expect($departmentPayload['fleet_usage']['inventory']['online'])->toBe(2);
|
|
});
|
|
|
|
it('removes deleted gateways from cached list and detail payloads', function (): void {
|
|
$gatewayA = edgeGatewayCacheGatewayRow(701, 1, edge_gateway_manager::STATUS_ONLINE);
|
|
$gatewayB = edgeGatewayCacheGatewayRow(702, 1, edge_gateway_manager::STATUS_OFFLINE);
|
|
$payload = [
|
|
'gateways' => [$gatewayA, $gatewayB],
|
|
'fleet_usage' => edge_gateway_manager::summarizeFleetUsageFromGatewayRows([$gatewayA, $gatewayB]),
|
|
];
|
|
|
|
edge_gateway_view_cache::storeListPayload(null, false, $payload);
|
|
edge_gateway_view_cache::storeListPayload(1, false, $payload);
|
|
edge_gateway_view_cache::storeDetailPayload(701, $gatewayA);
|
|
|
|
edge_gateway_view_cache::removeGateway(701, 1);
|
|
|
|
expect(edge_gateway_view_cache::getDetailPayload(701))->toBeNull();
|
|
expect(edge_gateway_view_cache::getListPayload(null, false)['gateways'])->toHaveCount(1);
|
|
expect(edge_gateway_view_cache::getListPayload(1, false)['gateways'])->toHaveCount(1);
|
|
expect(edge_gateway_view_cache::getListPayload(null, false)['fleet_usage']['gateways']['total'])->toBe(1);
|
|
});
|