Commit 8a37560b98877e16e71bd59317e053150ebca687

Authored by mp-loki
1 parent d570f087

Fixing Mqtt Integration tests

@@ -41,6 +41,7 @@ import org.springframework.context.annotation.Configuration; @@ -41,6 +41,7 @@ import org.springframework.context.annotation.Configuration;
41 import org.springframework.http.HttpHeaders; 41 import org.springframework.http.HttpHeaders;
42 import org.springframework.http.MediaType; 42 import org.springframework.http.MediaType;
43 import org.springframework.http.converter.HttpMessageConverter; 43 import org.springframework.http.converter.HttpMessageConverter;
  44 +import org.springframework.http.converter.StringHttpMessageConverter;
44 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; 45 import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
45 import org.springframework.mock.http.MockHttpInputMessage; 46 import org.springframework.mock.http.MockHttpInputMessage;
46 import org.springframework.mock.http.MockHttpOutputMessage; 47 import org.springframework.mock.http.MockHttpOutputMessage;
@@ -51,6 +52,7 @@ import org.springframework.test.context.TestPropertySource; @@ -51,6 +52,7 @@ import org.springframework.test.context.TestPropertySource;
51 import org.springframework.test.context.junit4.SpringRunner; 52 import org.springframework.test.context.junit4.SpringRunner;
52 import org.springframework.test.context.web.WebAppConfiguration; 53 import org.springframework.test.context.web.WebAppConfiguration;
53 import org.springframework.test.web.servlet.MockMvc; 54 import org.springframework.test.web.servlet.MockMvc;
  55 +import org.springframework.test.web.servlet.MvcResult;
54 import org.springframework.test.web.servlet.ResultActions; 56 import org.springframework.test.web.servlet.ResultActions;
55 import org.springframework.test.web.servlet.ResultMatcher; 57 import org.springframework.test.web.servlet.ResultMatcher;
56 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; 58 import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
@@ -97,28 +99,31 @@ public abstract class AbstractControllerTest { @@ -97,28 +99,31 @@ public abstract class AbstractControllerTest {
97 99
98 protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org"; 100 protected static final String SYS_ADMIN_EMAIL = "sysadmin@thingsboard.org";
99 private static final String SYS_ADMIN_PASSWORD = "sysadmin"; 101 private static final String SYS_ADMIN_PASSWORD = "sysadmin";
100 - 102 +
101 protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org"; 103 protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
102 private static final String TENANT_ADMIN_PASSWORD = "tenant"; 104 private static final String TENANT_ADMIN_PASSWORD = "tenant";
103 105
104 protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org"; 106 protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
105 private static final String CUSTOMER_USER_PASSWORD = "customer"; 107 private static final String CUSTOMER_USER_PASSWORD = "customer";
106 - 108 +
107 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(), 109 protected MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
108 MediaType.APPLICATION_JSON.getSubtype(), 110 MediaType.APPLICATION_JSON.getSubtype(),
109 Charset.forName("utf8")); 111 Charset.forName("utf8"));
110 112
111 protected MockMvc mockMvc; 113 protected MockMvc mockMvc;
112 - 114 +
113 protected String token; 115 protected String token;
114 protected String refreshToken; 116 protected String refreshToken;
115 protected String username; 117 protected String username;
116 118
117 private TenantId tenantId; 119 private TenantId tenantId;
118 - 120 +
119 @SuppressWarnings("rawtypes") 121 @SuppressWarnings("rawtypes")
120 private HttpMessageConverter mappingJackson2HttpMessageConverter; 122 private HttpMessageConverter mappingJackson2HttpMessageConverter;
121 - 123 +
  124 + @SuppressWarnings("rawtypes")
  125 + private HttpMessageConverter stringHttpMessageConverter;
  126 +
