]> git.basschouten.com Git - openhab-addons.git/blob
390651f554af058179e5797baefc79f1201cc109
[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 -> "logger".equalsIgnoreCase(f.getName()), 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 -> "logger".equalsIgnoreCase(f.getName()), 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) -> new BoschSHCException("test exception")));
214         assertEquals("test exception", e.getMessage());
215     }
216
217     @Test
218     void sendRequestEmptyResponse()
219             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
220         Request request = mock(Request.class);
221         ContentResponse response = mock(ContentResponse.class);
222         when(request.send()).thenReturn(response);
223         when(response.getStatus()).thenReturn(200);
224         ExecutionException e = assertThrows(ExecutionException.class,
225                 () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null));
226         assertEquals(
227                 "Received no content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult",
228                 e.getMessage());
229     }
230
231     @Test
232     void sendRequestInvalidResponse()
233             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
234         Request request = mock(Request.class);
235         ContentResponse response = mock(ContentResponse.class);
236         when(request.send()).thenReturn(response);
237         when(response.getStatus()).thenReturn(200);
238         when(response.getContentAsString()).thenReturn(
239                 "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}");
240         ExecutionException e = assertThrows(ExecutionException.class,
241                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> false, null));
242         String actualMessage = e.getMessage();
243         assertTrue(actualMessage.contains(
244                 "Received invalid content for type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult:"));
245     }
246
247     @Test
248     void sendRequestInvalidSyntaxInResponse()
249             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
250         Request request = mock(Request.class);
251         ContentResponse response = mock(ContentResponse.class);
252         when(request.send()).thenReturn(response);
253         when(response.getStatus()).thenReturn(200);
254         when(response.getContentAsString()).thenReturn("{\"@type\": \"JsonRestExceptionResponseEntity}");
255         ExecutionException e = assertThrows(ExecutionException.class,
256                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> false, null));
257         assertEquals(
258                 "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",
259                 e.getMessage());
260     }
261 }