]> git.basschouten.com Git - openhab-addons.git/blob
5809c1d4376474db8bfe9bc6f49e269ae92b82d5
[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     }
164
165     @Test
166     void createRequestWithObject() {
167         BinarySwitchServiceState binarySwitchState = new BinarySwitchServiceState();
168         binarySwitchState.on = true;
169         Request request = httpClient.createRequest("https://127.0.0.1", HttpMethod.GET, binarySwitchState);
170         assertNotNull(request);
171         assertEquals("{\"on\":true,\"stateType\":\"binarySwitchState\",\"@type\":\"binarySwitchState\"}",
172                 StandardCharsets.UTF_8.decode(request.getContent().iterator().next()).toString());
173     }
174
175     @Test
176     void sendRequest() throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
177         Request request = mock(Request.class);
178         ContentResponse response = mock(ContentResponse.class);
179         when(request.send()).thenReturn(response);
180         when(response.getStatus()).thenReturn(200);
181         when(response.getContentAsString()).thenReturn("{\"jsonrpc\": \"2.0\", \"result\": \"test result\"}");
182
183         SubscribeResult subscribeResult = httpClient.sendRequest(request, SubscribeResult.class,
184                 SubscribeResult::isValid, null);
185         assertEquals("2.0", subscribeResult.getJsonrpc());
186         assertEquals("test result", subscribeResult.getResult());
187     }
188
189     @Test
190     void sendRequestResponseError()
191             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
192         Request request = mock(Request.class);
193         ContentResponse response = mock(ContentResponse.class);
194         when(request.send()).thenReturn(response);
195         when(response.getStatus()).thenReturn(500);
196         ExecutionException e = assertThrows(ExecutionException.class,
197                 () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null));
198         assertEquals("Request failed with status code 500", e.getMessage());
199     }
200
201     @Test
202     void sendRequestResponseErrorWithErrorHandler()
203             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
204         Request request = mock(Request.class);
205         ContentResponse response = mock(ContentResponse.class);
206         when(request.send()).thenReturn(response);
207         when(response.getStatus()).thenReturn(500);
208         when(response.getContentAsString()).thenReturn(
209                 "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}");
210
211         BoschSHCException e = assertThrows(BoschSHCException.class, () -> httpClient.sendRequest(request, Device.class,
212                 Device::isValid, (Integer statusCode, String content) -> {
213                     return new BoschSHCException("test exception");
214                 }));
215         assertEquals("test exception", e.getMessage());
216     }
217
218     @Test
219     void sendRequestEmptyResponse()
220             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
221         Request request = mock(Request.class);
222         ContentResponse response = mock(ContentResponse.class);
223         when(request.send()).thenReturn(response);
224         when(response.getStatus()).thenReturn(200);
225         ExecutionException e = assertThrows(ExecutionException.class,
226                 () -> httpClient.sendRequest(request, SubscribeResult.class, SubscribeResult::isValid, null));
227         assertEquals(
228                 "Received no content in response, expected type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult",
229                 e.getMessage());
230     }
231
232     @Test
233     void sendRequestInvalidResponse()
234             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
235         Request request = mock(Request.class);
236         ContentResponse response = mock(ContentResponse.class);
237         when(request.send()).thenReturn(response);
238         when(response.getStatus()).thenReturn(200);
239         when(response.getContentAsString()).thenReturn(
240                 "{\"@type\": \"JsonRestExceptionResponseEntity\", \"errorCode\": \"500\", \"statusCode\": \"500\"}");
241         ExecutionException e = assertThrows(ExecutionException.class,
242                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> {
243                     return false;
244                 }, null));
245         String actualMessage = e.getMessage();
246         assertTrue(actualMessage.contains(
247                 "Received invalid content for type org.openhab.binding.boschshc.internal.devices.bridge.dto.SubscribeResult:"));
248     }
249
250     @Test
251     void sendRequestInvalidSyntaxInResponse()
252             throws InterruptedException, TimeoutException, ExecutionException, BoschSHCException {
253         Request request = mock(Request.class);
254         ContentResponse response = mock(ContentResponse.class);
255         when(request.send()).thenReturn(response);
256         when(response.getStatus()).thenReturn(200);
257         when(response.getContentAsString()).thenReturn("{\"@type\": \"JsonRestExceptionResponseEntity}");
258         ExecutionException e = assertThrows(ExecutionException.class,
259                 () -> httpClient.sendRequest(request, SubscribeResult.class, sr -> {
260                     return false;
261                 }, null));
262         assertEquals(
263                 "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",
264                 e.getMessage());
265     }
266 }