122 @Autowired 127 @Autowired
123 private WebApplicationContext webApplicationContext; 128 private WebApplicationContext webApplicationContext;
124 129
@@ -132,7 +137,7 @@ public abstract class AbstractControllerTest { @@ -132,7 +137,7 @@ public abstract class AbstractControllerTest {
132 log.info("Finished test: {}", description.getMethodName()); 137 log.info("Finished test: {}", description.getMethodName());
133 } 138 }
134 }; 139 };
135 - 140 +
136 @Autowired 141 @Autowired
137 void setConverters(HttpMessageConverter<?>[] converters) { 142 void setConverters(HttpMessageConverter<?>[] converters) {
138 143
@@ -141,10 +146,15 @@ public abstract class AbstractControllerTest { @@ -141,10 +146,15 @@ public abstract class AbstractControllerTest {
141 .findAny() 146 .findAny()
142 .get(); 147 .get();
143 148
  149 + this.stringHttpMessageConverter = Arrays.stream(converters)
  150 + .filter(hmc -> hmc instanceof StringHttpMessageConverter)
  151 + .findAny()
  152 + .get();
  153 +
144 Assert.assertNotNull("the JSON message converter must not be null", 154 Assert.assertNotNull("the JSON message converter must not be null",
145 this.mappingJackson2HttpMessageConverter); 155 this.mappingJackson2HttpMessageConverter);
146 } 156 }
147 - 157 +
148 @Before 158 @Before
149 public void setup() throws Exception { 159 public void setup() throws Exception {
150 log.info("Executing setup"); 160 log.info("Executing setup");
@@ -188,7 +198,7 @@ public abstract class AbstractControllerTest { @@ -188,7 +198,7 @@ public abstract class AbstractControllerTest {
188 public void teardown() throws Exception { 198 public void teardown() throws Exception {
189 log.info("Executing teardown"); 199 log.info("Executing teardown");
190 loginSysAdmin(); 200 loginSysAdmin();
191 - doDelete("/api/tenant/"+tenantId.getId().toString()) 201 + doDelete("/api/tenant/" + tenantId.getId().toString())
192 .andExpect(status().isOk()); 202 .andExpect(status().isOk());
193 log.info("Executed teardown"); 203 log.info("Executed teardown");
194 } 204 }
@@ -196,7 +206,7 @@ public abstract class AbstractControllerTest { @@ -196,7 +206,7 @@ public abstract class AbstractControllerTest {
196 protected void loginSysAdmin() throws Exception { 206 protected void loginSysAdmin() throws Exception {
197 login(SYS_ADMIN_EMAIL, SYS_ADMIN_PASSWORD); 207 login(SYS_ADMIN_EMAIL, SYS_ADMIN_PASSWORD);
198 } 208 }
199 - 209 +
200 protected void loginTenantAdmin() throws Exception { 210 protected void loginTenantAdmin() throws Exception {
201 login(TENANT_ADMIN_EMAIL, TENANT_ADMIN_PASSWORD); 211 login(TENANT_ADMIN_EMAIL, TENANT_ADMIN_PASSWORD);
202 } 212 }
@@ -204,13 +214,13 @@ public abstract class AbstractControllerTest { @@ -204,13 +214,13 @@ public abstract class AbstractControllerTest {
204 protected void loginCustomerUser() throws Exception { 214 protected void loginCustomerUser() throws Exception {
205 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD); 215 login(CUSTOMER_USER_EMAIL, CUSTOMER_USER_PASSWORD);
206 } 216 }
207 - 217 +
208 protected User createUserAndLogin(User user, String password) throws Exception { 218 protected User createUserAndLogin(User user, String password) throws Exception {
209 User savedUser = doPost("/api/user", user, User.class); 219 User savedUser = doPost("/api/user", user, User.class);
210 logout(); 220 logout();
211 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken) 221 doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
212 - .andExpect(status().isSeeOther())  
213 - .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken)); 222 + .andExpect(status().isSeeOther())
  223 + .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
214 JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", "activateToken", TestMailService.currentActivateToken, "password", password).andExpect(status().isOk()), JsonNode.class); 224 JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", "activateToken", TestMailService.currentActivateToken, "password", password).andExpect(status().isOk()), JsonNode.class);
215 validateAndSetJwtToken(tokenInfo, user.getEmail()); 225 validateAndSetJwtToken(tokenInfo, user.getEmail());
216 return savedUser; 226 return savedUser;
@@ -247,14 +257,14 @@ public abstract class AbstractControllerTest { @@ -247,14 +257,14 @@ public abstract class AbstractControllerTest {
247 Assert.assertNotNull(token); 257 Assert.assertNotNull(token);
248 Assert.assertFalse(token.isEmpty()); 258 Assert.assertFalse(token.isEmpty());
249 int i = token.lastIndexOf('.'); 259 int i = token.lastIndexOf('.');
250 - Assert.assertTrue(i>0);  
251 - String withoutSignature = token.substring(0, i+1);  
252 - Jwt<Header,Claims> jwsClaims = Jwts.parser().parseClaimsJwt(withoutSignature); 260 + Assert.assertTrue(i > 0);
  261 + String withoutSignature = token.substring(0, i + 1);
  262 + Jwt<Header, Claims> jwsClaims = Jwts.parser().parseClaimsJwt(withoutSignature);
253 Claims claims = jwsClaims.getBody(); 263 Claims claims = jwsClaims.getBody();
254 String subject = claims.getSubject(); 264 String subject = claims.getSubject();
255 Assert.assertEquals(username, subject); 265 Assert.assertEquals(username, subject);
256 } 266 }
257 - 267 +
258 protected void logout() throws Exception { 268 protected void logout() throws Exception {
259 this.token = null; 269 this.token = null;
260 this.refreshToken = null; 270 this.refreshToken = null;
@@ -266,24 +276,35 @@ public abstract class AbstractControllerTest { @@ -266,24 +276,35 @@ public abstract class AbstractControllerTest {
266 request.header(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM, "Bearer " + this.token); 276 request.header(ThingsboardSecurityConfiguration.JWT_TOKEN_HEADER_PARAM, "Bearer " + this.token);
267 } 277 }
268 } 278 }
269 - 279 +
270 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception { 280 protected ResultActions doGet(String urlTemplate, Object... urlVariables) throws Exception {
271 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables); 281 MockHttpServletRequestBuilder getRequest = get(urlTemplate, urlVariables);
272 setJwtToken(getRequest); 282 setJwtToken(getRequest);
273 return mockMvc.perform(getRequest); 283 return mockMvc.perform(getRequest);
274 } 284 }
275 - 285 +
276 protected <T> T doGet(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception { 286 protected <T> T doGet(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
277 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass); 287 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
278 } 288 }
279 - 289 +
  290 + protected <T> T doGetAsync(String urlTemplate, Class<T> responseClass, Object... urlVariables) throws Exception {
  291 + return readResponse(doGetAsync(urlTemplate, urlVariables).andExpect(status().isOk()), responseClass);
  292 + }
  293 +
  294 + protected ResultActions doGetAsync(String urlTemplate, Object... urlVariables) throws Exception {
  295 + MockHttpServletRequestBuilder getRequest;
  296 + getRequest = get(urlTemplate, urlVariables);
  297 + setJwtToken(getRequest);
  298 + return mockMvc.perform(asyncDispatch(mockMvc.perform(getRequest).andExpect(request().asyncStarted()).andReturn()));
  299 + }
  300 +
280 protected <T> T doGetTyped(String urlTemplate, TypeReference<T> responseType, Object... urlVariables) throws Exception { 301 protected <T> T doGetTyped(String urlTemplate, TypeReference<T> responseType, Object... urlVariables) throws Exception {
281 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseType); 302 return readResponse(doGet(urlTemplate, urlVariables).andExpect(status().isOk()), responseType);
282 } 303 }
283 - 304 +
284 protected <T> T doGetTypedWithPageLink(String urlTemplate, TypeReference<T> responseType, 305 protected <T> T doGetTypedWithPageLink(String urlTemplate, TypeReference<T> responseType,
285 - TextPageLink pageLink,  
286 - Object... urlVariables) throws Exception { 306 + TextPageLink pageLink,
  307 + Object... urlVariables) throws Exception {
287 List<Object> pageLinkVariables = new ArrayList<>(); 308 List<Object> pageLinkVariables = new ArrayList<>();
288 urlTemplate += "limit={limit}"; 309 urlTemplate += "limit={limit}";
289 pageLinkVariables.add(pageLink.getLimit()); 310 pageLinkVariables.add(pageLink.getLimit());
@@ -299,75 +320,93 @@ public abstract class AbstractControllerTest { @@ -299,75 +320,93 @@ public abstract class AbstractControllerTest {
299 urlTemplate += "&textOffset={textOffset}"; 320 urlTemplate += "&textOffset={textOffset}";
300 pageLinkVariables.add(pageLink.getTextOffset()); 321 pageLinkVariables.add(pageLink.getTextOffset());
301 } 322 }
302 -  
303 - Object[] vars = new Object[urlVariables.length + pageLinkVariables.size()]; 323 +
  324 + Object[] vars = new Object[urlVariables.length + pageLinkVariables.size()];
304 System.arraycopy(urlVariables, 0, vars, 0, urlVariables.length); 325 System.arraycopy(urlVariables, 0, vars, 0, urlVariables.length);
305 System.arraycopy(pageLinkVariables.toArray(), 0, vars, urlVariables.length, pageLinkVariables.size()); 326 System.arraycopy(pageLinkVariables.toArray(), 0, vars, urlVariables.length, pageLinkVariables.size());
306 - 327 +
307 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType); 328 return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
308 } 329 }
309 - 330 +
310 protected <T> T doPost(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 331 protected <T> T doPost(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
311 return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass); 332 return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
312 } 333 }
313 - 334 +
  335 + protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
  336 + return readResponse(doPost(urlTemplate, params).andExpect(resultMatcher), responseClass);
  337 + }
  338 +
314 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception { 339 protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception {
315 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); 340 return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
316 } 341 }
317 342
  343 + protected <T> T doPostAsync(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
  344 + return readResponse(doPostAsync(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
  345 + }
  346 +
318 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception { 347 protected <T> T doDelete(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
319 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass); 348 return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
320 } 349 }
321 - 350 +
322 protected ResultActions doPost(String urlTemplate, String... params) throws Exception { 351 protected ResultActions doPost(String urlTemplate, String... params) throws Exception {
323 MockHttpServletRequestBuilder postRequest = post(urlTemplate); 352 MockHttpServletRequestBuilder postRequest = post(urlTemplate);
324 setJwtToken(postRequest); 353 setJwtToken(postRequest);
325 populateParams(postRequest, params); 354 populateParams(postRequest, params);
326 return mockMvc.perform(postRequest); 355 return mockMvc.perform(postRequest);
327 } 356 }
328 -  
329 - protected <T> ResultActions doPost(String urlTemplate, T content, String... params) throws Exception { 357 +
  358 + protected <T> ResultActions doPost(String urlTemplate, T content, String... params) throws Exception {
330 MockHttpServletRequestBuilder postRequest = post(urlTemplate); 359 MockHttpServletRequestBuilder postRequest = post(urlTemplate);
331 setJwtToken(postRequest); 360 setJwtToken(postRequest);
332 String json = json(content); 361 String json = json(content);
333 postRequest.contentType(contentType).content(json); 362 postRequest.contentType(contentType).content(json);
334 - populateParams(postRequest, params);  
335 return mockMvc.perform(postRequest); 363 return mockMvc.perform(postRequest);
336 } 364 }
337 - 365 +
  366 + protected <T> ResultActions doPostAsync(String urlTemplate, T content, String... params) throws Exception {
  367 + MockHttpServletRequestBuilder postRequest = post(urlTemplate);
  368 + setJwtToken(postRequest);
  369 + String json = json(content);
  370 + postRequest.contentType(contentType).content(json);
  371 + MvcResult result = mockMvc.perform(postRequest).andReturn();
  372 + return mockMvc.perform(asyncDispatch(result));
  373 + }
  374 +
338 protected ResultActions doDelete(String urlTemplate, String... params) throws Exception { 375 protected ResultActions doDelete(String urlTemplate, String... params) throws Exception {
339 MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate); 376 MockHttpServletRequestBuilder deleteRequest = delete(urlTemplate);
340 setJwtToken(deleteRequest); 377 setJwtToken(deleteRequest);
341 populateParams(deleteRequest, params); 378 populateParams(deleteRequest, params);
342 return mockMvc.perform(deleteRequest); 379 return mockMvc.perform(deleteRequest);
343 } 380 }
344 - 381 +
345 protected void populateParams(MockHttpServletRequestBuilder request, String... params) { 382 protected void populateParams(MockHttpServletRequestBuilder request, String... params) {
346 if (params != null && params.length > 0) { 383 if (params != null && params.length > 0) {
347 Assert.assertEquals(params.length % 2, 0); 384 Assert.assertEquals(params.length % 2, 0);
348 MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>(); 385 MultiValueMap<String, String> paramsMap = new LinkedMultiValueMap<String, String>();
349 - for (int i=0;i<params.length;i+=2) {  
350 - paramsMap.add(params[i], params[i+1]); 386 + for (int i = 0; i < params.length; i += 2) {
  387 + paramsMap.add(params[i], params[i + 1]);
351 } 388 }
352 request.params(paramsMap); 389 request.params(paramsMap);
353 } 390 }
354 } 391 }
355 - 392 +
356 @SuppressWarnings("unchecked") 393 @SuppressWarnings("unchecked")
357 protected String json(Object o) throws IOException { 394 protected String json(Object o) throws IOException {
358 MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage(); 395 MockHttpOutputMessage mockHttpOutputMessage = new MockHttpOutputMessage();
359 - this.mappingJackson2HttpMessageConverter.write(  
360 - o, MediaType.APPLICATION_JSON, mockHttpOutputMessage); 396 +
  397 + HttpMessageConverter converter = o instanceof String ? stringHttpMessageConverter : mappingJackson2HttpMessageConverter;
  398 + converter.write(o, MediaType.APPLICATION_JSON, mockHttpOutputMessage);
361 return mockHttpOutputMessage.getBodyAsString(); 399 return mockHttpOutputMessage.getBodyAsString();
362 } 400 }
363 - 401 +
364 @SuppressWarnings("unchecked") 402 @SuppressWarnings("unchecked")
365 protected <T> T readResponse(ResultActions result, Class<T> responseClass) throws Exception { 403 protected <T> T readResponse(ResultActions result, Class<T> responseClass) throws Exception {
366 byte[] content = result.andReturn().getResponse().getContentAsByteArray(); 404 byte[] content = result.andReturn().getResponse().getContentAsByteArray();
367 MockHttpInputMessage mockHttpInputMessage = new MockHttpInputMessage(content); 405 MockHttpInputMessage mockHttpInputMessage = new MockHttpInputMessage(content);
368 - return (T) this.mappingJackson2HttpMessageConverter.read(responseClass, mockHttpInputMessage); 406 + HttpMessageConverter converter = responseClass.equals(String.class) ? stringHttpMessageConverter : mappingJackson2HttpMessageConverter;
  407 + return (T) converter.read(responseClass, mockHttpInputMessage);
369 } 408 }
370 - 409 +
371 protected <T> T readResponse(ResultActions result, TypeReference<T> type) throws Exception { 410 protected <T> T readResponse(ResultActions result, TypeReference<T> type) throws Exception {
372 byte[] content = result.andReturn().getResponse().getContentAsByteArray(); 411 byte[] content = result.andReturn().getResponse().getContentAsByteArray();
373 ObjectMapper mapper = new ObjectMapper(); 412 ObjectMapper mapper = new ObjectMapper();
application/src/test/java/org/thingsboard/server/mqtt/MqttNoSqlTestSuite.java renamed from application/src/test/java/org/thingsboard/server/mqtt/MqttTestSuite.java
@@ -25,8 +25,8 @@ import java.util.Arrays; @@ -25,8 +25,8 @@ import java.util.Arrays;
25 25
26 @RunWith(ClasspathSuite.class) 26 @RunWith(ClasspathSuite.class)
27 @ClasspathSuite.ClassnameFilters({ 27 @ClasspathSuite.ClassnameFilters({
28 - "org.thingsboard.server.mqtt.*.*Test"})  
29 -public class MqttTestSuite { 28 + "org.thingsboard.server.mqtt.*.nosql.*Test"})
  29 +public class MqttNoSqlTestSuite {
30 30
31 @ClassRule 31 @ClassRule
32 public static CustomCassandraCQLUnit cassandraUnit = 32 public static CustomCassandraCQLUnit cassandraUnit =
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt;
  17 +
  18 +import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
  19 +import org.junit.ClassRule;
  20 +import org.junit.extensions.cpsuite.ClasspathSuite;
  21 +import org.junit.runner.RunWith;
  22 +import org.thingsboard.server.dao.CustomCassandraCQLUnit;
  23 +import org.thingsboard.server.dao.CustomSqlUnit;
  24 +
  25 +import java.util.Arrays;
  26 +
  27 +@RunWith(ClasspathSuite.class)
  28 +@ClasspathSuite.ClassnameFilters({
  29 + "org.thingsboard.server.mqtt.rpc.sql.*Test", "org.thingsboard.server.mqtt.telemetry.sql.*Test"})
  30 +public class MqttSqlTestSuite {
  31 +
  32 + @ClassRule
  33 + public static CustomSqlUnit sqlUnit = new CustomSqlUnit(
  34 + Arrays.asList("sql/schema.sql", "sql/system-data.sql"),
  35 + "sql/drop-all-tables.sql",
  36 + "sql-test.properties");
  37 +}
application/src/test/java/org/thingsboard/server/mqtt/rpc/AbstractMqttServerSideRpcIntegrationTest.java renamed from application/src/test/java/org/thingsboard/server/mqtt/rpc/MqttServerSideRpcIntegrationTest.java
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 package org.thingsboard.server.mqtt.rpc; 16 package org.thingsboard.server.mqtt.rpc;
17 17
18 import lombok.extern.slf4j.Slf4j; 18 import lombok.extern.slf4j.Slf4j;
  19 +import org.apache.commons.lang3.StringUtils;
19 import org.eclipse.paho.client.mqttv3.*; 20 import org.eclipse.paho.client.mqttv3.*;
20 import org.junit.*; 21 import org.junit.*;
21 import org.springframework.http.HttpStatus; 22 import org.springframework.http.HttpStatus;
@@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.User; @@ -26,6 +27,7 @@ import org.thingsboard.server.common.data.User;
26 import org.thingsboard.server.common.data.security.Authority; 27 import org.thingsboard.server.common.data.security.Authority;
27 import org.thingsboard.server.common.data.security.DeviceCredentials; 28 import org.thingsboard.server.common.data.security.DeviceCredentials;
28 import org.thingsboard.server.controller.AbstractControllerTest; 29 import org.thingsboard.server.controller.AbstractControllerTest;
  30 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
29 31
30 import java.util.UUID; 32 import java.util.UUID;
31 33
@@ -37,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. @@ -37,7 +39,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
37 * @author Valerii Sosliuk 39 * @author Valerii Sosliuk
38 */ 40 */
39 @Slf4j 41 @Slf4j
40 -public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { 42 +public abstract class AbstractMqttServerSideRpcIntegrationTest extends AbstractControllerTest {
41 43
42 private static final String MQTT_URL = "tcp://localhost:1883"; 44 private static final String MQTT_URL = "tcp://localhost:1883";
43 private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered"; 45 private static final String FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED = "HttpClientErrorException expected, but not encountered";
@@ -67,13 +69,13 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -67,13 +69,13 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
67 @After 69 @After
68 public void afterTest() throws Exception { 70 public void afterTest() throws Exception {
69 loginSysAdmin(); 71 loginSysAdmin();
70 -  
71 - doDelete("/api/tenant/" + savedTenant.getId().getId().toString())  
72 - .andExpect(status().isOk()); 72 + if (savedTenant != null) {
  73 + doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
  74 + .andExpect(status().isOk());
  75 + }
73 } 76 }
74 77
75 @Test 78 @Test
76 - @Ignore  
77 public void testServerMqttOneWayRpc() throws Exception { 79 public void testServerMqttOneWayRpc() throws Exception {
78 Device device = new Device(); 80 Device device = new Device();
79 device.setName("Test One-Way Server-Side RPC"); 81 device.setName("Test One-Way Server-Side RPC");
@@ -95,12 +97,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -95,12 +97,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
95 97
96 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 98 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
97 String deviceId = savedDevice.getId().getId().toString(); 99 String deviceId = savedDevice.getId().getId().toString();
98 - String result = doPost("api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class);  
99 - Assert.assertNull(result); 100 + String result = doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().isOk());
  101 + Assert.assertTrue(StringUtils.isEmpty(result));
100 } 102 }
101 103
102 @Test 104 @Test
103 - @Ignore 105 + @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
104 public void testServerMqttOneWayRpcDeviceOffline() throws Exception { 106 public void testServerMqttOneWayRpcDeviceOffline() throws Exception {
105 Device device = new Device(); 107 Device device = new Device();
106 device.setName("Test One-Way Server-Side RPC Device Offline"); 108 device.setName("Test One-Way Server-Side RPC Device Offline");
@@ -114,7 +116,7 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -114,7 +116,7 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
114 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 116 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
115 String deviceId = savedDevice.getId().getId().toString(); 117 String deviceId = savedDevice.getId().getId().toString();
116 try { 118 try {
117 - doPost("api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class); 119 + doPost("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(408));
118 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); 120 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
119 } catch (HttpClientErrorException e) { 121 } catch (HttpClientErrorException e) {
120 log.error(e.getMessage(), e); 122 log.error(e.getMessage(), e);
@@ -124,12 +126,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -124,12 +126,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
124 } 126 }
125 127
126 @Test 128 @Test
127 - @Ignore 129 + @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
128 public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { 130 public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception {
129 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 131 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
130 String nonExistentDeviceId = UUID.randomUUID().toString(); 132 String nonExistentDeviceId = UUID.randomUUID().toString();
131 try { 133 try {
132 - doPost("api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class); 134 + doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
133 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); 135 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
134 } catch (HttpClientErrorException e) { 136 } catch (HttpClientErrorException e) {
135 log.error(e.getMessage(), e); 137 log.error(e.getMessage(), e);
@@ -139,7 +141,6 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -139,7 +141,6 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
139 } 141 }
140 142
141 @Test 143 @Test
142 - @Ignore  
143 public void testServerMqttTwoWayRpc() throws Exception { 144 public void testServerMqttTwoWayRpc() throws Exception {
144 Device device = new Device(); 145 Device device = new Device();
145 device.setName("Test Two-Way Server-Side RPC"); 146 device.setName("Test Two-Way Server-Side RPC");
@@ -161,12 +162,13 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -161,12 +162,13 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
161 162
162 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 163 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
163 String deviceId = savedDevice.getId().getId().toString(); 164 String deviceId = savedDevice.getId().getId().toString();
164 - String result = getStringResult(setGpioRequest, "twoway", deviceId); 165 +
  166 + String result = doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().isOk());
165 Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result); 167 Assert.assertEquals("{\"value1\":\"A\",\"value2\":\"B\"}", result);
166 } 168 }
167 169
168 @Test 170 @Test
169 - @Ignore 171 + @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 408 but was: 200
170 public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { 172 public void testServerMqttTwoWayRpcDeviceOffline() throws Exception {
171 Device device = new Device(); 173 Device device = new Device();
172 device.setName("Test Two-Way Server-Side RPC Device Offline"); 174 device.setName("Test Two-Way Server-Side RPC Device Offline");
@@ -180,7 +182,7 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -180,7 +182,7 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
180 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 182 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
181 String deviceId = savedDevice.getId().getId().toString(); 183 String deviceId = savedDevice.getId().getId().toString();
182 try { 184 try {
183 - doPost("api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class); 185 + doPost("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(408));
184 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); 186 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
185 } catch (HttpClientErrorException e) { 187 } catch (HttpClientErrorException e) {
186 log.error(e.getMessage(), e); 188 log.error(e.getMessage(), e);
@@ -190,12 +192,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -190,12 +192,12 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
190 } 192 }
191 193
192 @Test 194 @Test
193 - @Ignore 195 + @Ignore // TODO: figure out the right error code for this case. Ignored due to failure: expected 400 (404?) but was: 401
194 public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { 196 public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception {
195 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}"; 197 String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"23\",\"value\": 1}}";
196 String nonExistentDeviceId = UUID.randomUUID().toString(); 198 String nonExistentDeviceId = UUID.randomUUID().toString();
197 try { 199 try {
198 - doPost("api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class); 200 + doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, status().is(400));
199 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED); 201 Assert.fail(FAIL_MSG_IF_HTTP_CLIENT_ERROR_NOT_ENCOUNTERED);
200 } catch (HttpClientErrorException e) { 202 } catch (HttpClientErrorException e) {
201 log.error(e.getMessage(), e); 203 log.error(e.getMessage(), e);
@@ -212,10 +214,6 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest { @@ -212,10 +214,6 @@ public class MqttServerSideRpcIntegrationTest extends AbstractControllerTest {
212 return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); 214 return doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
213 } 215 }
214 216
215 - private String getStringResult(String requestData, String callType, String deviceId) throws Exception {  
216 - return doPost("api/plugins/rpc/" + callType + "/" + deviceId, requestData, String.class);  
217 - }  
218 -  
219 private static class TestMqttCallback implements MqttCallback { 217 private static class TestMqttCallback implements MqttCallback {
220 218
221 private final MqttAsyncClient client; 219 private final MqttAsyncClient client;
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest;
  20 +
  21 +/**
  22 + * Created by Valerii Sosliuk on 8/22/2017.
  23 + */
  24 +@DaoNoSqlTest
  25 +public class MqttServerSideRpcNoSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
  26 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.rpc.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcIntegrationTest;
  21 +
  22 +/**
  23 + * Created by Valerii Sosliuk on 8/22/2017.
  24 + */
  25 +@DaoSqlTest
  26 +public class MqttServerSideRpcSqlIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest {
  27 +}
application/src/test/java/org/thingsboard/server/mqtt/telemetry/AbstractMqttTelemetryIntegrationTest.java renamed from application/src/test/java/org/thingsboard/server/mqtt/telemetry/MqttTelemetryIntegrationTest.java
@@ -26,11 +26,10 @@ import org.springframework.web.util.UriComponentsBuilder; @@ -26,11 +26,10 @@ import org.springframework.web.util.UriComponentsBuilder;
26 import org.thingsboard.server.common.data.Device; 26 import org.thingsboard.server.common.data.Device;
27 import org.thingsboard.server.common.data.security.DeviceCredentials; 27 import org.thingsboard.server.common.data.security.DeviceCredentials;
28 import org.thingsboard.server.controller.AbstractControllerTest; 28 import org.thingsboard.server.controller.AbstractControllerTest;
  29 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
29 30
30 import java.net.URI; 31 import java.net.URI;
31 -import java.util.Arrays;  
32 -import java.util.List;  
33 -import java.util.Map; 32 +import java.util.*;
34 33
35 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertEquals;
36 import static org.junit.Assert.assertNotNull; 35 import static org.junit.Assert.assertNotNull;
@@ -39,7 +38,7 @@ import static org.junit.Assert.assertNotNull; @@ -39,7 +38,7 @@ import static org.junit.Assert.assertNotNull;
39 * @author Valerii Sosliuk 38 * @author Valerii Sosliuk
40 */ 39 */
41 @Slf4j 40 @Slf4j
42 -public class MqttTelemetryIntegrationTest extends AbstractControllerTest { 41 +public abstract class AbstractMqttTelemetryIntegrationTest extends AbstractControllerTest {
43 42
44 private static final String MQTT_URL = "tcp://localhost:1883"; 43 private static final String MQTT_URL = "tcp://localhost:1883";
45 44
@@ -64,7 +63,6 @@ public class MqttTelemetryIntegrationTest extends AbstractControllerTest { @@ -64,7 +63,6 @@ public class MqttTelemetryIntegrationTest extends AbstractControllerTest {
64 } 63 }
65 64
66 @Test 65 @Test
67 - @Ignore  
68 public void testPushMqttRpcData() throws Exception { 66 public void testPushMqttRpcData() throws Exception {
69 String clientId = MqttAsyncClient.generateClientId(); 67 String clientId = MqttAsyncClient.generateClientId();
70 MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId); 68 MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId);
@@ -80,13 +78,16 @@ public class MqttTelemetryIntegrationTest extends AbstractControllerTest { @@ -80,13 +78,16 @@ public class MqttTelemetryIntegrationTest extends AbstractControllerTest {
80 String deviceId = savedDevice.getId().getId().toString(); 78 String deviceId = savedDevice.getId().getId().toString();
81 79
82 Thread.sleep(1000); 80 Thread.sleep(1000);
83 - Object keys = doGet("/api/plugins/telemetry/" + deviceId + "/keys/timeseries", Object.class);  
84 - assertEquals(Arrays.asList("key1", "key2", "key3", "key4"), keys); 81 + List<String> actualKeys = doGetAsync("/api/plugins/telemetry/DEVICE/" + deviceId + "/keys/timeseries", List.class);
  82 + Set<String> actualKeySet = new HashSet<>(actualKeys);
85 83
86 - UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl("/api/plugins/telemetry/" + deviceId + "/values/timeseries")  
87 - .queryParam("keys", String.join(",", (CharSequence[]) keys));  
88 - URI uri = builder.build().encode().toUri();  
89 - Map<String, List<Map<String, String>>> values = doGet(uri.getPath(), Map.class); 84 + List<String> expectedKeys = Arrays.asList("key1", "key2", "key3", "key4");
  85 + Set<String> expectedKeySet = new HashSet<>(expectedKeys);
  86 +
  87 + assertEquals(expectedKeySet, actualKeySet);
  88 +
  89 + String getTelemetryValuesUrl = "/api/plugins/telemetry/DEVICE/" + deviceId + "/values/timeseries?keys=" + String.join(",", actualKeySet);
  90 + Map<String, List<Map<String, String>>> values = doGetAsync(getTelemetryValuesUrl, Map.class);
90 91
91 assertEquals("value1", values.get("key1").get(0).get("value")); 92 assertEquals("value1", values.get("key1").get(0).get("value"));
92 assertEquals("true", values.get("key2").get(0).get("value")); 93 assertEquals("true", values.get("key2").get(0).get("value"));
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.nosql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest;
  20 +
  21 +/**
  22 + * Created by Valerii Sosliuk on 8/22/2017.
  23 + */
  24 +@DaoNoSqlTest
  25 +public class MqttTelemetryNoSqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest {
  26 +}
  1 +/**
  2 + * Copyright © 2016-2017 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.mqtt.telemetry.sql;
  17 +
  18 +import org.thingsboard.server.dao.service.DaoNoSqlTest;
  19 +import org.thingsboard.server.dao.service.DaoSqlTest;
  20 +import org.thingsboard.server.mqtt.telemetry.AbstractMqttTelemetryIntegrationTest;
  21 +
  22 +/**
  23 + * Created by Valerii Sosliuk on 8/22/2017.
  24 + */
  25 +@DaoSqlTest
  26 +public class MqttTelemetrySqlIntegrationTest extends AbstractMqttTelemetryIntegrationTest {
  27 +}