]> git.basschouten.com Git - openhab-addons.git/blob
5461b7eed8a2b64ab7d3a5620c2c2aeeb65da66b
[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.boschshc.internal.devices.bridge;
14
15 import static org.junit.jupiter.api.Assertions.*;
16 import static org.mockito.ArgumentMatchers.*;
17 import static org.mockito.Mockito.*;
18
19 import java.lang.reflect.Field;
20 import java.nio.charset.StandardCharsets;
21 import java.util.List;
22 import java.util.concurrent.ExecutionException;
23 import java.util.concurrent.TimeoutException;
24
25 import org.eclipse.jdt.annotation.NonNullByDefault;
26 import org.eclipse.jetty.client.api.ContentResponse;
27 import org.eclipse.jetty.client.api.Request;
28 import org.eclipse.jetty.http.HttpMethod;
29 import org.eclipse.jetty.util.ssl.SslContextFactory;
30 import org.junit.jupiter.api.BeforeAll;
31 import org.junit.jupiter.api.BeforeEach;
32 import org.junit.jupiter.api.Test;
33 import org.junit.platform.commons.support.HierarchyTraversalMode;
34 import org.junit.platform.commons.support.ReflectionSupport;
35 import org.openhab.binding.boschshc.internal.devices.bridge.dto.Device;
36 import org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult;
37 import org.openhab.binding.boschshc.internal.exceptions.BoschSHCException;
38 import org.openhab.binding.boschshc.internal.exceptions.PairingFailedException;
39 import org.openhab.binding.boschshc.internal.services.binaryswitch.dto.BinarySwitchServiceState;
40 import org.slf4j.Logger;
41
42 /**
43  * Tests cases for {@link BoschHttpClient}.
44  *
45  * @author Gerd Zanker - Initial contribution
46  */
47 @NonNullByDefault
48 class BoschHttpClientTest {
49
50     private @NonNullByDefault({}) BoschHttpClient httpClient;
51
52     @BeforeAll
53     static void beforeAll() {
54         BoschSslUtilTest.prepareTempFolderForKeyStore();
55     }
56
57     @BeforeEach
58     void beforeEach() throws PairingFailedException {
59         SslContextFactory sslFactory = new BoschSslUtil("127.0.0.1").getSslContextFactory();
60         httpClient = new BoschHttpClient("127.0.0.1", "dummy", sslFactory);
61         assertNotNull(httpClient);
62     }
63
64     @Test
65     void getPublicInformationUrl() {
66         assertEquals("https://127.0.0.1:8446/smarthome/public/information", httpClient.getPublicInformationUrl());
67     }
68
69     @Test
70     void getPairingUrl() {
71         assertEquals("https://127.0.0.1:8443/smarthome/clients", httpClient.getPairingUrl());
72     }
73
74     @Test
75     void getBoschShcUrl() {
76         assertEquals("https://127.0.0.1:8444/testEndpoint", httpClient.getBoschShcUrl("testEndpoint"));
77     }
78
79     @Test
80     void getBoschSmartHomeUrl() {
81         assertEquals("https://127.0.0.1:8444/smarthome/endpointForTest",
82                 httpClient.getBoschSmartHomeUrl("endpointForTest"));
83     }
84
85     @Test
86     void getServiceUrl() {
87         assertEquals("https://127.0.0.1:8444/smarthome/devices/testDevice/services/testService",
88                 httpClient.getServiceUrl("testService", "testDevice"));
89     }
90
91     @Test
92     void getServiceStateUrl() {
93         assertEquals("https://127.0.0.1:8444/smarthome/devices/testDevice/services/testService/state",
94                 httpClient.getServiceStateUrl("testService", "testDevice"));
95     }
96
97     @Test
98     void isAccessPossible() throws InterruptedException {
99         assertFalse(httpClient.isAccessPossible());
100     }
101
102     @Test
103     void isOnline() throws InterruptedException {
104         assertFalse(httpClient.isOnline());
105     }
106
107     @Test
108     void isOnlineErrorResponse() throws InterruptedException, IllegalArgumentException, IllegalAccessException,
109             TimeoutException, ExecutionException {
110         BoschHttpClient mockedHttpClient = mock(BoschHttpClient.class);
111         when(mockedHttpClient.isOnline()).thenCallRealMethod();
112         when(mockedHttpClient.getPublicInformationUrl()).thenCallRealMethod();
113
114         // mock a logger using reflection to avoid NPEs during logger calls
115         Logger mockedLogger = mock(Logger.class);
116         List<Field> fields = ReflectionSupport.findFields(BoschHttpClient.class,
117                 f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN);
118         Field field = fields.iterator().next();
119         field.setAccessible(true);
120         field.set(mockedHttpClient, mockedLogger);
121
122         Request request = mock(Request.class);
123         when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
124         ContentResponse response = mock(ContentResponse.class);
125         when(request.send()).thenReturn(response);
126         when(response.getStatus()).thenReturn(500);
127         assertFalse(mockedHttpClient.isOnline());
128     }
129
130     @Test
131     void isOnlineMockedResponse() throws InterruptedException, TimeoutException, ExecutionException,
132             IllegalArgumentException, IllegalAccessException {
133         BoschHttpClient mockedHttpClient = mock(BoschHttpClient.class);
134         when(mockedHttpClient.isOnline()).thenCallRealMethod();
135         when(mockedHttpClient.getPublicInformationUrl()).thenCallRealMethod();
136
137         // mock a logger using reflection to avoid NPEs during logger calls
138         Logger mockedLogger = mock(Logger.class);
139         List<Field> fields = ReflectionSupport.findFields(BoschHttpClient.class,
140                 f -> f.getName().equalsIgnoreCase("logger"), HierarchyTraversalMode.TOP_DOWN);
141         Field field = fields.iterator().next();
142         field.setAccessible(true);
143         field.set(mockedHttpClient, mockedLogger);
144
145         Request request = mock(Request.class);
146         when(mockedHttpClient.createRequest(anyString(), same(HttpMethod.GET))).thenReturn(request);
147         ContentResponse response = mock(ContentResponse.class);
148         when(request.send()).thenReturn(response);
149         when(response.getStatus()).thenReturn(200);
150         when(response.getContentAsString()).thenReturn("response");
151         assertTrue(mockedHttpClient.isOnline());
152     }
153
154     @Test
155     void doPairing() throws InterruptedException {
156         assertFalse(httpClient.doPairing());
157     }
158
159     @Test
160     void createRequest() {
161         Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET);
162         assertNotNull(request);
163         assertEquals("3.2", request.getHeaders().get("api-version"));
164     }
165
166     @Test
167     void createRequestWithObject() {
168         BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState();
169         binarySwitchState.on = true;
170         Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, binarySwitchState);
171         assertNotNull(request);
172         assertEquals("{\"on\":true,\"stateType\":\"binarySwitchState\",\"@type\":\"binarySwitchState\"}",
173                 StandardCharsets.UTF_8.decode(request.getContent().iterator().next()).toString());
174     }
175
176     @Test
177     void sendRequest() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
178         Request request = mock(Request.class);
179         ContentResponse response = mock(ContentResponse.class);
180         when(request.send()).thenReturn(response);
181         when(response.getStatus()).thenReturn(200);
182         when(response.getContentAsString()).thenReturn("{\"jsonrpc\": \"2.0\", \"result\": \"test result\"}");
183
184         SubscribeResult subscribeResult = httpClient.sendRequest(request, SubscribeResult.class,
185                 SubscribeResult::isValid, null);
186         assertEquals("2.0", subscribeResult.getJsonrpc());
187         assertEquals("test result", subscribeResult.getResult());
188     }
189
190     @Test
191     void sendRequestResponseError()
192             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
193         Request request = mock(Request.class);
194         ContentResponse response = mock(ContentResponse.class);
195         when(request.send()).thenReturn(response);
196         when(response.getStatus()).thenReturn(500);
197         ExecutionException e = assertThrows(ExecutionException.class,
198                 () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null));
199         assertEquals("Send request failed with status code 500", e.getMessage());
200     }
201
202     @Test
203     void sendRequestResponseErrorWithErrorHandler()
204             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
205         Request request = mock(Request.class);
206         ContentResponse response = mock(ContentResponse.class);
207         when(request.send()).thenReturn(response);
208         when(response.getStatus()).thenReturn(500);
209         when(response.getContentAsString()).thenReturn(
210                 "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}");
211
212         BoschSHCException e = assertThrows(BoschSHCException.class, () -> httpClient.sendRequest(request, Device.class,
213                 Device::isValid, (Integer statusCode, String content) -> {
214                     return new BoschSHCException("test exception");
215                 }));
216         assertEquals("test exception", e.getMessage());
217     }
218
219     @Test
220     void sendRequestEmptyResponse()
221             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
222         Request request = mock(Request.class);
223         ContentResponse response = mock(ContentResponse.class);
224         when(request.send()).thenReturn(response);
225         when(response.getStatus()).thenReturn(200);
226         ExecutionException e = assertThrows(ExecutionException.class,
227                 () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null));
228         assertEquals(
229                 "Received no content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult",
230                 e.getMessage());
231     }
232
233     @Test
234     void sendRequestInvalidResponse()
235             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
236         Request request = mock(Request.class);
237         ContentResponse response = mock(ContentResponse.class);
238         when(request.send()).thenReturn(response);
239         when(response.getStatus()).thenReturn(200);
240         when(response.getContentAsString()).thenReturn(
241                 "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}");
242         ExecutionException e = assertThrows(ExecutionException.class,
243                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> {
244                     return false;
245                 }, null));
246         String actualMessage = e.getMessage();
247         assertTrue(actualMessage.contains(
248                 "Received invalid content for type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult:"));
249     }
250
251     @Test
252     void sendRequestInvalidSyntaxInResponse()
253             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
254         Request request = mock(Request.class);
255         ContentResponse response = mock(ContentResponse.class);
256         when(request.send()).thenReturn(response);
257         when(response.getStatus()).thenReturn(200);
258         when(response.getContentAsString()).thenReturn("{\"@type\": \"JsonRestExceptionResponseEntity}");
259         ExecutionException e = assertThrows(ExecutionException.class,
260                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> {
261                     return false;
262                 }, null));
263         assertEquals(
264                 "Received invalid content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult: com.google.gson.stream.MalformedJsonException: Unterminated string at line 1 column 44 path $.@type",
265                 e.getMessage());
266     }
267 }