2 * Copyright (c) 2010-2024 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.boschshc.internal.devices.bridge;
15 import static org.junit.jupiter.api.Assertions.assertEquals;
16 import static org.junit.jupiter.api.Assertions.assertNotNull;
17 import static org.junit.jupiter.api.Assertions.assertSame;
18 import static org.junit.jupiter.api.Assertions.assertThrows;
19 import static org.junit.jupiter.api.Assertions.assertTrue;
20 import static org.mockito.ArgumentMatchers.any;
21 import static org.mockito.ArgumentMatchers.anyString;
22 import static org.mockito.ArgumentMatchers.argThat;
23 import static org.mockito.ArgumentMatchers.contains;
24 import static org.mockito.ArgumentMatchers.eq;
25 import static org.mockito.ArgumentMatchers.same;
26 import static org.mockito.Mockito.mock;
27 import static org.mockito.Mockito.verify;
28 import static org.mockito.Mockito.verifyNoMoreInteractions;
29 import static org.mockito.Mockito.when;
31 import java.io.IOException;
32 import java.nio.file.Files;
33 import java.nio.file.Path;
34 import java.nio.file.Paths;
35 import java.util.ArrayList;
36 import java.util.HashMap;
37 import java.util.List;
39 import java.util.UUID;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.TimeoutException;
42 import java.util.function.BiFunction;
44 import org.eclipse.jdt.annotation.NonNullByDefault;
45 import org.eclipse.jdt.annotation.Nullable;
46 import org.eclipse.jetty.client.api.ContentResponse;
47 import org.eclipse.jetty.client.api.Request;
48 import org.eclipse.jetty.http.HttpMethod;
49 import org.junit.jupiter.api.AfterEach;
50 import org.junit.jupiter.api.BeforeAll;
51 import org.junit.jupiter.api.BeforeEach;
52 import org.junit.jupiter.api.Test;
53 import org.mockito.ArgumentCaptor;
54 import org.openhab.binding.boschshc.internal.devices.BoschSHCHandler;
55 import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
56 import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceServiceData;
57 import org.openhab.binding.boschshc.internal.devices.bridge.dto.DeviceTest;
58 import org.openhab.binding.boschshc.internal.devices.bridge.dto.Faults;
59 import org.openhab.binding.boschshc.internal.devices.bridge.dto.LongPollResult;
60 import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
61 import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedState;
62 import org.openhab.binding.boschshc.internal.devices.bridge.dto.UserDefinedStateTest;
63 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
64 import org.openhab.binding.boschshc.internal.serialization.GsonUtils;
65 import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState;
66 import org.openhab.binding.boschshc.internal.services.intrusion.actions.arm.dto.ArmActionRequest;
67 import org.openhab.binding.boschshc.internal.services.intrusion.dto.AlarmState;
68 import org.openhab.binding.boschshc.internal.services.intrusion.dto.ArmingState;
69 import org.openhab.binding.boschshc.internal.services.intrusion.dto.IntrusionDetectionSystemState;
70 import org.openhab.binding.boschshc.internal.services.shuttercontact.ShutterContactState;
71 import org.openhab.binding.boschshc.internal.services.shuttercontact.dto.ShutterContactServiceState;
72 import org.openhab.core.config.core.Configuration;
73 import org.openhab.core.thing.Bridge;
74 import org.openhab.core.thing.Thing;
75 import org.openhab.core.thing.ThingStatus;
76 import org.openhab.core.thing.ThingStatusDetail;
77 import org.openhab.core.thing.binding.ThingHandlerCallback;
78 import org.openhab.core.thing.binding.builder.ThingStatusInfoBuilder;
80 import com.google.gson.JsonElement;
81 import com.google.gson.JsonParser;
84 * Unit tests for the {@link BridgeHandler}.
86 * @author David Pace - Initial contribution
90 class BridgeHandlerTest {
92 private @NonNullByDefault({}) BridgeHandler fixture;
94 private @NonNullByDefault({}) BoschHttpClient httpClient;
96 private @NonNullByDefault({}) ThingHandlerCallback thingHandlerCallback;
99 * A mocked bridge instance
101 private @NonNullByDefault({}) Bridge thing;
104 static void beforeAll() throws IOException {
105 Path mavenTargetFolder = Paths.get("target");
106 assertTrue(Files.exists(mavenTargetFolder), "Maven target folder does not exist.");
107 System.setProperty("openhab.userdata", mavenTargetFolder.toFile().getAbsolutePath());
108 Path etc = mavenTargetFolder.resolve("etc");
109 if (!Files.exists(etc)) {
110 Files.createDirectory(etc);
115 void beforeEach() throws Exception {
116 Bridge bridge = mock(Bridge.class);
117 fixture = new BridgeHandler(bridge);
119 thingHandlerCallback = mock(ThingHandlerCallback.class);
120 fixture.setCallback(thingHandlerCallback);
122 Configuration bridgeConfiguration = new Configuration();
123 Map<@Nullable String, @Nullable Object> properties = new HashMap<>();
124 properties.put("ipAddress", "localhost");
125 properties.put("password", "test");
126 bridgeConfiguration.setProperties(properties);
128 thing = mock(Bridge.class);
129 when(thing.getConfiguration()).thenReturn(bridgeConfiguration);
130 // this calls initialize() as well
131 fixture.thingUpdated(thing);
133 // shut down the real HTTP client
134 if (fixture.httpClient != null) {
135 fixture.httpClient.stop();
138 // use a mocked HTTP client
139 httpClient = mock(BoschHttpClient.class);
140 fixture.httpClient = httpClient;
144 void postAction() throws InterruptedException, TimeoutException, ExecutionException {
145 String endpoint = "/intrusion/actions/arm";
146 String url = "https://127.0.0.1:8444/smarthome/intrusion/actions/arm";
147 when(httpClient.getBoschSmartHomeUrl(endpoint)).thenReturn(url);
148 Request mockRequest = mock(Request.class);
149 when(httpClient.createRequest(anyString(), any(), any())).thenReturn(mockRequest);
150 ArmActionRequest request = new ArmActionRequest();
151 request.profileId = "0";
153 fixture.postAction(endpoint, request);
154 verify(httpClient).createRequest(eq(url), same(HttpMethod.POST), same(request));
155 verify(mockRequest).send();
159 void initialAccessHttpClientOffline() {
160 fixture.initialAccess(httpClient);
164 void initialAccessHttpClientOnline() throws InterruptedException {
165 when(httpClient.isOnline()).thenReturn(true);
166 fixture.initialAccess(httpClient);
170 void initialAccessAccessPossible()
171 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
172 when(httpClient.isOnline()).thenReturn(true);
173 when(httpClient.isAccessPossible()).thenReturn(true);
174 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
175 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
177 // mock a request and response to obtain rooms
178 Request roomsRequest = mock(Request.class);
179 ContentResponse roomsResponse = mock(ContentResponse.class);
180 when(roomsResponse.getStatus()).thenReturn(200);
181 when(roomsResponse.getContentAsString()).thenReturn(
182 "[{\"@type\":\"room\",\"id\":\"hz_1\",\"iconId\":\"icon_room_bedroom\",\"name\":\"Bedroom\"}]");
183 when(roomsRequest.send()).thenReturn(roomsResponse);
184 when(httpClient.createRequest(contains("/rooms"), same(HttpMethod.GET))).thenReturn(roomsRequest);
186 // mock a request and response to obtain devices
187 Request devicesRequest = mock(Request.class);
188 ContentResponse devicesResponse = mock(ContentResponse.class);
189 when(devicesResponse.getStatus()).thenReturn(200);
190 when(devicesResponse.getContentAsString()).thenReturn("""
192 "rootDeviceId":"64-da-a0-02-14-9b",
193 "id":"hdm:HomeMaticIP:3014F711A00004953859F31B",
194 "deviceServiceIds":["PowerMeter","PowerSwitch","PowerSwitchProgram","Routing"],
195 "manufacturer":"BOSCH",
198 "serial":"3014F711A00004953859F31B",
200 "name":"Coffee Machine",
201 "status":"AVAILABLE",
205 when(devicesRequest.send()).thenReturn(devicesResponse);
206 when(httpClient.createRequest(contains("/devices"), same(HttpMethod.GET))).thenReturn(devicesRequest);
208 SubscribeResult subscribeResult = new SubscribeResult();
209 when(httpClient.sendRequest(any(), same(SubscribeResult.class), any(), any())).thenReturn(subscribeResult);
211 Request longPollRequest = mock(Request.class);
212 when(httpClient.createRequest(anyString(), same(HttpMethod.POST),
213 argThat((JsonRpcRequest r) -> "RE/longPoll".equals(r.method)))).thenReturn(longPollRequest);
215 fixture.initialAccess(httpClient);
216 verify(thingHandlerCallback).statusUpdated(any(),
217 eq(ThingStatusInfoBuilder.create(ThingStatus.ONLINE, ThingStatusDetail.NONE).build()));
221 void getState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
222 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
223 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
224 Request request = mock(Request.class);
225 when(request.header(anyString(), anyString())).thenReturn(request);
226 ContentResponse response = mock(ContentResponse.class);
227 when(response.getStatus()).thenReturn(200);
228 when(response.getContentAsString()).thenReturn("""
230 "@type": "systemState",
231 "systemAvailability": {
232 "@type": "systemAvailabilityState",
237 "@type": "armingState",
238 "state": "SYSTEM_DISARMED",
242 "@type": "alarmState",
243 "value": "ALARM_OFF",
247 "activeConfigurationProfile": {
248 "@type": "activeConfigurationProfile",
251 "securityGapState": {
252 "@type": "securityGapState",
259 when(request.send()).thenReturn(response);
260 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
262 IntrusionDetectionSystemState state = fixture.getState("intrusion/states/system",
263 IntrusionDetectionSystemState.class);
264 assertNotNull(state);
265 assertTrue(state.systemAvailability.available);
266 assertSame(AlarmState.ALARM_OFF, state.alarmState.value);
267 assertSame(ArmingState.SYSTEM_DISARMED, state.armingState.state);
271 void getDeviceState() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
272 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
273 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
274 when(httpClient.getServiceStateUrl(anyString(), anyString(), any())).thenCallRealMethod();
275 when(httpClient.getServiceStateUrl(anyString(), anyString())).thenCallRealMethod();
277 Request request = mock(Request.class);
278 when(request.header(anyString(), anyString())).thenReturn(request);
279 ContentResponse response = mock(ContentResponse.class);
280 when(response.getStatus()).thenReturn(200);
281 when(response.getContentAsString())
282 .thenReturn("{\n" + " \"@type\": \"shutterContactState\",\n" + " \"value\": \"OPEN\"\n" + " }");
283 when(request.send()).thenReturn(response);
284 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
286 ShutterContactServiceState state = fixture.getState("hdm:HomeMaticIP:3014D711A000009D545DEB39D",
287 "ShutterContact", ShutterContactServiceState.class);
288 assertNotNull(state);
289 assertSame(ShutterContactState.OPEN, state.value);
293 void getDeviceInfo() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
294 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
295 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
297 Request request = mock(Request.class);
298 when(request.header(anyString(), anyString())).thenReturn(request);
299 ContentResponse response = mock(ContentResponse.class);
300 when(response.getStatus()).thenReturn(200);
301 when(request.send()).thenReturn(response);
302 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
303 when(httpClient.sendRequest(same(request), same(Device.class), any(), any()))
304 .thenReturn(DeviceTest.createTestDevice());
306 String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B";
307 Device device = fixture.getDeviceInfo(deviceId);
308 assertEquals(deviceId, device.id);
312 void getDeviceInfoErrorCases()
313 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
314 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
315 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
317 Request request = mock(Request.class);
318 when(request.header(anyString(), anyString())).thenReturn(request);
319 ContentResponse response = mock(ContentResponse.class);
320 when(response.getStatus()).thenReturn(200);
321 when(request.send()).thenReturn(response);
322 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
324 @SuppressWarnings("unchecked")
325 ArgumentCaptor<BiFunction<Integer, String, BoschSHCException>> errorResponseHandlerCaptor = ArgumentCaptor
326 .forClass(BiFunction.class);
328 when(httpClient.sendRequest(same(request), same(Device.class), any(), errorResponseHandlerCaptor.capture()))
329 .thenReturn(DeviceTest.createTestDevice());
331 String deviceId = "hdm:HomeMaticIP:3014F711A00004953859F31B";
332 fixture.getDeviceInfo(deviceId);
334 BiFunction<Integer, String, BoschSHCException> errorResponseHandler = errorResponseHandlerCaptor.getValue();
335 Exception e = errorResponseHandler.apply(500,
336 "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}");
338 "Request for info of device hdm:HomeMaticIP:3014F711A00004953859F31B failed with status code 500 and error code testErrorCode",
341 e = errorResponseHandler.apply(404,
342 "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"ENTITY_NOT_FOUND\",\"statusCode\": 404}");
345 e = errorResponseHandler.apply(500, "");
346 assertEquals("Request for info of device hdm:HomeMaticIP:3014F711A00004953859F31B failed with status code 500",
351 void getServiceData() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
352 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
353 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
354 when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod();
356 Request request = mock(Request.class);
357 when(request.header(anyString(), anyString())).thenReturn(request);
358 ContentResponse response = mock(ContentResponse.class);
359 when(response.getStatus()).thenReturn(200);
360 when(response.getContentAsString()).thenReturn("""
362 "@type":"DeviceServiceData",
363 "path":"/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel",
365 "deviceId":"hdm:ZigBee:000d6f0004b93361",
369 "type":"LOW_BATTERY",
376 when(request.send()).thenReturn(response);
377 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
379 DeviceServiceData serviceData = fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel");
380 assertNotNull(serviceData);
381 Faults faults = serviceData.faults;
382 assertNotNull(faults);
383 assertEquals("LOW_BATTERY", faults.entries.get(0).type);
387 void getServiceDataError() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
388 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
389 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
390 when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod();
392 Request request = mock(Request.class);
393 when(request.header(anyString(), anyString())).thenReturn(request);
394 ContentResponse response = mock(ContentResponse.class);
395 when(response.getStatus()).thenReturn(500);
396 when(response.getContentAsString()).thenReturn(
397 "{\"@type\":\"JsonRestExceptionResponseEntity\",\"errorCode\": \"testErrorCode\",\"statusCode\": 500}");
398 when(request.send()).thenReturn(response);
399 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
400 when(httpClient.sendRequest(same(request), same(Device.class), any(), any()))
401 .thenReturn(DeviceTest.createTestDevice());
403 BoschSHCException e = assertThrows(BoschSHCException.class,
404 () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel"));
406 "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500 and error code testErrorCode",
411 void getServiceDataErrorNoRestExceptionResponse()
412 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
413 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
414 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
415 when(httpClient.getServiceUrl(anyString(), anyString())).thenCallRealMethod();
417 Request request = mock(Request.class);
418 when(request.header(anyString(), anyString())).thenReturn(request);
419 ContentResponse response = mock(ContentResponse.class);
420 when(response.getStatus()).thenReturn(500);
421 when(response.getContentAsString()).thenReturn("");
422 when(request.send()).thenReturn(response);
423 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
425 BoschSHCException e = assertThrows(BoschSHCException.class,
426 () -> fixture.getServiceData("hdm:ZigBee:000d6f0004b93361", "BatteryLevel"));
428 "State request with URL https://null:8444/smarthome/devices/hdm:ZigBee:000d6f0004b93361/services/BatteryLevel failed with status code 500",
433 void putState() throws InterruptedException, TimeoutException, ExecutionException {
434 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
435 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
436 when(httpClient.getServiceStateUrl(anyString(), anyString())).thenCallRealMethod();
437 when(httpClient.getServiceStateUrl(anyString(), anyString(), any())).thenCallRealMethod();
439 Request request = mock(Request.class);
440 when(request.header(anyString(), anyString())).thenReturn(request);
441 ContentResponse response = mock(ContentResponse.class);
443 when(httpClient.createRequest(anyString(), same(HttpMethod.PUT), any(BinarySwitchServiceState.class)))
444 .thenReturn(request);
445 when(request.send()).thenReturn(response);
447 BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState();
448 binarySwitchState.on = true;
449 fixture.putState("hdm:ZigBee:f0d1b80000f2a3e9", "BinarySwitch", binarySwitchState);
453 void getUserStateInfo() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
454 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
455 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
456 String stateId = UUID.randomUUID().toString();
458 Request request = mock(Request.class);
459 when(request.header(anyString(), anyString())).thenReturn(request);
460 ContentResponse response = mock(ContentResponse.class);
461 when(response.getStatus()).thenReturn(200);
462 when(request.send()).thenReturn(response);
463 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
464 when(httpClient.sendRequest(same(request), same(UserDefinedState.class), any(), any()))
465 .thenReturn(UserDefinedStateTest.createTestState(stateId));
467 UserDefinedState userState = fixture.getUserStateInfo(stateId);
468 assertEquals(stateId, userState.getId());
472 void getUserStates() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
473 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
474 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
475 String stateId = UUID.randomUUID().toString();
477 Request request = mock(Request.class);
478 when(request.header(anyString(), anyString())).thenReturn(request);
479 ContentResponse response = mock(ContentResponse.class);
480 when(response.getStatus()).thenReturn(200);
481 when(request.send()).thenReturn(response);
482 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
483 when(response.getContentAsString()).thenReturn(
484 GsonUtils.DEFAULT_GSON_INSTANCE.toJson(List.of(UserDefinedStateTest.createTestState(stateId))));
486 List<UserDefinedState> userStates = fixture.getUserStates();
487 assertEquals(1, userStates.size());
491 void getUserStatesReturnsEmptyListIfRequestNotSuccessful()
492 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
493 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
494 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
496 Request request = mock(Request.class);
497 when(request.header(anyString(), anyString())).thenReturn(request);
498 ContentResponse response = mock(ContentResponse.class);
499 when(response.getStatus()).thenReturn(401);
500 when(request.send()).thenReturn(response);
501 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
503 List<UserDefinedState> userStates = fixture.getUserStates();
504 assertTrue(userStates.isEmpty());
508 void getUserStatesReturnsEmptyListIfExceptionHappened()
509 throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
510 when(httpClient.getBoschSmartHomeUrl(anyString())).thenCallRealMethod();
511 when(httpClient.getBoschShcUrl(anyString())).thenCallRealMethod();
513 Request request = mock(Request.class);
514 when(request.header(anyString(), anyString())).thenReturn(request);
515 ContentResponse response = mock(ContentResponse.class);
516 when(response.getStatus()).thenReturn(401);
517 when(request.send()).thenThrow(new TimeoutException("text exception"));
518 when(httpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
520 List<UserDefinedState> userStates = fixture.getUserStates();
521 assertTrue(userStates.isEmpty());
525 void afterEach() throws Exception {
530 void handleLongPollResultNoDeviceId() {
531 List<Thing> things = new ArrayList<Thing>();
532 when(thing.getThings()).thenReturn(things);
534 Thing thing = mock(Thing.class);
537 BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
538 when(thing.getHandler()).thenReturn(thingHandler);
543 "path": "/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
544 "@type": "DeviceServiceData",
547 "@type": "powerSwitchState",
550 "deviceId": "hdm:HomeMaticIP:3014F711A0001916D859A8A9"
555 LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
556 assertNotNull(longPollResult);
558 fixture.handleLongPollResult(longPollResult);
560 verify(thingHandler).getBoschID();
561 verifyNoMoreInteractions(thingHandler);
565 void handleLongPollResult() {
566 List<Thing> things = new ArrayList<Thing>();
567 when(thing.getThings()).thenReturn(things);
569 Thing thing = mock(Thing.class);
572 BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
573 when(thing.getHandler()).thenReturn(thingHandler);
575 when(thingHandler.getBoschID()).thenReturn("hdm:HomeMaticIP:3014F711A0001916D859A8A9");
580 "path": "/devices/hdm:HomeMaticIP:3014F711A0001916D859A8A9/services/PowerSwitch",
581 "@type": "DeviceServiceData",
584 "@type": "powerSwitchState",
587 "deviceId": "hdm:HomeMaticIP:3014F711A0001916D859A8A9"
592 LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
593 assertNotNull(longPollResult);
595 fixture.handleLongPollResult(longPollResult);
597 verify(thingHandler).getBoschID();
599 JsonElement expectedState = JsonParser.parseString("""
601 "@type": "powerSwitchState",
606 verify(thingHandler).processUpdate("PowerSwitch", expectedState);
610 void handleLongPollResultHandleChildUpdate() {
611 List<Thing> things = new ArrayList<Thing>();
612 when(thing.getThings()).thenReturn(things);
614 Thing thing = mock(Thing.class);
617 BoschSHCHandler thingHandler = mock(BoschSHCHandler.class);
618 when(thing.getHandler()).thenReturn(thingHandler);
620 when(thingHandler.getBoschID()).thenReturn("hdm:ZigBee:70ac08fffefead2d");
625 "path": "/devices/hdm:ZigBee:70ac08fffefead2d#3/services/PowerSwitch",
626 "@type": "DeviceServiceData",
629 "@type": "powerSwitchState",
632 "deviceId": "hdm:ZigBee:70ac08fffefead2d#3"
637 LongPollResult longPollResult = GsonUtils.DEFAULT_GSON_INSTANCE.fromJson(json, LongPollResult.class);
638 assertNotNull(longPollResult);
640 fixture.handleLongPollResult(longPollResult);
642 verify(thingHandler).getBoschID();
644 JsonElement expectedState = JsonParser.parseString("""
646 "@type": "powerSwitchState",
651 verify(thingHandler).processChildUpdate("hdm:ZigBee:70ac08fffefead2d#3", "PowerSwitch", expectedState);