Commit ca7ffd99c0480185b19dedb98c2781f85cd05cbc
Committed by
Andrew Shvayka
1 parent
e49c713a
Fix ignored MQTT integration tests.
Showing
3 changed files
with
55 additions
and
54 deletions
... | ... | @@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext { |
62 | 62 | private static final Executor executor = Executors.newSingleThreadExecutor(); |
63 | 63 | public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; |
64 | 64 | public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; |
65 | + public static final String DEVICE_WITH_REQUESTED_ID_NOT_FOUND = "Device with requested id wasn't found!"; | |
65 | 66 | |
66 | 67 | private final SharedPluginProcessingContext pluginCtx; |
67 | 68 | private final Optional<PluginApiCallSecurityContext> securityCtx; |
... | ... | @@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext { |
309 | 310 | ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); |
310 | 311 | Futures.addCallback(deviceFuture, getCallback(callback, device -> { |
311 | 312 | if (device == null) { |
312 | - return ValidationResult.entityNotFound("Device with requested id wasn't found!"); | |
313 | + return ValidationResult.entityNotFound(DEVICE_WITH_REQUESTED_ID_NOT_FOUND); | |
313 | 314 | } else { |
314 | 315 | if (!device.getTenantId().equals(ctx.getTenantId())) { |
315 | 316 | return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); | ... | ... |
... | ... | @@ -106,6 +106,11 @@ public abstract class AbstractControllerTest { |
106 | 106 | protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; |
107 | 107 | private static final String CUSTOMER_USER_PASSWORD = "customer"; |
108 | 108 | |
109 | + /** See {@link org.springframework.test.web.servlet.DefaultMvcResult#getAsyncResult(long)} | |
110 | + * and {@link org.springframework.mock.web.MockAsyncContext#getTimeout()} | |
111 | + */ | |
112 | + private static final long DEFAULT_TIMEOUT = -1L; | |
113 | + | |
109 | 114 | protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), |
110 | 115 | MediaType.APPLICATION_JSON.getSubtype(), |
111 | 116 | Charset.forName("utf8")); |
... | ... | @@ -336,7 +341,7 @@ public abstract class AbstractControllerTest { |
336 | 341 | } |
337 | 342 | |
338 | 343 | protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { |
339 | - return readResponse(doPost(urlTemplate, params).andExpect(resultMatcher), responseClass); | |
344 | + return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass); | |
340 | 345 | } |
341 | 346 | |
342 | 347 | protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception { |
... | ... | @@ -344,7 +349,11 @@ public abstract class AbstractControllerTest { |
344 | 349 | } |
345 | 350 | |
346 | 351 | protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { |
347 | - return readResponse(doPostAsync(urlTemplate, content, params).andExpect(resultMatcher), responseClass); | |
352 | + return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass); | |
353 | + } | |
354 | + | |
355 | + protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, Long timeout, String... params) throws Exception { | |
356 | + return readResponse(doPostAsync(urlTemplate, content, timeout, params).andExpect(resultMatcher), responseClass); | |
348 | 357 | } |
349 | 358 | |
350 | 359 | protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { |
... | ... | @@ -366,12 +375,13 @@ public abstract class AbstractControllerTest { |
366 | 375 | return mockMvc.perform(postRequest); |
367 | 376 | } |
368 | 377 | |
369 | - protected <T> ResultActions doPostAsync(String urlTemplate, T content, String... params) throws Exception { | |
378 | + protected <T> ResultActions doPostAsync(String urlTemplate, T content, Long timeout, String... params) throws Exception { | |
370 | 379 | MockHttpServletRequestBuilder postRequest = post(urlTemplate); |
371 | 380 | setJwtToken(postRequest); |
372 | 381 | String json = json(content); |
373 | 382 | postRequest.contentType(contentType).content(json); |
374 | 383 | MvcResult result = mockMvc.perform(postRequest).andReturn(); |
384 | + result.getAsyncResult(timeout); | |
375 | 385 | return mockMvc.perform(asyncDispatch(result)); |
376 | 386 | } |
377 | 387 | |
... | ... | @@ -384,8 +394,8 @@ public abstract class AbstractControllerTest { |
384 | 394 | |
385 | 395 | protected void populateParams(MockHttpServletRequestBuilder request, String... params) { |
386 | 396 | if (params != null && params.length > 0) { |
387 | - Assert.assertEquals(params.length % 2, 0); | |
388 | - MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>(); | |
397 | + Assert.assertEquals(0, params.length % 2); | |
398 | + MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<>(); | |
389 | 399 | for (int i = 0; i < params.length; i += 2) { |
390 | 400 | paramsMap.add(params[i], params[i + 1]); |
391 | 401 | } | ... | ... |
... | ... | @@ -15,21 +15,23 @@ |
15 | 15 | */ |
16 | 16 | package org.thingsboard.server.mqtt.rpc; |
17 | 17 | |
18 | +import java.util.Arrays; | |
19 | + | |
20 | +import com.datastax.driver.core.utils.UUIDs; | |
21 | +import com.fasterxml.jackson.core.type.TypeReference; | |
18 | 22 | import lombok.extern.slf4j.Slf4j; |
19 | 23 | import org.apache.commons.lang3.StringUtils; |
20 | 24 | import org.eclipse.paho.client.mqttv3.*; |
21 | 25 | import org.junit.*; |
22 | -import org.springframework.http.HttpStatus; | |
23 | -import org.springframework.web.client.HttpClientErrorException; | |
26 | +import org.thingsboard.server.actors.plugin.PluginProcessingContext; | |
24 | 27 | import org.thingsboard.server.common.data.Device; |
25 | 28 | import org.thingsboard.server.common.data.Tenant; |
26 | 29 | import org.thingsboard.server.common.data.User; |
30 | +import org.thingsboard.server.common.data.page.TextPageData; | |
31 | +import org.thingsboard.server.common.data.plugin.PluginMetaData; | |
27 | 32 | import org.thingsboard.server.common.data.security.Authority; |
28 | 33 | import org.thingsboard.server.common.data.security.DeviceCredentials; |
29 | 34 | import org.thingsboard.server.controller.AbstractControllerTest; |
30 | -import org.thingsboard.server.dao.service.DaoNoSqlTest; | |
31 | - | |
32 | -import java.util.UUID; | |
33 | 35 | |
34 | 36 | import static org.junit.Assert.assertEquals; |
35 | 37 | import static org.junit.Assert.assertNotNull; |
... | ... | @@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. |
42 | 44 | public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { |
43 | 45 | |
44 | 46 | private static final String MQTT_URL = "tcp://localhost:1883"; |
45 | - private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered"; | |
47 | + private static final Long TIME_TO_HANDLE_REQUEST = 500L; | |
46 | 48 | |
47 | 49 | private Tenant savedTenant; |
48 | 50 | private User tenantAdmin; |
51 | + private Long asyncContextTimeoutToUseRpcPlugin; | |
52 | + | |
49 | 53 | |
50 | 54 | @Before |
51 | 55 | public void beforeTest() throws Exception { |
52 | 56 | loginSysAdmin(); |
53 | 57 | |
58 | + asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin(); | |
59 | + | |
54 | 60 | Tenant tenant = new Tenant(); |
55 | 61 | tenant.setTitle("My tenant"); |
56 | 62 | savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
... | ... | @@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
70 | 76 | public void afterTest() throws Exception { |
71 | 77 | loginSysAdmin(); |
72 | 78 | if (savedTenant != null) { |
73 | - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) | |
74 | - .andExpect(status().isOk()); | |
79 | + doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); | |
75 | 80 | } |
76 | 81 | } |
77 | 82 | |
... | ... | @@ -102,7 +107,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
102 | 107 | } |
103 | 108 | |
104 | 109 | @Test |
105 | - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200 | |
106 | 110 | public void testServerMqttOneWayRpcDeviceOffline() throws Exception { |
107 | 111 | Device device = new Device(); |
108 | 112 | device.setName("Test One-Way Server-Side RPC Device Offline"); |
... | ... | @@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
115 | 119 | |
116 | 120 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
117 | 121 | String deviceId = savedDevice.getId().getId().toString(); |
118 | - try { | |
119 | - doPost("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(408)); | |
120 | - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); | |
121 | - } catch (HttpClientErrorException e) { | |
122 | - log.error(e.getMessage(), e); | |
123 | - Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode()); | |
124 | - Assert.assertEquals("408 null", e.getMessage()); | |
125 | - } | |
122 | + | |
123 | + doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), | |
124 | + asyncContextTimeoutToUseRpcPlugin); | |
126 | 125 | } |
127 | 126 | |
128 | 127 | @Test |
129 | - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401 | |
130 | 128 | public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { |
131 | 129 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
132 | - String nonExistentDeviceId = UUID.randomUUID().toString(); | |
133 | - try { | |
134 | - doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400)); | |
135 | - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); | |
136 | - } catch (HttpClientErrorException e) { | |
137 | - log.error(e.getMessage(), e); | |
138 | - Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); | |
139 | - Assert.assertEquals("400 null", e.getMessage()); | |
140 | - } | |
130 | + String nonExistentDeviceId = UUIDs.timeBased().toString(); | |
131 | + | |
132 | + String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, | |
133 | + status().isNotFound()); | |
134 | + Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | |
141 | 135 | } |
142 | 136 | |
143 | 137 | @Test |
... | ... | @@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
168 | 162 | } |
169 | 163 | |
170 | 164 | @Test |
171 | - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200 | |
172 | 165 | public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { |
173 | 166 | Device device = new Device(); |
174 | 167 | device.setName("Test Two-Way Server-Side RPC Device Offline"); |
... | ... | @@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
181 | 174 | |
182 | 175 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
183 | 176 | String deviceId = savedDevice.getId().getId().toString(); |
184 | - try { | |
185 | - doPost("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(408)); | |
186 | - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); | |
187 | - } catch (HttpClientErrorException e) { | |
188 | - log.error(e.getMessage(), e); | |
189 | - Assert.assertEquals(HttpStatus.REQUEST_TIMEOUT, e.getStatusCode()); | |
190 | - Assert.assertEquals("408 null", e.getMessage()); | |
191 | - } | |
177 | + | |
178 | + doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isRequestTimeout(), | |
179 | + asyncContextTimeoutToUseRpcPlugin); | |
192 | 180 | } |
193 | 181 | |
194 | 182 | @Test |
195 | - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401 | |
196 | 183 | public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { |
197 | 184 | String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; |
198 | - String nonExistentDeviceId = UUID.randomUUID().toString(); | |
199 | - try { | |
200 | - doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400)); | |
201 | - Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); | |
202 | - } catch (HttpClientErrorException e) { | |
203 | - log.error(e.getMessage(), e); | |
204 | - Assert.assertEquals(HttpStatus.BAD_REQUEST, e.getStatusCode()); | |
205 | - Assert.assertEquals("400 null", e.getMessage()); | |
206 | - } | |
185 | + String nonExistentDeviceId = UUIDs.timeBased().toString(); | |
186 | + | |
187 | + String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, | |
188 | + status().isNotFound()); | |
189 | + Assert.assertEquals(PluginProcessingContext.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); | |
207 | 190 | } |
208 | 191 | |
209 | 192 | private Device getSavedDevice(Device device) throws Exception { |
... | ... | @@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
214 | 197 | return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
215 | 198 | } |
216 | 199 | |
200 | + private Long getAsyncContextTimeoutToUseRpcPlugin() throws Exception { | |
201 | + TextPageData<PluginMetaData> plugins = doGetTyped("/api/plugin/system?limit=1&textSearch=system rpc plugin", | |
202 | + new TypeReference<TextPageData<PluginMetaData>>(){}); | |
203 | + Long systemRpcPluginTimeout = plugins.getData().iterator().next().getConfiguration().get("defaultTimeout").asLong(); | |
204 | + return systemRpcPluginTimeout + TIME_TO_HANDLE_REQUEST; | |
205 | + } | |
206 | + | |
217 | 207 | private static class TestMqttCallback implements MqttCallback { |
218 | 208 | |
219 | 209 | private final MqttAsyncClient client; |
... | ... | @@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC |
228 | 218 | |
229 | 219 | @Override |
230 | 220 | public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
231 | - log.info("Message Arrived: " + mqttMessage.getPayload().toString()); | |
221 | + log.info("Message Arrived: " + Arrays.toString(mqttMessage.getPayload())); | |
232 | 222 | MqttMessage message = new MqttMessage(); |
233 | 223 | String responseTopic = requestTopic.replace("request", "response"); |
234 | - message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes()); | |
224 | + message.setPayload("{\"value1\":\"A\", \"value2\":\"B\"}".getBytes("UTF-8")); | |
235 | 225 | client.publish(responseTopic, message); |
236 | 226 | } |
237 | 227 | ... | ... |