Fix edge gateway relay binding reactivation

Reactivate existing relay binding rows when a gateway/relay pair is re-added after soft deletion, avoiding duplicate uniq_edge_gateway_binding inserts. Add regression coverage for the reactivation path.
This commit is contained in:
Jeppe B
2026-07-08 18:46:33 +02:00
parent fc6c76ad1b
commit a7181a4ab2
3 changed files with 36 additions and 1 deletions
@@ -358,6 +358,22 @@ it('persists gateway cutover relay bindings used by self-serve Shelly dispatch',
->toHaveKey('fallback_mode', edge_gateway_manager::RELAY_FALLBACK_PREFER_LOCAL)
->and($context['manager']->getDepartmentTransportMode((int)$department['id']))
->toBe(edge_gateway_manager::TRANSPORT_MODE_GATEWAY);
$context['manager']->setRelayBindings($gatewayId, [], (int)$user['id']);
$recreatedBindings = $context['manager']->setRelayBindings($gatewayId, [[
'relay_id' => 'relay-machine',
'device_id' => 'device-machine-recreated',
'local_ip' => '10.50.60.71',
'channel' => 1,
]], (int)$user['id']);
expect($recreatedBindings)->toHaveCount(1)
->and($recreatedBindings[0])->toMatchArray([
'relay_id' => 'relay-machine',
'device_id' => 'device-machine-recreated',
'local_ip' => '10.50.60.71',
'channel' => 1,
]);
} finally {
$context['cleanup']->run();
}
@@ -104,6 +104,25 @@ it('resolves relay bindings without requiring a primary department gateway first
->and($body)->toContain('heartbeatTimestamp');
});
it('reactivates soft-deleted relay bindings before inserting replacements', function (): void {
$managerSource = file_get_contents(app_path('classes/edge_gateway_manager.php'));
expect($managerSource)->not->toBeFalse();
preg_match(
'/public function setRelayBindings\(int \$gatewayId, array \$bindings, \?int \$userId = null\): array\s*\{(?P<body>.*?)\n \}\n\n \/\*\*/s',
(string)$managerSource,
$matches
);
expect($matches)->toHaveKey('body');
$body = (string)$matches['body'];
expect($body)->toContain("'gateway_id' => \$gatewayId")
->and($body)->toContain("'relay_id' => \$relayId")
->and($body)->toContain('$bindingObject->deleted_at->set(null);')
->and($body)->not->toContain("'deleted_at' => null,\n ], ['id']);");
});
it('loads relay command helpers on the manager and gateway operations on the dedicated service', function (): void {
$reflection = new ReflectionClass(edge_gateway_manager::class);
$operationServiceReflection = new ReflectionClass(\classes\edge_gateway_operation_service::class);