2 * Copyright (c) 2010-2022 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.hue.internal.handler;
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.openhab.binding.hue.internal.HueBindingConstants.*;
20 import static org.openhab.binding.hue.internal.config.HueBridgeConfig.HTTP;
21 import static org.openhab.core.thing.Thing.PROPERTY_SERIAL_NUMBER;
23 import java.io.IOException;
24 import java.lang.reflect.Field;
25 import java.util.concurrent.ScheduledExecutorService;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Test;
29 import org.openhab.binding.hue.internal.AbstractHueOSGiTestParent;
30 import org.openhab.binding.hue.internal.HueBridge;
31 import org.openhab.binding.hue.internal.HueConfigStatusMessage;
32 import org.openhab.binding.hue.internal.exceptions.ApiException;
33 import org.openhab.binding.hue.internal.exceptions.LinkButtonException;
34 import org.openhab.binding.hue.internal.exceptions.UnauthorizedException;
35 import org.openhab.core.common.ThreadPoolManager;
36 import org.openhab.core.config.core.Configuration;
37 import org.openhab.core.config.core.status.ConfigStatusMessage;
38 import org.openhab.core.thing.Bridge;
39 import org.openhab.core.thing.ThingRegistry;
40 import org.openhab.core.thing.ThingStatus;
41 import org.openhab.core.thing.ThingStatusDetail;
42 import org.openhab.core.thing.ThingTypeUID;
43 import org.openhab.core.thing.ThingUID;
46 * Tests for {@link HueBridgeHandler}.
48 * @author Oliver Libutzki - Initial contribution
49 * @author Michael Grammling - Initial contribution
50 * @author Denis Dudnik - switched to internally integrated source of Jue library
52 public class HueBridgeHandlerOSGiTest extends AbstractHueOSGiTestParent {
54 private static final ThingTypeUID BRIDGE_THING_TYPE_UID = new ThingTypeUID(BINDING_ID, "bridge");
55 private static final String TEST_USER_NAME = "eshTestUser";
56 private static final String DUMMY_HOST = "1.2.3.4";
58 private ThingRegistry thingRegistry;
60 private ScheduledExecutorService scheduler;
64 registerVolatileStorageService();
65 thingRegistry = getService(ThingRegistry.class, ThingRegistry.class);
66 assertNotNull(thingRegistry);
68 scheduler = ThreadPoolManager.getScheduledPool("hueBridgeTest");
72 public void assertThatANewUserIsAddedToConfigIfNotExistingYet() {
73 Configuration configuration = new Configuration();
74 configuration.put(HOST, DUMMY_HOST);
75 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
76 Bridge bridge = createBridgeThing(configuration);
78 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
79 hueBridgeHandler.thingUpdated(bridge);
81 injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
83 public String link(String deviceType) throws IOException, ApiException {
84 return TEST_USER_NAME;
88 hueBridgeHandler.onNotAuthenticated();
90 assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
94 public void assertThatAnExistingUserIsUsedIfAuthenticationWasSuccessful() {
95 Configuration configuration = new Configuration();
96 configuration.put(HOST, DUMMY_HOST);
97 configuration.put(USER_NAME, TEST_USER_NAME);
98 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
99 Bridge bridge = createBridgeThing(configuration);
101 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
102 hueBridgeHandler.thingUpdated(bridge);
104 injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
106 public void authenticate(String userName) throws IOException, ApiException {
110 hueBridgeHandler.onNotAuthenticated();
112 assertThat(bridge.getConfiguration().get(USER_NAME), equalTo(TEST_USER_NAME));
116 public void assertCorrectStatusIfAuthenticationFailedForOldUser() {
117 Configuration configuration = new Configuration();
118 configuration.put(HOST, DUMMY_HOST);
119 configuration.put(USER_NAME, "notAuthenticatedUser");
120 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
121 Bridge bridge = createBridgeThing(configuration);
123 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
124 hueBridgeHandler.thingUpdated(bridge);
126 injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
128 public void authenticate(String userName) throws IOException, ApiException {
129 throw new UnauthorizedException();
133 hueBridgeHandler.onNotAuthenticated();
135 assertEquals("notAuthenticatedUser", bridge.getConfiguration().get(USER_NAME));
136 waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
137 assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
141 public void verifyStatusIfLinkButtonIsNotPressed() {
142 Configuration configuration = new Configuration();
143 configuration.put(HOST, DUMMY_HOST);
144 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
145 Bridge bridge = createBridgeThing(configuration);
147 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
148 hueBridgeHandler.thingUpdated(bridge);
150 injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
152 public String link(String deviceType) throws IOException, ApiException {
153 throw new LinkButtonException();
157 hueBridgeHandler.onNotAuthenticated();
159 assertNull(bridge.getConfiguration().get(USER_NAME));
160 waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
161 assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR, bridge.getStatusInfo().getStatusDetail());
165 public void verifyStatusIfNewUserCannotBeCreated() {
166 Configuration configuration = new Configuration();
167 configuration.put(HOST, DUMMY_HOST);
168 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
169 Bridge bridge = createBridgeThing(configuration);
171 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
172 hueBridgeHandler.thingUpdated(bridge);
174 injectBridge(hueBridgeHandler, new HueBridge(DUMMY_HOST, 80, HTTP, scheduler) {
176 public String link(String deviceType) throws IOException, ApiException {
177 throw new ApiException();
181 hueBridgeHandler.onNotAuthenticated();
183 assertNull(bridge.getConfiguration().get(USER_NAME));
184 waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
185 waitForAssert(() -> assertEquals(ThingStatusDetail.OFFLINE.CONFIGURATION_ERROR,
186 bridge.getStatusInfo().getStatusDetail()));
190 public void verifyOfflineIsSetWithoutBridgeOfflineStatus() {
191 Configuration configuration = new Configuration();
192 configuration.put(HOST, DUMMY_HOST);
193 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
194 Bridge bridge = createBridgeThing(configuration);
196 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
197 hueBridgeHandler.thingUpdated(bridge);
199 hueBridgeHandler.onConnectionLost();
201 waitForAssert(() -> assertEquals(ThingStatus.OFFLINE, bridge.getStatus()));
202 assertNotEquals(ThingStatusDetail.BRIDGE_OFFLINE, bridge.getStatusInfo().getStatusDetail());
206 public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsNull() {
207 Configuration configuration = new Configuration();
208 configuration.put(HOST, null);
209 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
211 Bridge bridge = createBridgeThing(configuration);
213 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
215 ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
216 .withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
218 waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
222 public void assertThatAStatusConfigurationMessageForMissingBridgeIPIsProperlyReturnedIPIsAnEmptyString() {
223 Configuration configuration = new Configuration();
224 configuration.put(HOST, "");
225 configuration.put(PROPERTY_SERIAL_NUMBER, "testSerialNumber");
227 Bridge bridge = createBridgeThing(configuration);
229 HueBridgeHandler hueBridgeHandler = getThingHandler(bridge, HueBridgeHandler.class);
231 ConfigStatusMessage expected = ConfigStatusMessage.Builder.error(HOST)
232 .withMessageKeySuffix(HueConfigStatusMessage.IP_ADDRESS_MISSING).withArguments(HOST).build();
234 waitForAssert(() -> assertEquals(expected, hueBridgeHandler.getConfigStatus().iterator().next()));
237 private Bridge createBridgeThing(Configuration configuration) {
238 Bridge bridge = (Bridge) thingRegistry.createThingOfType(BRIDGE_THING_TYPE_UID,
239 new ThingUID(BRIDGE_THING_TYPE_UID, "testBridge"), null, "Bridge", configuration);
241 bridge = requireNonNull(bridge, "Bridge is null");
242 thingRegistry.add(bridge);
246 private void injectBridge(HueBridgeHandler hueBridgeHandler, HueBridge bridge) {
248 Field hueBridgeField = hueBridgeHandler.getClass().getDeclaredField("hueBridge");
249 hueBridgeField.setAccessible(true);
250 hueBridgeField.set(hueBridgeHandler, bridge);
251 } catch (Exception e) {
252 throw new AssertionError(e);