Commit ca7ffd99c0480185b19dedb98c2781f85cd05cbc

Authored by Posi-Paka
Committed by Andrew Shvayka
1 parent e49c713a

Fix ignored MQTT integration tests.

@@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -62,6 +62,7 @@ public final class PluginProcessingContext implements PluginContext {
62 private static final Executor executor = Executors.newSingleThreadExecutor(); 62 private static final Executor executor = Executors.newSingleThreadExecutor();
63 public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!"; 63 public static final String CUSTOMER_USER_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "Customer user is not allowed to perform this operation!";
64 public static final String SYSTEM_ADMINISTRATOR_IS_NOT_ALLOWED_TO_PERFORM_THIS_OPERATION = "System administrator is not allowed to perform this operation!"; 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 private final SharedPluginProcessingContext pluginCtx; 67 private final SharedPluginProcessingContext pluginCtx;
67 private final Optional<PluginApiCallSecurityContext> securityCtx; 68 private final Optional<PluginApiCallSecurityContext> securityCtx;
@@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext { @@ -309,7 +310,7 @@ public final class PluginProcessingContext implements PluginContext {
309 ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId())); 310 ListenableFuture<Device> deviceFuture = pluginCtx.deviceService.findDeviceByIdAsync(new DeviceId(entityId.getId()));
310 Futures.addCallback(deviceFuture, getCallback(callback, device -> { 311 Futures.addCallback(deviceFuture, getCallback(callback, device -> {
311 if (device == null) { 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 } else { 314 } else {
314 if (!device.getTenantId().equals(ctx.getTenantId())) { 315 if (!device.getTenantId().equals(ctx.getTenantId())) {
315 return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!"); 316 return ValidationResult.accessDenied("Device doesn't belong to the current Tenant!");
@@ -106,6 +106,11 @@ public abstract class AbstractControllerTest { @@ -106,6 +106,11 @@ public abstract class AbstractControllerTest {
106 protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; 106 protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
107 private static final String CUSTOMER_USER_PASSWORD = "customer"; 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 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), 114 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
110 MediaType.APPLICATION_JSON.getSubtype(), 115 MediaType.APPLICATION_JSON.getSubtype(),
111 Charset.forName("utf8")); 116 Charset.forName("utf8"));
@@ -336,7 +341,7 @@ public abstract class AbstractControllerTest { @@ -336,7 +341,7 @@ public abstract class AbstractControllerTest {
336 } 341 }
337 342
338 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { 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 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception { 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,7 +349,11 @@ public abstract class AbstractControllerTest {
344 } 349 }
345 350
346 protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception { 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 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 359 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
@@ -366,12 +375,13 @@ public abstract class AbstractControllerTest { @@ -366,12 +375,13 @@ public abstract class AbstractControllerTest {
366 return mockMvc.perform(postRequest); 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 MockHttpServletRequestBuilder postRequest = post(urlTemplate); 379 MockHttpServletRequestBuilder postRequest = post(urlTemplate);
371 setJwtToken(postRequest); 380 setJwtToken(postRequest);
372 String json = json(content); 381 String json = json(content);
373 postRequest.contentType(contentType).content(json); 382 postRequest.contentType(contentType).content(json);
374 MvcResult result = mockMvc.perform(postRequest).andReturn(); 383 MvcResult result = mockMvc.perform(postRequest).andReturn();
  384 + result.getAsyncResult(timeout);
375 return mockMvc.perform(asyncDispatch(result)); 385 return mockMvc.perform(asyncDispatch(result));
376 } 386 }
377 387
@@ -384,8 +394,8 @@ public abstract class AbstractControllerTest { @@ -384,8 +394,8 @@ public abstract class AbstractControllerTest {
384 394
385 protected void populateParams(MockHttpServletRequestBuilder request, String... params) { 395 protected void populateParams(MockHttpServletRequestBuilder request, String... params) {
386 if (params != null && params.length > 0) { 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 for (int i = 0; i < params.length; i += 2) { 399 for (int i = 0; i < params.length; i += 2) {
390 paramsMap.add(params[i], params[i + 1]); 400 paramsMap.add(params[i], params[i + 1]);
391 } 401 }
@@ -15,21 +15,23 @@ @@ -15,21 +15,23 @@
15 */ 15 */
16 package org.thingsboard.server.mqtt.rpc; 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 import lombok.extern.slf4j.Slf4j; 22 import lombok.extern.slf4j.Slf4j;
19 import org.apache.commons.lang3.StringUtils; 23 import org.apache.commons.lang3.StringUtils;
20 import org.eclipse.paho.client.mqttv3.*; 24 import org.eclipse.paho.client.mqttv3.*;
21 import org.junit.*; 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 import org.thingsboard.server.common.data.Device; 27 import org.thingsboard.server.common.data.Device;
25 import org.thingsboard.server.common.data.Tenant; 28 import org.thingsboard.server.common.data.Tenant;
26 import org.thingsboard.server.common.data.User; 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 import org.thingsboard.server.common.data.security.Authority; 32 import org.thingsboard.server.common.data.security.Authority;
28 import org.thingsboard.server.common.data.security.DeviceCredentials; 33 import org.thingsboard.server.common.data.security.DeviceCredentials;
29 import org.thingsboard.server.controller.AbstractControllerTest; 34 import org.thingsboard.server.controller.AbstractControllerTest;
30 -import org.thingsboard.server.dao.service.DaoNoSqlTest;  
31 -  
32 -import java.util.UUID;  
33 35
34 import static org.junit.Assert.assertEquals; 36 import static org.junit.Assert.assertEquals;
35 import static org.junit.Assert.assertNotNull; 37 import static org.junit.Assert.assertNotNull;
@@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -42,15 +44,19 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
42 public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest { 44 public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest {
43 45
44 private static final String MQTT_URL = "tcp://localhost:1883"; 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 private Tenant savedTenant; 49 private Tenant savedTenant;
48 private User tenantAdmin; 50 private User tenantAdmin;
  51 + private Long asyncContextTimeoutToUseRpcPlugin;
  52 +
49 53
50 @Before 54 @Before
51 public void beforeTest() throws Exception { 55 public void beforeTest() throws Exception {
52 loginSysAdmin(); 56 loginSysAdmin();
53 57
  58 + asyncContextTimeoutToUseRpcPlugin = getAsyncContextTimeoutToUseRpcPlugin();
  59 +
54 Tenant tenant = new Tenant(); 60 Tenant tenant = new Tenant();
55 tenant.setTitle("My tenant"); 61 tenant.setTitle("My tenant");
56 savedTenant = doPost("/api/tenant", tenant, Tenant.class); 62 savedTenant = doPost("/api/tenant", tenant, Tenant.class);
@@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -70,8 +76,7 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
70 public void afterTest() throws Exception { 76 public void afterTest() throws Exception {
71 loginSysAdmin(); 77 loginSysAdmin();
72 if (savedTenant != null) { 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,7 +107,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
102 } 107 }
103 108
104 @Test 109 @Test
105 - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200  
106 public void testServerMqttOneWayRpcDeviceOffline() throws Exception { 110 public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
107 Device device = new Device(); 111 Device device = new Device();
108 device.setName("Test One-Way Server-Side RPC Device Offline"); 112 device.setName("Test One-Way Server-Side RPC Device Offline");
@@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -115,29 +119,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
115 119
116 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 120 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
117 String deviceId = savedDevice.getId().getId().toString(); 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 @Test 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 public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { 128 public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
131 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 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 @Test 137 @Test
@@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -168,7 +162,6 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
168 } 162 }
169 163
170 @Test 164 @Test
171 - @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200  
172 public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { 165 public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
173 Device device = new Device(); 166 Device device = new Device();
174 device.setName("Test Two-Way Server-Side RPC Device Offline"); 167 device.setName("Test Two-Way Server-Side RPC Device Offline");
@@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -181,29 +174,19 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
181 174
182 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 175 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
183 String deviceId = savedDevice.getId().getId().toString(); 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 @Test 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 public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { 183 public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
197 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 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 private Device getSavedDevice(Device device) throws Exception { 192 private Device getSavedDevice(Device device) throws Exception {
@@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -214,6 +197,13 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
214 return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 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 private static class TestMqttCallback implements MqttCallback { 207 private static class TestMqttCallback implements MqttCallback {
218 208
219 private final MqttAsyncClient client; 209 private final MqttAsyncClient client;
@@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC @@ -228,10 +218,10 @@ public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractC
228 218
229 @Override 219 @Override
230 public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { 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 MqttMessage message = new MqttMessage(); 222 MqttMessage message = new MqttMessage();
233 String responseTopic = requestTopic.replace("request", "response"); 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 client.publish(responseTopic, message); 225 client.publish(responseTopic, message);
236 } 226 }
237 227