2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
7 * This program and the accompanying materials are made available under the
8 * terms of the Eclipse Public License 2.0 which is available at
9 * http://www.eclipse.org/legal/epl-2.0
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.innogysmarthome.internal.handler;
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.Mockito.*;
18 import java.net.ConnectException;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.concurrent.ScheduledExecutorService;
22 import java.util.concurrent.ScheduledFuture;
24 import org.eclipse.jdt.annotation.NonNull;
25 import org.eclipse.jetty.client.HttpClient;
26 import org.junit.jupiter.api.BeforeEach;
27 import org.junit.jupiter.api.Test;
28 import org.openhab.binding.innogysmarthome.internal.InnogyBindingConstants;
29 import org.openhab.binding.innogysmarthome.internal.InnogyWebSocket;
30 import org.openhab.binding.innogysmarthome.internal.client.InnogyClient;
31 import org.openhab.binding.innogysmarthome.internal.client.entity.device.Device;
32 import org.openhab.binding.innogysmarthome.internal.client.entity.device.DeviceConfig;
33 import org.openhab.binding.innogysmarthome.internal.manager.FullDeviceManager;
34 import org.openhab.core.auth.client.oauth2.OAuthClientService;
35 import org.openhab.core.auth.client.oauth2.OAuthFactory;
36 import org.openhab.core.config.core.Configuration;
37 import org.openhab.core.thing.Bridge;
38 import org.openhab.core.thing.ThingUID;
41 * @author Sven Strohschein - Initial contribution
43 public class InnogyBridgeHandlerTest {
45 private static final int MAXIMUM_RETRY_EXECUTIONS = 10;
47 private InnogyBridgeHandlerAccessible bridgeHandler;
48 private Bridge bridgeMock;
49 private InnogyWebSocket webSocketMock;
52 public void before() throws Exception {
53 bridgeMock = mock(Bridge.class);
54 when(bridgeMock.getUID()).thenReturn(new ThingUID("innogysmarthome", "bridge"));
56 webSocketMock = mock(InnogyWebSocket.class);
58 OAuthClientService oAuthService = mock(OAuthClientService.class);
60 OAuthFactory oAuthFactoryMock = mock(OAuthFactory.class);
61 when(oAuthFactoryMock.createOAuthClientService(any(), any(), any(), any(), any(), any(), any()))
62 .thenReturn(oAuthService);
64 HttpClient httpClientMock = mock(HttpClient.class);
66 bridgeHandler = new InnogyBridgeHandlerAccessible(bridgeMock, oAuthFactoryMock, httpClientMock);
70 public void testInitializeBridgeNotAvailable() throws Exception {
71 Configuration bridgeConfig = new Configuration();
72 HashMap<String, Object> map = new HashMap<>();
73 map.put("brand", "XY");
74 bridgeConfig.setProperties(map);
76 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
78 bridgeHandler.initialize();
80 verify(webSocketMock, never()).start();
81 assertEquals(0, bridgeHandler.getDirectExecutionCount());
85 public void testInitialize() throws Exception {
86 Configuration bridgeConfig = new Configuration();
88 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
90 bridgeHandler.initialize();
92 verify(webSocketMock).start();
93 assertEquals(1, bridgeHandler.getDirectExecutionCount());
97 public void testInitializeErrorOnStartingWebSocket() throws Exception {
98 Configuration bridgeConfig = new Configuration();
100 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
102 doThrow(new RuntimeException("Test-Exception")).when(webSocketMock).start();
104 bridgeHandler.initialize();
106 verify(webSocketMock, times(MAXIMUM_RETRY_EXECUTIONS)).start();
107 assertEquals(1, bridgeHandler.getDirectExecutionCount()); // only the first execution should be without a delay
111 public void testConnectionClosed() throws Exception {
112 Configuration bridgeConfig = new Configuration();
114 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
116 bridgeHandler.initialize();
118 verify(webSocketMock).start();
119 assertEquals(1, bridgeHandler.getDirectExecutionCount());
121 bridgeHandler.connectionClosed();
123 verify(webSocketMock, times(2)).start(); // automatically restarted (with a delay)
124 assertEquals(1, bridgeHandler.getDirectExecutionCount());
126 bridgeHandler.connectionClosed();
128 verify(webSocketMock, times(3)).start(); // automatically restarted (with a delay)
129 assertEquals(1, bridgeHandler.getDirectExecutionCount());
133 public void testConnectionClosedReconnectNotPossible() throws Exception {
134 Configuration bridgeConfig = new Configuration();
136 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
138 bridgeHandler.initialize();
140 verify(webSocketMock).start();
141 assertEquals(1, bridgeHandler.getDirectExecutionCount());
143 doThrow(new ConnectException("Connection refused")).when(webSocketMock).start();
145 bridgeHandler.connectionClosed();
147 verify(webSocketMock, times(10)).start(); // automatic reconnect attempts (with a delay)
148 assertEquals(1, bridgeHandler.getDirectExecutionCount());
152 public void testOnEventDisconnect() throws Exception {
153 final String disconnectEventJSON = "{ type: \"Disconnect\" }";
155 Configuration bridgeConfig = new Configuration();
157 when(bridgeMock.getConfiguration()).thenReturn(bridgeConfig);
159 bridgeHandler.initialize();
161 verify(webSocketMock).start();
162 assertEquals(1, bridgeHandler.getDirectExecutionCount());
164 bridgeHandler.onEvent(disconnectEventJSON);
166 verify(webSocketMock, times(2)).start(); // automatically restarted (with a delay)
167 assertEquals(1, bridgeHandler.getDirectExecutionCount());
169 bridgeHandler.onEvent(disconnectEventJSON);
171 verify(webSocketMock, times(3)).start(); // automatically restarted (with a delay)
172 assertEquals(1, bridgeHandler.getDirectExecutionCount());
175 private class InnogyBridgeHandlerAccessible extends InnogyBridgeHandler {
177 private final InnogyClient innogyClientMock;
178 private final FullDeviceManager fullDeviceManagerMock;
179 private final ScheduledExecutorService schedulerMock;
180 private int executionCount;
181 private int directExecutionCount;
183 private InnogyBridgeHandlerAccessible(Bridge bridge, OAuthFactory oAuthFactory, HttpClient httpClient)
185 super(bridge, oAuthFactory, httpClient);
187 Device bridgeDevice = new Device();
188 bridgeDevice.setId("bridgeId");
189 bridgeDevice.setType(InnogyBindingConstants.DEVICE_SHC);
190 bridgeDevice.setConfig(new DeviceConfig());
192 innogyClientMock = mock(InnogyClient.class);
193 fullDeviceManagerMock = mock(FullDeviceManager.class);
194 when(fullDeviceManagerMock.getFullDevices()).thenReturn(Collections.singletonList(bridgeDevice));
196 schedulerMock = mock(ScheduledExecutorService.class);
198 doAnswer(invocationOnMock -> {
199 if (executionCount <= MAXIMUM_RETRY_EXECUTIONS) {
201 invocationOnMock.getArgument(0, Runnable.class).run();
204 }).when(schedulerMock).execute(any());
206 doAnswer(invocationOnMock -> {
207 if (executionCount <= MAXIMUM_RETRY_EXECUTIONS) {
209 long seconds = invocationOnMock.getArgument(1);
211 directExecutionCount++;
214 invocationOnMock.getArgument(0, Runnable.class).run();
216 return mock(ScheduledFuture.class);
217 }).when(schedulerMock).schedule(any(Runnable.class), anyLong(), any());
220 public int getDirectExecutionCount() {
221 return directExecutionCount;
226 FullDeviceManager createFullDeviceManager(@NonNull InnogyClient client) {
227 return fullDeviceManagerMock;
232 InnogyClient createInnogyClient(@NonNull OAuthClientService oAuthService, @NonNull HttpClient httpClient) {
233 return innogyClientMock;
238 InnogyWebSocket createWebSocket() {
239 return webSocketMock;
244 ScheduledExecutorService getScheduler() {
245 return schedulerMock;