]> git.basschouten.com Git - openhab-addons.git/blob
ef46aad7dfd9673ed029699a1e23e6c7b8cf3e09
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
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
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.hue.internal.handler;
14
15 import static org.eclipse.jdt.annotation.Checks.requireNonNull;
16 import static org.hamcrest.CoreMatchers.equalTo;
17 import static org.hamcrest.MatcherAssert.assertThat;
18 import static org.junit.jupiter.api.Assertions.*;
19 import static org.mockito.Mockito.mock;
20 import static org.openhab.binding.hue.internal.HueBindingConstants.*;
21
22 import java.io.IOException;
23 import java.lang.reflect.Field;
24 import java.util.concurrent.ScheduledExecutorService;
25
26 import org.eclipse.jdt.annotation.NonNullByDefault;
27 import org.eclipse.jetty.client.HttpClient;
28 import org.junit.jupiter.api.BeforeEach;
29 import org.junit.jupiter.api.Test;
30 import org.openhab.binding.hue.internal.AbstractHueOSGiTestParent;
31 import org.openhab.binding.hue.internal.config.HueBridgeConfig;
32 import org.openhab.binding.hue.internal.connection.HueBridge;
33 import org.openhab.binding.hue.internal.exceptions.ApiException;
34 import org.openhab.binding.hue.internal.exceptions.LinkButtonException;
35 import org.openhab.binding.hue.internal.exceptions.UnauthorizedException;
36 import org.openhab.core.common.ThreadPoolManager;
37 import org.openhab.core.config.core.Configuration;
38 import org.openhab.core.config.core.status.ConfigStatusMessage;
39 import org.openhab.core.thing.Bridge;
40 import org.openhab.core.thing.Thing;
41 import org.openhab.core.thing.ThingRegistry;
42 import org.openhab.core.thing.ThingStatus;
43 import org.openhab.core.thing.ThingStatusDetail;
44 import org.openhab.core.thing.ThingTypeUID;
45 import org.openhab.core.thing.ThingUID;
46
47 /**
48  * Tests for {@link HueBridgeHandler}.
49  *
50  * @author Oliver Libutzki - Initial contribution
51  * @author Michael Grammling - Initial contribution
52  * @author Denis Dudnik - switched to internally integrated source of Jue library
53  */
54 @NonNullByDefault
55 public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
56
57     private static final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "bridge");
58     private static final String TEST_USER_NAME = "eshTestUser";
59     private static final String DUMMY_HOST = "1.2.3.4";
60
61     private @NonNullByDefault({}) ThingRegistry thingRegistry;
62     private @NonNullByDefault({}) ScheduledExecutorService scheduler;
63
64     @BeforeEach
65     public void setUp() {
66         registerVolatileStorageService();
67         thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
68         assertNotNull(thingRegistry);
69
70         scheduler = ThreadPoolManager.getScheduledPool("hueBridgeTest");
71     }
72
73     @Test
74     public void assertThatANewUserIsAddedToConfigIfNotExistingYet() {
75         Configuration configuration = new Configuration();
76         configuration.put(HOST, DUMMY_HOST);
77         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
78         Bridge bridge = createBridgeThing(configuration);
79
80         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
81         hueBridgeHandler.thingUpdated(bridge);
82
83         injectBridge(hueBridgeHandler,
84                 new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
85                     @Override
86                     public String link(String deviceType) throws IOException, ApiException {
87                         return TEST_USER_NAME;
88                     }
89                 });
90
91         hueBridgeHandler.onNotAuthenticated();
92
93         assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
94     }
95
96     @Test
97     public void assertThatAnExistingUserIsUsedIfAuthenticationWasSuccessful() {
98         Configuration configuration = new Configuration();
99         configuration.put(HOST, DUMMY_HOST);
100         configuration.put(USER_NAME, TEST_USER_NAME);
101         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
102         Bridge bridge = createBridgeThing(configuration);
103
104         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
105         hueBridgeHandler.thingUpdated(bridge);
106
107         injectBridge(hueBridgeHandler,
108                 new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
109                     @Override
110                     public void authenticate(String userName) throws IOException, ApiException {
111                     }
112                 });
113
114         hueBridgeHandler.onNotAuthenticated();
115
116         assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
117     }
118
119     @Test
120     public void assertCorrectStatusIfAuthenticationFailedForOldUser() {
121         Configuration configuration = new Configuration();
122         configuration.put(HOST, DUMMY_HOST);
123         configuration.put(USER_NAME, "notAuthenticatedUser");
124         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
125         Bridge bridge = createBridgeThing(configuration);
126
127         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
128         hueBridgeHandler.thingUpdated(bridge);
129
130         injectBridge(hueBridgeHandler,
131                 new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
132                     @Override
133                     public void authenticate(String userName) throws IOException, ApiException {
134                         throw new UnauthorizedException();
135                     }
136                 });
137
138         hueBridgeHandler.onNotAuthenticated();
139
140         assertEquals("notAuthenticatedUser", bridge.getConfiguration().get(USER_NAME));
141         waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
142         assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
143     }
144
145     @Test
146     public void verifyStatusIfLinkButtonIsNotPressed() {
147         Configuration configuration = new Configuration();
148         configuration.put(HOST, DUMMY_HOST);
149         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
150         Bridge bridge = createBridgeThing(configuration);
151
152         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
153         hueBridgeHandler.thingUpdated(bridge);
154
155         injectBridge(hueBridgeHandler,
156                 new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
157                     @Override
158                     public String link(String deviceType) throws IOException, ApiException {
159                         throw new LinkButtonException();
160                     }
161                 });
162
163         hueBridgeHandler.onNotAuthenticated();
164
165         assertNull(bridge.getConfiguration().get(USER_NAME));
166         waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
167         assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
168     }
169
170     @Test
171     public void verifyStatusIfNewUserCannotBeCreated() {
172         Configuration configuration = new Configuration();
173         configuration.put(HOST, DUMMY_HOST);
174         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
175         Bridge bridge = createBridgeThing(configuration);
176
177         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
178         hueBridgeHandler.thingUpdated(bridge);
179
180         injectBridge(hueBridgeHandler,
181                 new HueBridge(mock(HttpClient.class), DUMMY_HOST, 80, HueBridgeConfig.HTTP, scheduler) {
182                     @Override
183                     public String link(String deviceType) throws IOException, ApiException {
184                         throw new ApiException();
185                     }
186                 });
187
188         hueBridgeHandler.onNotAuthenticated();
189
190         assertNull(bridge.getConfiguration().get(USER_NAME));
191         waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
192         waitForAssert(() -> assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
193                 bridge.getStatusInfo().getStatusDetail()));
194     }
195
196     @Test
197     public void verifyOfflineIsSetWithoutBridgeOfflineStatus() {
198         Configuration configuration = new Configuration();
199         configuration.put(HOST, DUMMY_HOST);
200         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
201         Bridge bridge = createBridgeThing(configuration);
202
203         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
204         hueBridgeHandler.thingUpdated(bridge);
205
206         hueBridgeHandler.onConnectionLost();
207
208         waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
209         assertNotEquals(ThingStatusDetail.BRIDGE_OFFLINE, bridge.getStatusInfo().getStatusDetail());
210     }
211
212     @Test
213     public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsNull() {
214         Configuration configuration = new Configuration();
215         configuration.put(HOST, null);
216         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
217         Bridge bridge = createBridgeThing(configuration);
218
219         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
220
221         ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST).withMessageKeySuffix(IP_ADDRESS_MISSING)
222                 .withArguments(HOST).build();
223
224         waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
225     }
226
227     @Test
228     public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsAnEmptyString() {
229         Configuration configuration = new Configuration();
230         configuration.put(HOST, "");
231         configuration.put(Thing.PROPERTY_SERIAL_NUMBER, "testSerialNumber");
232         Bridge bridge = createBridgeThing(configuration);
233
234         HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
235
236         ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST).withMessageKeySuffix(IP_ADDRESS_MISSING)
237                 .withArguments(HOST).build();
238
239         waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
240     }
241
242     private Bridge createBridgeThing(Configuration configuration) {
243         configuration.put("useSelfSignedCertificate", false);
244         Bridge bridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID,
245                 new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge"), null, "Bridge", configuration);
246
247         bridge = requireNonNull(bridge, "Bridge is null");
248         thingRegistry.add(bridge);
249         return bridge;
250     }
251
252     private void injectBridge(HueBridgeHandler hueBridgeHandler, HueBridge bridge) {
253         try {
254             Field hueBridgeField = hueBridgeHandler.getClass().getDeclaredField("hueBridge");
255             hueBridgeField.setAccessible(true);
256             hueBridgeField.set(hueBridgeHandler, bridge);
257         } catch (Exception e) {
258             throw new AssertionError(e);
259         }
260     }
261 }