Browse Source

Merge pull request #6431 from smatvienko-tb/tests-speedup-tenant

[3.4] Tests performance improvements
pull/6519/head
Andrew Shvayka 4 years ago
committed by GitHub
parent
commit
761e36be7f
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java
  2. 57
      application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
  3. 34
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  4. 54
      application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java
  5. 96
      application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
  6. 371
      application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
  7. 355
      application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
  8. 244
      application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java
  9. 382
      application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
  10. 119
      application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
  11. 50
      application/src/test/java/org/thingsboard/server/service/script/MockJsInvokeService.java
  12. 2
      application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java
  13. 24
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  14. 2
      application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java
  15. 35
      application/src/test/resources/application-test.properties
  16. 4
      common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceTypeFilter.java
  17. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java
  18. 2
      common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java
  19. 76
      dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java
  20. 45
      dao/src/test/resources/application-test.properties
  21. 11
      dao/src/test/resources/nosql-test.properties
  22. 7
      dao/src/test/resources/sql-test.properties
  23. 32
      msa/black-box-tests/src/test/resources/logback.xml
  24. 23
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java
  25. 2
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNodeConfiguration.java
  26. 80
      rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java

2
application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java

@ -22,6 +22,7 @@ import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.core.annotation.Order;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
@ -84,6 +85,7 @@ import static springfox.documentation.builders.PathSelectors.regex;
@Slf4j
@Configuration
@TbCoreComponent
@Profile("!test")
public class SwaggerConfiguration {
@Value("${swagger.api_path_regex}")

57
application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java

@ -16,16 +16,25 @@
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@ -33,9 +42,49 @@ import org.springframework.test.context.web.WebAppConfiguration;
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@Configuration
@ComponentScan({"org.thingsboard.server"})
@WebAppConfiguration
@SpringBootTest()
@EnableWebSocket
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public abstract class AbstractControllerTest extends AbstractWebTest {
public abstract class AbstractControllerTest extends AbstractWebTest {
public static final String WS_URL = "ws://localhost:";
@LocalServerPort
protected int wsPort;
private TbTestWebSocketClient wsClient; // lazy
public TbTestWebSocketClient getWsClient() {
if (wsClient == null) {
synchronized (this) {
try {
if (wsClient == null) {
wsClient = buildAndConnectWebSocketClient();
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return wsClient;
}
@Before
public void beforeWsTest() throws Exception {
// placeholder
}
@After
public void afterWsTest() throws Exception {
if (wsClient != null) {
wsClient.close();
}
}
private TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token));
assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue();
return wsClient;
}
}

34
application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java

@ -18,6 +18,9 @@ package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwt;
@ -67,6 +70,7 @@ import org.thingsboard.server.common.data.edge.Edge;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.HasId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.page.TimePageLink;
@ -97,6 +101,7 @@ import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppC
@Slf4j
public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
public static final int TIMEOUT = 30;
protected ObjectMapper mapper = new ObjectMapper();
@ -106,7 +111,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
private static final String SYS_ADMIN_PASSWORD = "sysadmin";
protected static final String TENANT_ADMIN_EMAIL = "testtenant@thingsboard.org";
private static final String TENANT_ADMIN_PASSWORD = "tenant";
protected static final String TENANT_ADMIN_PASSWORD = "tenant";
protected static final String CUSTOMER_USER_EMAIL = "testcustomer@thingsboard.org";
private static final String CUSTOMER_USER_PASSWORD = "customer";
@ -509,16 +514,24 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType);
}
protected <T> T doPost(String urlTemplate, Class<T> responseClass, String... params) throws Exception {
return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
protected <T> T doPost(String urlTemplate, Class<T> responseClass, String... params) {
try {
return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, ResultMatcher resultMatcher, String... params) throws Exception {
return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass);
}
protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) throws Exception {
return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
protected <T> T doPost(String urlTemplate, T content, Class<T> responseClass, String... params) {
try {
return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
protected <T, R> R doPostWithResponse(String urlTemplate, T content, Class<R> responseClass, String... params) throws Exception {
@ -639,4 +652,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20));
return edge;
}
protected <T extends HasId<? extends UUIDBased>> ListenableFuture<List<ResultActions>> deleteEntitiesAsync(String urlTemplate, List<T> entities, ListeningExecutorService executor) {
List<ListenableFuture<ResultActions>> futures = new ArrayList<>(entities.size());
for (T entity : entities) {
futures.add(executor.submit(() ->
doDelete(urlTemplate + entity.getId().getId())
.andExpect(status().isOk())));
}
return Futures.allAsList(futures);
}
}

54
application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java

@ -1,54 +0,0 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.junit.Assert;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootContextLoader;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import java.net.URI;
import java.net.URISyntaxException;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AbstractControllerTest.class, loader = SpringBootContextLoader.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
@Configuration
@ComponentScan({"org.thingsboard.server"})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@Slf4j
public abstract class AbstractWebsocketTest extends AbstractWebTest {
protected static final String WS_URL = "ws://localhost:";
@LocalServerPort
protected int wsPort;
protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException {
TbTestWebSocketClient wsClient = new TbTestWebSocketClient(new URI(WS_URL + wsPort + "/api/ws/plugins/telemetry?token=" + token));
Assert.assertTrue(wsClient.connectBlocking());
return wsClient;
}
}

96
application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java

@ -15,16 +15,17 @@
*/
package org.thingsboard.server.controller;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
@ -32,20 +33,28 @@ import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.junit.Assert;
import org.junit.Test;
import com.fasterxml.jackson.core.type.TypeReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
public abstract class BaseCustomerControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<Customer>> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() {
};
private IdComparator<Customer> idComparator = new IdComparator<>();
ListeningExecutorService executor;
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
loginSysAdmin();
Tenant tenant = new Tenant();
@ -65,6 +74,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
@After
public void afterTest() throws Exception {
executor.shutdownNow();
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
@ -83,11 +94,11 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
savedCustomer.setTitle("My new customer");
doPost("/api/customer", savedCustomer, Customer.class);
Customer foundCustomer = doGet("/api/customer/"+savedCustomer.getId().getId().toString(), Customer.class);
Customer foundCustomer = doGet("/api/customer/" + savedCustomer.getId().getId().toString(), Customer.class);
Assert.assertEquals(foundCustomer.getTitle(), savedCustomer.getTitle());
doDelete("/api/customer/"+savedCustomer.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/customer/" + savedCustomer.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
@ -182,29 +193,30 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
public void testFindCustomers() throws Exception {
TenantId tenantId = savedTenant.getId();
List<Customer> customers = new ArrayList<>();
List<ListenableFuture<Customer>> futures = new ArrayList<>(135);
for (int i = 0; i < 135; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
customer.setTitle("Customer" + i);
customers.add(doPost("/api/customer", customer, Customer.class));
futures.add(executor.submit(() ->
doPost("/api/customer", customer, Customer.class)));
}
List<Customer> customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Customer> loadedCustomers = new ArrayList<>();
List<Customer> loadedCustomers = new ArrayList<>(135);
PageLink pageLink = new PageLink(23);
PageData<Customer> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomers.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(customers, idComparator);
Collections.sort(loadedCustomers, idComparator);
assertThat(customers).containsExactlyInAnyOrderElementsOf(loadedCustomers);
Assert.assertEquals(customers, loadedCustomers);
deleteEntitiesAsync("/api/customer/", loadedCustomers, executor).get(TIMEOUT, TimeUnit.SECONDS);
}
@Test
@ -212,7 +224,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
TenantId tenantId = savedTenant.getId();
String title1 = "Customer title 1";
List<Customer> customersTitle1 = new ArrayList<>();
List<ListenableFuture<Customer>> futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
@ -220,10 +232,13 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle1.add(doPost("/api/customer", customer, Customer.class));
futures.add(executor.submit(() ->
doPost("/api/customer", customer, Customer.class)));
}
List<Customer> customersTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Customer title 2";
List<Customer> customersTitle2 = new ArrayList<>();
futures = new ArrayList<>(175);
for (int i = 0; i < 175; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
@ -231,57 +246,48 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle2.add(doPost("/api/customer", customer, Customer.class));
futures.add(executor.submit(() ->
doPost("/api/customer", customer, Customer.class)));
}
List<Customer> customersTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Customer> loadedCustomersTitle1 = new ArrayList<>();
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Customer> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomersTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(customersTitle1, idComparator);
Collections.sort(loadedCustomersTitle1, idComparator);
Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
assertThat(customersTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle1);
List<Customer> loadedCustomersTitle2 = new ArrayList<>();
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
loadedCustomersTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(customersTitle2, idComparator);
Collections.sort(loadedCustomersTitle2, idComparator);
assertThat(customersTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle2);
Assert.assertEquals(customersTitle2, loadedCustomersTitle2);
for (Customer customer : loadedCustomersTitle1) {
doDelete("/api/customer/" + customer.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/customer/", loadedCustomersTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Customer customer : loadedCustomersTitle2) {
doDelete("/api/customer/" + customer.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/customer/", loadedCustomersTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference<PageData<Customer>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}

371
application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java

@ -17,11 +17,17 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntitySubtype;
@ -41,22 +47,32 @@ import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@Slf4j
public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<Device>> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() {
};
private IdComparator<Device> idComparator = new IdComparator<>();
ListeningExecutorService executor;
List<ListenableFuture<Device>> futures;
PageData<Device> pageData;
private Tenant savedTenant;
private User tenantAdmin;
@Before
public void beforeTest() throws Exception {
log.debug("beforeTest");
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
loginSysAdmin();
Tenant tenant = new Tenant();
@ -76,10 +92,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@After
public void afterTest() throws Exception {
log.debug("afterTest...");
executor.shutdownNow();
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
doDelete("/api/tenant/" + savedTenant.getId().getId())
.andExpect(status().isOk());
log.debug("afterTest done");
}
@Test
@ -98,7 +118,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals(device.getName(), savedDevice.getName());
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertNotNull(deviceCredentials);
Assert.assertNotNull(deviceCredentials.getId());
@ -110,7 +130,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
savedDevice.setName("My new device");
doPost("/api/device", savedDevice, Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(foundDevice.getName(), savedDevice.getName());
}
@ -145,7 +165,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setName("My device");
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertNotNull(foundDevice);
Assert.assertEquals(savedDevice, foundDevice);
}
@ -180,6 +200,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
Assert.assertEquals("typeA", deviceTypes.get(0).getType());
Assert.assertEquals("typeB", deviceTypes.get(1).getType());
Assert.assertEquals("typeC", deviceTypes.get(2).getType());
deleteEntitiesAsync("/api/device/", devices, executor).get(TIMEOUT, TimeUnit.SECONDS);
}
@Test
@ -189,10 +211,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doDelete("/api/device/" + savedDevice.getId().getId().toString())
doDelete("/api/device/" + savedDevice.getId().getId())
.andExpect(status().isOk());
doGet("/api/device/" + savedDevice.getId().getId().toString())
doGet("/api/device/" + savedDevice.getId().getId())
.andExpect(status().isNotFound());
}
@ -224,18 +246,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
customer.setTitle("My customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
Device assignedDevice = doPost("/api/customer/" + savedCustomer.getId().getId()
+ "/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(savedCustomer.getId(), assignedDevice.getCustomerId());
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
Device foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(savedCustomer.getId(), foundDevice.getCustomerId());
Device unassignedDevice =
doDelete("/api/customer/device/" + savedDevice.getId().getId().toString(), Device.class);
doDelete("/api/customer/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, unassignedDevice.getCustomerId().getId());
foundDevice = doGet("/api/device/" + savedDevice.getId().getId().toString(), Device.class);
foundDevice = doGet("/api/device/" + savedDevice.getId().getId(), Device.class);
Assert.assertEquals(ModelConstants.NULL_UUID, foundDevice.getCustomerId().getId());
}
@ -246,7 +268,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + Uuids.timeBased().toString()
+ "/device/" + savedDevice.getId().getId().toString())
+ "/device/" + savedDevice.getId().getId())
.andExpect(status().isNotFound());
}
@ -279,13 +301,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/customer/" + savedCustomer.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString())
doPost("/api/customer/" + savedCustomer.getId().getId()
+ "/device/" + savedDevice.getId().getId())
.andExpect(status().isForbidden());
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant2.getId().getId().toString())
doDelete("/api/tenant/" + savedTenant2.getId().getId())
.andExpect(status().isOk());
}
@ -296,7 +318,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
}
@ -307,7 +329,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId());
deviceCredentials.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
deviceCredentials.setCredentialsId("access_token");
@ -315,7 +337,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
DeviceCredentials foundDeviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
Assert.assertEquals(deviceCredentials, foundDeviceCredentials);
}
@ -334,7 +356,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsType(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
@ -348,7 +370,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setCredentialsId(null);
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isBadRequest())
@ -362,7 +384,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
DeviceCredentials newDeviceCredentials = new DeviceCredentials(new DeviceCredentialsId(Uuids.timeBased()));
newDeviceCredentials.setCreatedTime(deviceCredentials.getCreatedTime());
newDeviceCredentials.setDeviceId(deviceCredentials.getDeviceId());
@ -380,7 +402,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class);
doGet("/api/device/" + savedDevice.getId().getId() + "/credentials", DeviceCredentials.class);
deviceCredentials.setDeviceId(new DeviceId(Uuids.timeBased()));
doPost("/api/device/credentials", deviceCredentials)
.andExpect(status().isNotFound());
@ -388,19 +410,24 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
@Test
public void testFindTenantDevices() throws Exception {
List<Device> devices = new ArrayList<>();
log.debug("testFindTenantDevices");
futures = new ArrayList<>(178);
for (int i = 0; i < 178; i++) {
Device device = new Device();
device.setName("Device" + i);
device.setType("default");
devices.add(doPost("/api/device", device, Device.class));
futures.add(executor.submit(() ->
doPost("/api/device", device, Device.class)));
}
List<Device> loadedDevices = new ArrayList<>();
log.debug("await create devices");
List<Device> devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("start reading");
List<Device> loadedDevices = new ArrayList<>(178);
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
@ -408,16 +435,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
}
} while (pageData.hasNext());
Collections.sort(devices, idComparator);
Collections.sort(loadedDevices, idComparator);
Assert.assertEquals(devices, loadedDevices);
log.debug("asserting");
assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
log.debug("delete devices async");
deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("done");
}
@Test
public void testFindTenantDevicesByName() throws Exception {
String title1 = "Device title 1";
List<Device> devicesTitle1 = new ArrayList<>();
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -425,10 +454,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle1.add(doPost("/api/device", device, Device.class));
futures.add(executor.submit(() ->
doPost("/api/device", device, Device.class)));
}
List<Device> devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Device title 2";
List<Device> devicesTitle2 = new ArrayList<>();
futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -436,59 +468,50 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
devicesTitle2.add(doPost("/api/device", device, Device.class));
futures.add(executor.submit(() ->
doPost("/api/device", device, Device.class)));
}
List<Device> devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Device> loadedDevicesTitle1 = new ArrayList<>();
List<Device> loadedDevicesTitle1 = new ArrayList<>(143);
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle1, idComparator);
Collections.sort(loadedDevicesTitle1, idComparator);
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
assertThat(devicesTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
List<Device> loadedDevicesTitle2 = new ArrayList<>(75);
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle2, idComparator);
Collections.sort(loadedDevicesTitle2, idComparator);
assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2);
Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
deleteEntitiesAsync("/api/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
for (Device device : loadedDevicesTitle1) {
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesTitle2) {
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenant/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -497,7 +520,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
public void testFindTenantDevicesByType() throws Exception {
String title1 = "Device title 1";
String type1 = "typeA";
List<Device> devicesType1 = new ArrayList<>();
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -505,11 +528,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
devicesType1.add(doPost("/api/device", device, Device.class));
futures.add(executor.submit(() ->
doPost("/api/device", device, Device.class)));
if (i == 0) {
futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
}
}
List<Device> devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Device title 2";
String type2 = "typeB";
List<Device> devicesType2 = new ArrayList<>();
futures = new ArrayList<>(75);
for (int i = 0; i < 75; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -517,61 +546,54 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
devicesType2.add(doPost("/api/device", device, Device.class));
futures.add(executor.submit(() ->
doPost("/api/device", device, Device.class)));
if (i == 0) {
futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
}
}
List<Device> loadedDevicesType1 = new ArrayList<>();
List<Device> devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Device> loadedDevicesType1 = new ArrayList<>(143);
PageLink pageLink = new PageLink(15);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
List<Device> loadedDevicesType2 = new ArrayList<>(75);
pageLink = new PageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2);
for (Device device : loadedDevicesType1) {
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
doDelete("/api/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -583,32 +605,35 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
customer = doPost("/api/customer", customer, Customer.class);
CustomerId customerId = customer.getId();
List<Device> devices = new ArrayList<>();
futures = new ArrayList<>(128);
for (int i = 0; i < 128; i++) {
Device device = new Device();
device.setName("Device" + i);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devices.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
ListenableFuture<Device> future = executor.submit(() -> doPost("/api/device", device, Device.class));
futures.add(Futures.transform(future, (dev) ->
doPost("/api/customer/" + customerId.getId()
+ "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
List<Device> loadedDevices = new ArrayList<>();
List<Device> devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Device> loadedDevices = new ArrayList<>(128);
PageLink pageLink = new PageLink(23);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevices.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devices, idComparator);
Collections.sort(loadedDevices, idComparator);
assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices);
Assert.assertEquals(devices, loadedDevices);
log.debug("delete devices async");
deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("done");
}
@Test
@ -619,7 +644,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
CustomerId customerId = customer.getId();
String title1 = "Device title 1";
List<Device> devicesTitle1 = new ArrayList<>();
futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -627,12 +652,15 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
ListenableFuture<Device> future = executor.submit(() -> doPost("/api/device", device, Device.class));
futures.add(Futures.transform(future, (dev) ->
doPost("/api/customer/" + customerId.getId()
+ "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
List<Device> devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Device title 2";
List<Device> devicesTitle2 = new ArrayList<>();
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -640,61 +668,52 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType("default");
device = doPost("/api/device", device, Device.class);
devicesTitle2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
ListenableFuture<Device> future = executor.submit(() -> doPost("/api/device", device, Device.class));
futures.add(Futures.transform(future, (dev) ->
doPost("/api/customer/" + customerId.getId()
+ "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
}
List<Device> devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Device> loadedDevicesTitle1 = new ArrayList<>();
List<Device> loadedDevicesTitle1 = new ArrayList<>(125);
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle1, idComparator);
Collections.sort(loadedDevicesTitle1, idComparator);
assertThat(devicesTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle1);
Assert.assertEquals(devicesTitle1, loadedDevicesTitle1);
List<Device> loadedDevicesTitle2 = new ArrayList<>();
List<Device> loadedDevicesTitle2 = new ArrayList<>(143);
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
loadedDevicesTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesTitle2, idComparator);
Collections.sort(loadedDevicesTitle2, idComparator);
assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2);
Assert.assertEquals(devicesTitle2, loadedDevicesTitle2);
deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
for (Device device : loadedDevicesTitle1) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesTitle2) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?",
new TypeReference<PageData<Device>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -708,7 +727,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
String title1 = "Device title 1";
String type1 = "typeC";
List<Device> devicesType1 = new ArrayList<>();
futures = new ArrayList<>(125);
for (int i = 0; i < 125; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -716,13 +735,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type1);
device = doPost("/api/device", device, Device.class);
devicesType1.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
ListenableFuture<Device> future = executor.submit(() -> doPost("/api/device", device, Device.class));
futures.add(Futures.transform(future, (dev) ->
doPost("/api/customer/" + customerId.getId()
+ "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
if (i == 0) {
futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
}
}
List<Device> devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Device title 2";
String type2 = "typeD";
List<Device> devicesType2 = new ArrayList<>();
futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Device device = new Device();
String suffix = RandomStringUtils.randomAlphanumeric(15);
@ -730,63 +755,55 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase();
device.setName(name);
device.setType(type2);
device = doPost("/api/device", device, Device.class);
devicesType2.add(doPost("/api/customer/" + customerId.getId().toString()
+ "/device/" + device.getId().getId().toString(), Device.class));
ListenableFuture<Device> future = executor.submit(() -> doPost("/api/device", device, Device.class));
futures.add(Futures.transform(future, (dev) ->
doPost("/api/customer/" + customerId.getId()
+ "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor()));
if (i == 0) {
futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time
}
}
List<Device> devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Device> loadedDevicesType1 = new ArrayList<>();
List<Device> loadedDevicesType1 = new ArrayList<>(125);
PageLink pageLink = new PageLink(15);
PageData<Device> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
loadedDevicesType1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType1, idComparator);
Collections.sort(loadedDevicesType1, idComparator);
assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1);
Assert.assertEquals(devicesType1, loadedDevicesType1);
List<Device> loadedDevicesType2 = new ArrayList<>();
List<Device> loadedDevicesType2 = new ArrayList<>(143);
pageLink = new PageLink(4);
do {
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
loadedDevicesType2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(devicesType2, idComparator);
Collections.sort(loadedDevicesType2, idComparator);
Assert.assertEquals(devicesType2, loadedDevicesType2);
assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2);
for (Device device : loadedDevicesType1) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/customer/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type1);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type1);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Device device : loadedDevicesType2) {
doDelete("/api/customer/device/" + device.getId().getId().toString())
.andExpect(status().isOk());
}
deleteEntitiesAsync("/api/customer/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&",
new TypeReference<PageData<Device>>(){}, pageLink, type2);
pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&",
PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
}
@ -828,17 +845,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
login("tenant2@thingsboard.org", "testPassword1");
Device assignedDevice = doPost("/api/tenant/" + savedDifferentTenant.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class);
doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class, status().isNotFound());
doGet("/api/device/" + assignedDevice.getId().getId(), Device.class, status().isNotFound());
login("tenant9@thingsboard.org", "testPassword1");
Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId().toString(), Device.class);
Device foundDevice1 = doGet("/api/device/" + assignedDevice.getId().getId(), Device.class);
Assert.assertNotNull(foundDevice1);
doGet("/api/relation?fromId=" + savedDevice.getId().getId() + "&fromType=DEVICE&relationType=Contains&toId=" + savedAnotherDevice.getId().getId() + "&toType=DEVICE", EntityRelation.class, status().isNotFound());
loginSysAdmin();
doDelete("/api/tenant/" + savedDifferentTenant.getId().getId().toString())
doDelete("/api/tenant/" + savedDifferentTenant.getId().getId())
.andExpect(status().isOk());
}
@ -852,19 +869,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
device.setType("default");
Device savedDevice = doPost("/api/device", device, Device.class);
doPost("/api/edge/" + savedEdge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
doPost("/api/edge/" + savedEdge.getId().getId()
+ "/device/" + savedDevice.getId().getId(), Device.class);
PageData<Device> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100));
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
doDelete("/api/edge/" + savedEdge.getId().getId().toString()
+ "/device/" + savedDevice.getId().getId().toString(), Device.class);
doDelete("/api/edge/" + savedEdge.getId().getId()
+ "/device/" + savedDevice.getId().getId(), Device.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?",
new TypeReference<PageData<Device>>() {}, new PageLink(100));
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?",
PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}

355
application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java

@ -17,8 +17,13 @@ package org.thingsboard.server.controller;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken;
import org.eclipse.paho.client.mqttv3.MqttAsyncClient;
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.eclipse.paho.client.mqttv3.MqttMessage;
@ -28,7 +33,8 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityView;
@ -41,73 +47,71 @@ import org.thingsboard.server.common.data.objects.AttributesEntityView;
import org.thingsboard.server.common.data.objects.TelemetryEntityView;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.query.DeviceTypeFilter;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.dao.model.ModelConstants;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.hamcrest.Matchers.containsString;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@TestPropertySource(properties = {
"transport.mqtt.enabled=true",
"js.evaluator=mock",
})
@Slf4j
public abstract class BaseEntityViewControllerTest extends AbstractControllerTest {
static final TypeReference<PageData<EntityView>> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<EntityViewInfo>> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() {
};
private IdComparator<EntityView> idComparator;
private Tenant savedTenant;
private User tenantAdmin;
private Device testDevice;
private TelemetryEntityView telemetry;
List<ListenableFuture<ResultActions>> deleteFutures = new ArrayList<>();
ListeningExecutorService executor;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
idComparator = new IdComparator<>();
savedTenant = doPost("/api/tenant", getNewTenant("My tenant"), Tenant.class);
Assert.assertNotNull(savedTenant);
log.debug("beforeTest");
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
loginTenantAdmin();
Device device = new Device();
device.setName("Test device");
device.setName("Test device 4view");
device.setType("default");
testDevice = doPost("/api/device", device, Device.class);
telemetry = new TelemetryEntityView(
Arrays.asList("tsKey1", "tsKey2", "tsKey3"),
List.of("tsKey1", "tsKey2", "tsKey3"),
new AttributesEntityView(
Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"),
Arrays.asList("saKey1", "saKey2", "saKey3", "saKey4"),
Arrays.asList("shKey1", "shKey2", "shKey3", "shKey4")));
List.of("caKey1", "caKey2", "caKey3", "caKey4"),
List.of("saKey1", "saKey2", "saKey3", "saKey4"),
List.of("shKey1", "shKey2", "shKey3", "shKey4")));
}
@After
public void afterTest() throws Exception {
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
executor.shutdownNow();
}
@Test
@ -126,7 +130,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Assert.assertNotNull(savedView);
Assert.assertNotNull(savedView.getId());
Assert.assertTrue(savedView.getCreatedTime() > 0);
assertEquals(savedTenant.getId(), savedView.getTenantId());
assertEquals(tenantId, savedView.getTenantId());
Assert.assertNotNull(savedView.getCustomerId());
assertEquals(NULL_UUID, savedView.getCustomerId().getId());
assertEquals(name, savedView.getName());
@ -233,7 +237,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Customer customer = getNewCustomer("Different customer");
Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
login(tenantAdmin.getEmail(), "testPassword1");
login(TENANT_ADMIN_EMAIL, TENANT_ADMIN_PASSWORD);
EntityView savedView = getNewSavedEntityView("Test entity view");
@ -252,21 +256,18 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
CustomerId customerId = customer.getId();
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?";
List<EntityViewInfo> views = new ArrayList<>();
List<ListenableFuture<EntityViewInfo>> viewFutures = new ArrayList<>(128);
for (int i = 0; i < 128; i++) {
views.add(
String entityName = "Test entity view " + i;
viewFutures.add(executor.submit(() ->
new EntityViewInfo(doPost("/api/customer/" + customerId.getId().toString() + "/entityView/"
+ getNewSavedEntityView("Test entity view " + i).getId().getId().toString(), EntityView.class),
customer.getTitle(), customer.isPublic())
);
+ getNewSavedEntityView(entityName).getId().getId().toString(), EntityView.class),
customer.getTitle(), customer.isPublic())));
}
List<EntityViewInfo> entityViewInfos = Futures.allAsList(viewFutures).get(TIMEOUT, SECONDS);
List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), urlTemplate);
Collections.sort(views, idComparator);
Collections.sort(loadedViews, idComparator);
assertEquals(views, loadedViews);
assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews);
}
@Test
@ -275,35 +276,37 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViews?";
String name1 = "Entity view name1";
List<EntityView> namesOfView1 = fillListOf(125, name1, "/api/customer/" + customerId.getId().toString()
+ "/entityView/");
List<EntityView> namesOfView1 = Futures.allAsList(fillListByTemplate(125, name1, "/api/customer/" + customerId.getId().toString()
+ "/entityView/")).get(TIMEOUT, SECONDS);
List<EntityView> loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), urlTemplate);
Collections.sort(namesOfView1, idComparator);
Collections.sort(loadedNamesOfView1, idComparator);
assertEquals(namesOfView1, loadedNamesOfView1);
assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1);
String name2 = "Entity view name2";
List<EntityView> NamesOfView2 = fillListOf(143, name2, "/api/customer/" + customerId.getId().toString()
+ "/entityView/");
List<EntityView> namesOfView2 = Futures.allAsList(fillListByTemplate(143, name2, "/api/customer/" + customerId.getId().toString()
+ "/entityView/")).get(TIMEOUT, SECONDS);
List<EntityView> loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), urlTemplate);
Collections.sort(NamesOfView2, idComparator);
Collections.sort(loadedNamesOfView2, idComparator);
assertEquals(NamesOfView2, loadedNamesOfView2);
assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
deleteFutures.clear();
for (EntityView view : loadedNamesOfView1) {
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
deleteFutures.add(executor.submit(() ->
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
PageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate,
new TypeReference<PageData<EntityView>>() {
}, new PageLink(4, 0, name1));
Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
PageData<EntityView> pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
deleteFutures.clear();
for (EntityView view : loadedNamesOfView2) {
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
deleteFutures.add(executor.submit(() ->
doDelete("/api/customer/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityView>>() {
},
Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
@ -311,49 +314,51 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testGetTenantEntityViews() throws Exception {
List<EntityViewInfo> views = new ArrayList<>();
List<ListenableFuture<EntityViewInfo>> entityViewInfoFutures = new ArrayList<>(178);
for (int i = 0; i < 178; i++) {
views.add(new EntityViewInfo(getNewSavedEntityView("Test entity view" + i), null, false));
ListenableFuture<EntityView> entityViewFuture = getNewSavedEntityViewAsync("Test entity view" + i);
entityViewInfoFutures.add(Futures.transform(entityViewFuture,
view -> new EntityViewInfo(view, null, false),
MoreExecutors.directExecutor()));
}
List<EntityViewInfo> entityViewInfos = Futures.allAsList(entityViewInfoFutures).get(TIMEOUT, SECONDS);
List<EntityViewInfo> loadedViews = loadListOfInfo(new PageLink(23), "/api/tenant/entityViewInfos?");
Collections.sort(views, idComparator);
Collections.sort(loadedViews, idComparator);
assertEquals(views, loadedViews);
assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews);
}
@Test
public void testGetTenantEntityViewsByName() throws Exception {
String name1 = "Entity view name1";
List<EntityView> namesOfView1 = fillListOf(143, name1);
List<EntityView> loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), "/api/tenant/entityViews?");
Collections.sort(namesOfView1, idComparator);
Collections.sort(loadedNamesOfView1, idComparator);
assertEquals(namesOfView1, loadedNamesOfView1);
List<EntityView> namesOfView1 = Futures.allAsList(fillListOf(17, name1)).get(TIMEOUT, SECONDS);
List<EntityView> loadedNamesOfView1 = loadListOf(new PageLink(5, 0, name1), "/api/tenant/entityViews?");
assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1);
String name2 = "Entity view name2";
List<EntityView> NamesOfView2 = fillListOf(75, name2);
List<EntityView> namesOfView2 = Futures.allAsList(fillListOf(15, name2)).get(TIMEOUT, SECONDS);
;
List<EntityView> loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), "/api/tenant/entityViews?");
Collections.sort(NamesOfView2, idComparator);
Collections.sort(loadedNamesOfView2, idComparator);
assertEquals(NamesOfView2, loadedNamesOfView2);
assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2);
deleteFutures.clear();
for (EntityView view : loadedNamesOfView1) {
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
deleteFutures.add(executor.submit(() ->
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
PageData<EntityView> pageData = doGetTypedWithPageLink("/api/tenant/entityViews?",
new TypeReference<PageData<EntityView>>() {
}, new PageLink(4, 0, name1));
Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
PageData<EntityView> pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name1));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
deleteFutures.clear();
for (EntityView view : loadedNamesOfView2) {
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk());
deleteFutures.add(executor.submit(() ->
doDelete("/api/entityView/" + view.getId().getId().toString()).andExpect(status().isOk())));
}
pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", new TypeReference<PageData<EntityView>>() {
},
Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS);
pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", PAGE_DATA_ENTITY_VIEW_TYPE_REF,
new PageLink(4, 0, name2));
Assert.assertFalse(pageData.hasNext());
assertEquals(0, pageData.getData().size());
@ -361,20 +366,21 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testTheCopyOfAttrsIntoTSForTheView() throws Exception {
Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set<String> actualAttributesSet =
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}");
Set<String> expectedActualAttributesSet =
new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet));
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
log.debug("got correct actualAttributesSet, saving new entity view...");
EntityView savedView = getNewSavedEntityView("Test entity view");
Thread.sleep(1000);
List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
log.debug("fetching entity view telemetry...");
List<Map<String, Object>> values = await("telemetry/ENTITY_VIEW")
.atMost(TIMEOUT, SECONDS)
.until(() -> doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {
}),
x -> x.size() >= expectedActualAttributesSet.size());
log.debug("asserting...");
assertEquals("value1", getValue(values, "caKey1"));
assertEquals(true, getValue(values, "caKey2"));
assertEquals(42.0, getValue(values, "caKey3"));
@ -383,18 +389,17 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
@Test
public void testTheCopyOfAttrsOutOfTSForTheView() throws Exception {
Set<String> expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4");
Set<String> actualAttributesSet =
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}");
Set<String> expectedActualAttributesSet = new HashSet<>(Arrays.asList("caKey1", "caKey2", "caKey3", "caKey4"));
assertTrue(actualAttributesSet.containsAll(expectedActualAttributesSet));
getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet);
List<Map<String, Object>> valueTelemetryOfDevices = doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + testDevice.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {
});
EntityView view = new EntityView();
view.setEntityId(testDevice.getId());
view.setTenantId(savedTenant.getId());
view.setTenantId(tenantId);
view.setName("Test entity view");
view.setType("default");
view.setKeys(telemetry);
@ -402,28 +407,49 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
view.setEndTimeMs((long) getValue(valueTelemetryOfDevices, "lastActivityTime") / 10);
EntityView savedView = doPost("/api/entityView", view, EntityView.class);
Thread.sleep(1000);
List<Map<String, Object>> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() +
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {});
"/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {
});
assertEquals(0, values.size());
}
@Test
public void testGetTelemetryWhenEntityViewTimeRangeInsideTimestampRange() throws Exception {
uploadTelemetry("{\"tsKey1\":\"value1\", \"tsKey2\":true, \"tsKey3\":40.0}");
Thread.sleep(1000);
DeviceTypeFilter dtf = new DeviceTypeFilter(testDevice.getType(), testDevice.getName());
List<String> tsKeys = List.of("tsKey1", "tsKey2", "tsKey3");
DeviceCredentials deviceCredentials = doGet("/api/device/" + testDevice.getId().getId() + "/credentials", DeviceCredentials.class);
assertEquals(testDevice.getId(), deviceCredentials.getDeviceId());
String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken);
long now = System.currentTimeMillis();
getWsClient().subscribeTsUpdate(tsKeys, now, TimeUnit.HOURS.toMillis(1), dtf);
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value1\", \"tsKey2\":true, \"tsKey3\":40.0}", accessToken);
getWsClient().waitForUpdate();
long startTimeMs = System.currentTimeMillis();
uploadTelemetry("{\"tsKey1\":\"value2\", \"tsKey2\":false, \"tsKey3\":80.0}");
Thread.sleep(1000);
uploadTelemetry("{\"tsKey1\":\"value3\", \"tsKey2\":false, \"tsKey3\":120.0}");
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value2\", \"tsKey2\":false, \"tsKey3\":80.0}", accessToken);
getWsClient().waitForUpdate();
Thread.sleep(3);
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value3\", \"tsKey2\":false, \"tsKey3\":120.0}", accessToken);
getWsClient().waitForUpdate();
long endTimeMs = System.currentTimeMillis();
uploadTelemetry("{\"tsKey1\":\"value4\", \"tsKey2\":true, \"tsKey3\":160.0}");
getWsClient().registerWaitForUpdate();
uploadTelemetry("{\"tsKey1\":\"value4\", \"tsKey2\":true, \"tsKey3\":160.0}", accessToken);
getWsClient().waitForUpdate();
String deviceId = testDevice.getId().getId().toString();
Set<String> keys = getTelemetryKeys("DEVICE", deviceId);
Thread.sleep(1000);
EntityView view = createEntityView("Test entity view", startTimeMs, endTimeMs);
EntityView savedView = doPost("/api/entityView", view, EntityView.class);
@ -440,14 +466,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
Assert.assertEquals(1, actualValues.get("tsKey3").size());
}
private void uploadTelemetry(String strKvs) throws Exception {
private void uploadTelemetry(String strKvs, String accessToken) throws Exception {
String viewDeviceId = testDevice.getId().getId().toString();
DeviceCredentials deviceCredentials =
doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class);
assertEquals(testDevice.getId(), deviceCredentials.getDeviceId());
String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken);
String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
@ -455,35 +475,45 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
client.connect(options);
awaitConnected(client, TimeUnit.SECONDS.toMillis(30));
awaitConnected(client, SECONDS.toMillis(30));
MqttMessage message = new MqttMessage();
message.setPayload(strKvs.getBytes());
client.publish("v1/devices/me/telemetry", message);
Thread.sleep(1000);
IMqttDeliveryToken token = client.publish("v1/devices/me/telemetry", message);
await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null);
client.disconnect();
}
private void awaitConnected(MqttAsyncClient client, long ms) throws InterruptedException {
long start = System.currentTimeMillis();
while (!client.isConnected()) {
Thread.sleep(100);
if (start + ms < System.currentTimeMillis()) {
throw new RuntimeException("Client is not connected!");
}
}
await("awaitConnected").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS)
.until(client::isConnected);
}
private Set<String> getTelemetryKeys(String type, String id) throws Exception {
return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", new TypeReference<>() {}));
return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/timeseries", new TypeReference<>() {
}));
}
private Set<String> getAttributeKeys(String type, String id) throws Exception {
return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/attributes", new TypeReference<>() {
}));
}
private Map<String, List<Map<String, String>>> getTelemetryValues(String type, String id, Set<String> keys, Long startTs, Long endTs) throws Exception {
return doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id +
"/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, new TypeReference<>() {});
"/values/timeseries?keys=" + String.join(",", keys) + "&startTs=" + startTs + "&endTs=" + endTs, new TypeReference<>() {
});
}
private Set<String> getAttributesByKeys(String stringKV) throws Exception {
private Set<String> getAttributesByKeys(String stringKV, Set<String> expectedKeySet) throws Exception {
DeviceTypeFilter dtf = new DeviceTypeFilter(testDevice.getType(), testDevice.getName());
List<EntityKey> keysToSubscribe = expectedKeySet.stream()
.map(key -> new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, key))
.collect(Collectors.toList());
getWsClient().subscribeLatestUpdate(keysToSubscribe, dtf);
String viewDeviceId = testDevice.getId().getId().toString();
log.debug("deviceid {}", viewDeviceId);
DeviceCredentials deviceCredentials =
doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class);
assertEquals(testDevice.getId(), deviceCredentials.getDeviceId());
@ -491,20 +521,24 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
String accessToken = deviceCredentials.getCredentialsId();
assertNotNull(accessToken);
log.debug("creating mqtt client...");
String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken);
client.connect(options);
awaitConnected(client, TimeUnit.SECONDS.toMillis(30));
awaitConnected(client, SECONDS.toMillis(30));
log.debug("mqtt connected...");
MqttMessage message = new MqttMessage();
message.setPayload((stringKV).getBytes());
client.publish("v1/devices/me/attributes", message);
Thread.sleep(1000);
client.disconnect();
return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", new TypeReference<>() {}));
getWsClient().registerWaitForUpdate();
IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message);
log.debug("publish token.message {}", token.getMessage());
await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null);
log.debug("token.message delivered {}", token.getMessage());
assertThat(getWsClient().waitForUpdate()).as("ws update received").isNotBlank();
return getAttributeKeys("DEVICE", viewDeviceId);
}
private Object getValue(List<Map<String, Object>> values, String stringValue) {
@ -514,15 +548,19 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
.findFirst().get().get("value");
}
private EntityView getNewSavedEntityView(String name) throws Exception {
private EntityView getNewSavedEntityView(String name) {
EntityView view = createEntityView(name, 0, 0);
return doPost("/api/entityView", view, EntityView.class);
}
private ListenableFuture<EntityView> getNewSavedEntityViewAsync(String name) {
return executor.submit(() -> getNewSavedEntityView(name));
}
private EntityView createEntityView(String name, long startTimeMs, long endTimeMs) {
EntityView view = new EntityView();
view.setEntityId(testDevice.getId());
view.setTenantId(savedTenant.getId());
view.setTenantId(tenantId);
view.setName(name);
view.setType("default");
view.setKeys(telemetry);
@ -543,33 +581,41 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
return tenant;
}
private List<EntityView> fillListOf(int limit, String partOfName, String urlTemplate) throws Exception {
List<EntityView> views = new ArrayList<>();
for (EntityView view : fillListOf(limit, partOfName)) {
views.add(doPost(urlTemplate + view.getId().getId().toString(), EntityView.class));
private List<ListenableFuture<EntityView>> fillListByTemplate(int limit, String partOfName, String urlTemplate) {
List<ListenableFuture<EntityView>> futures = new ArrayList<>(limit);
for (ListenableFuture<EntityView> viewFuture : fillListOf(limit, partOfName)) {
futures.add(Futures.transform(viewFuture, view ->
doPost(urlTemplate + view.getId().getId().toString(), EntityView.class),
MoreExecutors.directExecutor()));
}
return views;
return futures;
}
private List<EntityView> fillListOf(int limit, String partOfName) throws Exception {
List<EntityView> viewNames = new ArrayList<>();
private List<ListenableFuture<EntityView>> fillListOf(int limit, String partOfName) {
List<ListenableFuture<EntityView>> viewNameFutures = new ArrayList<>(limit);
for (int i = 0; i < limit; i++) {
String fullName = partOfName + ' ' + RandomStringUtils.randomAlphanumeric(15);
fullName = i % 2 == 0 ? fullName.toLowerCase() : fullName.toUpperCase();
EntityView view = getNewSavedEntityView(fullName);
Customer customer = getNewCustomer("Test customer " + String.valueOf(Math.random()));
view.setCustomerId(doPost("/api/customer", customer, Customer.class).getId());
viewNames.add(doPost("/api/entityView", view, EntityView.class));
boolean even = i % 2 == 0;
ListenableFuture<CustomerId> customerFuture = executor.submit(() -> {
Customer customer = getNewCustomer("Test customer " + Math.random());
return doPost("/api/customer", customer, Customer.class).getId();
});
viewNameFutures.add(Futures.transform(customerFuture, customerId -> {
String fullName = partOfName + ' ' + RandomStringUtils.randomAlphanumeric(15);
fullName = even ? fullName.toLowerCase() : fullName.toUpperCase();
EntityView view = getNewSavedEntityView(fullName);
view.setCustomerId(customerId);
return doPost("/api/entityView", view, EntityView.class);
}, MoreExecutors.directExecutor()));
}
return viewNames;
return viewNameFutures;
}
private List<EntityView> loadListOf(PageLink pageLink, String urlTemplate) throws Exception {
List<EntityView> loadedItems = new ArrayList<>();
PageData<EntityView> pageData;
do {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityView>>() {
}, pageLink);
pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, pageLink);
loadedItems.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -583,8 +629,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
List<EntityViewInfo> loadedItems = new ArrayList<>();
PageData<EntityViewInfo> pageData;
do {
pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference<PageData<EntityViewInfo>>() {
}, pageLink);
pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF, pageLink);
loadedItems.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
@ -608,7 +653,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
PageData<EntityView> pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?",
new TypeReference<PageData<EntityView>>() {}, new PageLink(100));
PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(100));
Assert.assertEquals(1, pageData.getData().size());
@ -616,7 +661,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
+ "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class);
pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?",
new TypeReference<PageData<EntityView>>() {}, new PageLink(100));
PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(100));
Assert.assertEquals(0, pageData.getData().size());
}

244
application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java

@ -16,24 +16,55 @@
package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.TenantInfo;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@TestPropertySource(properties = {
"js.evaluator=mock",
})
@Slf4j
public abstract class BaseTenantControllerTest extends AbstractControllerTest {
private IdComparator<Tenant> idComparator = new IdComparator<>();
static final TypeReference<PageData<Tenant>> PAGE_DATA_TENANT_TYPE_REF = new TypeReference<>() {
};
static final TypeReference<PageData<TenantInfo>> PAGE_DATA_TENANT_INFO_TYPE_REF = new TypeReference<>() {
};
ListeningExecutorService executor;
@Before
public void setUp() throws Exception {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
}
@After
public void tearDown() throws Exception {
executor.shutdownNow();
}
@Test
public void testSaveTenant() throws Exception {
@ -47,10 +78,10 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Assert.assertEquals(tenant.getTitle(), savedTenant.getTitle());
savedTenant.setTitle("My new tenant");
doPost("/api/tenant", savedTenant, Tenant.class);
Tenant foundTenant = doGet("/api/tenant/"+savedTenant.getId().getId().toString(), Tenant.class);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertEquals(foundTenant.getTitle(), savedTenant.getTitle());
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
@ -60,18 +91,18 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
tenant.setTitle(RandomStringUtils.randomAlphanumeric(300));
doPost("/api/tenant", tenant).andExpect(statusReason(containsString("length of title must be equal or less than 255")));
}
@Test
public void testFindTenantById() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Tenant foundTenant = doGet("/api/tenant/"+savedTenant.getId().getId().toString(), Tenant.class);
Tenant foundTenant = doGet("/api/tenant/" + savedTenant.getId().getId().toString(), Tenant.class);
Assert.assertNotNull(foundTenant);
Assert.assertEquals(savedTenant, foundTenant);
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
@ -80,22 +111,22 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
TenantInfo foundTenant = doGet("/api/tenant/info/"+savedTenant.getId().getId().toString(), TenantInfo.class);
TenantInfo foundTenant = doGet("/api/tenant/info/" + savedTenant.getId().getId().toString(), TenantInfo.class);
Assert.assertNotNull(foundTenant);
Assert.assertEquals(new TenantInfo(savedTenant, "Default"), foundTenant);
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
}
@Test
public void testSaveTenantWithEmptyTitle() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant title should be specified")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Tenant title should be specified")));
}
@Test
public void testSaveTenantWithInvalidEmail() throws Exception {
loginSysAdmin();
@ -103,140 +134,147 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
tenant.setTitle("My tenant");
tenant.setEmail("invalid@mail");
doPost("/api/tenant", tenant)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format")));
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString("Invalid email address format")));
}
@Test
public void testDeleteTenant() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class);
doDelete("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/tenant/"+savedTenant.getId().getId().toString())
.andExpect(status().isNotFound());
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isOk());
doGet("/api/tenant/" + savedTenant.getId().getId().toString())
.andExpect(status().isNotFound());
}
@Test
public void testFindTenants() throws Exception {
loginSysAdmin();
List<Tenant> tenants = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<Tenant> pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
PageData<Tenant> pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
tenants.addAll(pageData.getData());
for (int i=0;i<56;i++) {
List<ListenableFuture<Tenant>> createFutures = new ArrayList<>(56);
for (int i = 0; i < 56; i++) {
Tenant tenant = new Tenant();
tenant.setTitle("Tenant"+i);
tenants.add(doPost("/api/tenant", tenant, Tenant.class));
tenant.setTitle("Tenant" + i);
createFutures.add(executor.submit(() ->
doPost("/api/tenant", tenant, Tenant.class)));
}
tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS));
List<Tenant> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
loadedTenants.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenants, idComparator);
Collections.sort(loadedTenants, idComparator);
Assert.assertEquals(tenants, loadedTenants);
for (Tenant tenant : loadedTenants) {
if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
doDelete("/api/tenant/"+tenant.getId().getId().toString())
.andExpect(status().isOk());
}
}
assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants);
deleteEntitiesAsync("/api/tenant/", loadedTenants.stream()
.filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle()))
.collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
}
@Test
public void testFindTenantsByTitle() throws Exception {
log.debug("login sys admin");
loginSysAdmin();
log.debug("test started");
String title1 = "Tenant title 1";
List<Tenant> tenantsTitle1 = new ArrayList<>();
for (int i=0;i<134;i++) {
List<ListenableFuture<Tenant>> createFutures = new ArrayList<>(134);
for (int i = 0; i < 134; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
String title = title1+suffix;
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);
tenantsTitle1.add(doPost("/api/tenant", tenant, Tenant.class));
createFutures.add(executor.submit(() ->
doPost("/api/tenant", tenant, Tenant.class)));
}
List<Tenant> tenantsTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("saved '{}', qty {}", title1, tenantsTitle1.size());
String title2 = "Tenant title 2";
List<Tenant> tenantsTitle2 = new ArrayList<>();
for (int i=0;i<127;i++) {
createFutures = new ArrayList<>(127);
for (int i = 0; i < 127; i++) {
Tenant tenant = new Tenant();
String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
String title = title2+suffix;
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
tenant.setTitle(title);
tenantsTitle2.add(doPost("/api/tenant", tenant, Tenant.class));
createFutures.add(executor.submit(() ->
doPost("/api/tenant", tenant, Tenant.class)));
}
List<Tenant> loadedTenantsTitle1 = new ArrayList<>();
List<Tenant> tenantsTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("saved '{}', qty {}", title2, tenantsTitle2.size());
List<Tenant> loadedTenantsTitle1 = new ArrayList<>(134);
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Tenant> pageData = null;
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
loadedTenantsTitle1.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenantsTitle1, idComparator);
Collections.sort(loadedTenantsTitle1, idComparator);
Assert.assertEquals(tenantsTitle1, loadedTenantsTitle1);
List<Tenant> loadedTenantsTitle2 = new ArrayList<>();
log.debug("found by name '{}', step 15 {}", title1, loadedTenantsTitle1.size());
assertThat(tenantsTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedTenantsTitle1);
log.debug("asserted");
List<Tenant> loadedTenantsTitle2 = new ArrayList<>(127);
pageLink = new PageLink(4, 0, title2);
do {
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
loadedTenantsTitle2.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenantsTitle2, idComparator);
Collections.sort(loadedTenantsTitle2, idComparator);
Assert.assertEquals(tenantsTitle2, loadedTenantsTitle2);
log.debug("found by name '{}', step 4 {}", title1, loadedTenantsTitle2.size());
assertThat(tenantsTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedTenantsTitle2);
log.debug("asserted");
deleteEntitiesAsync("/api/tenant/", loadedTenantsTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("deleted '{}', size {}", title1, loadedTenantsTitle1.size());
for (Tenant tenant : loadedTenantsTitle1) {
doDelete("/api/tenant/"+tenant.getId().getId().toString())
.andExpect(status().isOk());
}
pageLink = new PageLink(4, 0, title1);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
for (Tenant tenant : loadedTenantsTitle2) {
doDelete("/api/tenant/"+tenant.getId().getId().toString())
.andExpect(status().isOk());
}
log.debug("tried to search another '{}', step 4", title1);
deleteEntitiesAsync("/api/tenant/", loadedTenantsTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS);
log.debug("deleted '{}', size {}", title2, loadedTenantsTitle2.size());
pageLink = new PageLink(4, 0, title2);
pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference<PageData<Tenant>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
log.debug("tried to search another '{}', step 4", title2);
}
@Test
@ -244,42 +282,50 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest {
loginSysAdmin();
List<TenantInfo> tenants = new ArrayList<>();
PageLink pageLink = new PageLink(17);
PageData<TenantInfo> pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
PageData<TenantInfo> pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
tenants.addAll(pageData.getData());
for (int i=0;i<56;i++) {
List<ListenableFuture<TenantInfo>> createFutures = new ArrayList<>(56);
for (int i = 0; i < 56; i++) {
Tenant tenant = new Tenant();
tenant.setTitle("Tenant"+i);
tenants.add(new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default"));
tenant.setTitle("Tenant" + i);
createFutures.add(executor.submit(() ->
new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default")));
}
tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS));
List<TenantInfo> loadedTenants = new ArrayList<>();
pageLink = new PageLink(17);
do {
pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink);
loadedTenants.addAll(pageData.getData());
if (pageData.hasNext()) {
pageLink = pageLink.nextPageLink();
}
} while (pageData.hasNext());
Collections.sort(tenants, idComparator);
Collections.sort(loadedTenants, idComparator);
assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants);
Assert.assertEquals(tenants, loadedTenants);
for (TenantInfo tenant : loadedTenants) {
if (!tenant.getTitle().equals(TEST_TENANT_NAME)) {
doDelete("/api/tenant/"+tenant.getId().getId().toString())
.andExpect(status().isOk());
}
}
deleteEntitiesAsync("/api/tenant/", loadedTenants.stream()
.filter((t) -> !TEST_TENANT_NAME.equals(t.getTitle()))
.collect(Collectors.toList()), executor).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(17);
pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference<PageData<TenantInfo>>(){}, pageLink);
pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(1, pageData.getData().size());
}
ListenableFuture<List<ResultActions>> deleteTenantsAsync(String urlTemplate, List<Tenant> tenants) {
List<ListenableFuture<ResultActions>> futures = new ArrayList<>(tenants.size());
for (Tenant device : tenants) {
futures.add(executor.submit(() ->
doDelete(urlTemplate + device.getId().getId())
.andExpect(status().isOk())));
}
return Futures.allAsList(futures);
}
}

382
application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java

@ -24,10 +24,6 @@ import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.kv.AttributeKvEntry;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
@ -46,19 +42,12 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.NumericFilterPredicate;
import org.thingsboard.server.common.data.query.TsValue;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.service.subscription.TbAttributeSubscriptionScope;
import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -68,77 +57,38 @@ import java.util.concurrent.TimeUnit;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@Slf4j
public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
private Tenant savedTenant;
private User tenantAdmin;
private TbTestWebSocketClient wsClient;
public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
@Autowired
private TelemetrySubscriptionService tsService;
@Before
public void beforeTest() throws Exception {
loginSysAdmin();
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
savedTenant = doPost("/api/tenant", tenant, Tenant.class);
Assert.assertNotNull(savedTenant);
tenantAdmin = new User();
tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
tenantAdmin.setTenantId(savedTenant.getId());
tenantAdmin.setEmail("tenant2@thingsboard.org");
tenantAdmin.setFirstName("Joe");
tenantAdmin.setLastName("Downs");
Device device;
DeviceTypeFilter dtf;
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
@Before
public void setUp() throws Exception {
loginTenantAdmin();
wsClient = buildAndConnectWebSocketClient();
device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
dtf = new DeviceTypeFilter(device.getType(), device.getName());
}
@After
public void afterTest() throws Exception {
wsClient.close();
loginSysAdmin();
doDelete("/api/tenant/" + savedTenant.getId().getId().toString())
public void tearDown() throws Exception {
doDelete("/api/device/" + device.getId().getId())
.andExpect(status().isOk());
}
@Test
public void testEntityDataHistoryWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
List<String> keys = List.of("temperature");
long now = System.currentTimeMillis();
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf,
new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityHistoryCmd historyCmd = new EntityHistoryCmd();
historyCmd.setKeys(Arrays.asList("temperature"));
historyCmd.setAgg(Aggregation.NONE);
historyCmd.setLimit(1000);
historyCmd.setStartTs(now - TimeUnit.HOURS.toMillis(1));
historyCmd.setEndTs(now);
EntityDataCmd cmd = new EntityDataCmd(1, edq, historyCmd, null, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
EntityDataUpdate update = getWsClient().sendHistoryCmd(keys, now, TimeUnit.HOURS.toMillis(1), dtf);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
@ -154,9 +104,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
sendTelemetry(device, tsData);
Thread.sleep(100);
wsClient.send(mapper.writeValueAsString(wrapper));
msg = wsClient.waitForReply();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().sendHistoryCmd(keys, now, TimeUnit.HOURS.toMillis(1), dtf);
Assert.assertEquals(1, update.getCmdId());
List<EntityData> dataList = update.getUpdate();
Assert.assertNotNull(dataList);
@ -171,41 +120,16 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
@Test
public void testEntityDataTimeSeriesWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
long now = System.currentTimeMillis();
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, null, null);
EntityDataUpdate update = getWsClient().sendEntityDataQuery(dtf);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
Assert.assertEquals(1, pageData.getData().size());
Assert.assertEquals(device.getId(), pageData.getData().get(0).getEntityId());
TimeSeriesCmd tsCmd = new TimeSeriesCmd();
tsCmd.setKeys(Arrays.asList("temperature"));
tsCmd.setAgg(Aggregation.NONE);
tsCmd.setLimit(1000);
tsCmd.setStartTs(now - TimeUnit.HOURS.toMillis(1));
tsCmd.setTimeWindow(TimeUnit.HOURS.toMillis(1));
TsKvEntry dataPoint1 = new BasicTsKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("temperature", 42L));
TsKvEntry dataPoint2 = new BasicTsKvEntry(now - TimeUnit.MINUTES.toMillis(2), new LongDataEntry("temperature", 43L));
TsKvEntry dataPoint3 = new BasicTsKvEntry(now - TimeUnit.MINUTES.toMillis(3), new LongDataEntry("temperature", 44L));
@ -214,12 +138,7 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
sendTelemetry(device, tsData);
Thread.sleep(100);
cmd = new EntityDataCmd(1, null, null, null, tsCmd);
wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
msg = wsClient.waitForReply();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().subscribeTsUpdate(List.of("temperature"), now, TimeUnit.HOURS.toMillis(1));
Assert.assertEquals(1, update.getCmdId());
List<EntityData> listData = update.getUpdate();
Assert.assertNotNull(listData);
@ -233,10 +152,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
now = System.currentTimeMillis();
TsKvEntry dataPoint4 = new BasicTsKvEntry(now, new LongDataEntry("temperature", 45L));
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
Thread.sleep(100);
sendTelemetry(device, Arrays.asList(dataPoint4));
msg = wsClient.waitForUpdate();
String msg = getWsClient().waitForUpdate();
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
@ -252,44 +171,26 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
@Test
public void testEntityCountWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(System.currentTimeMillis(), new LongDataEntry("temperature", 42L));
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Collections.singletonList(dataPoint1));
DeviceTypeFilter dtf1 = new DeviceTypeFilter();
dtf1.setDeviceNameFilter("D");
dtf1.setDeviceType("default");
EntityCountQuery edq1 = new EntityCountQuery(dtf1, Collections.emptyList());
EntityCountQuery edq1 = new EntityCountQuery(dtf, Collections.emptyList());
EntityCountCmd cmd1 = new EntityCountCmd(1, edq1);
TelemetryPluginCmdsWrapper wrapper1 = new TelemetryPluginCmdsWrapper();
wrapper1.setEntityCountCmds(Collections.singletonList(cmd1));
getWsClient().send(cmd1);
wsClient.send(mapper.writeValueAsString(wrapper1));
String msg1 = wsClient.waitForReply();
EntityCountUpdate update1 = mapper.readValue(msg1, EntityCountUpdate.class);
EntityCountUpdate update1 = getWsClient().parseCountReply(getWsClient().waitForReply());
Assert.assertEquals(1, update1.getCmdId());
Assert.assertEquals(1, update1.getCount());
DeviceTypeFilter dtf2 = new DeviceTypeFilter();
dtf2.setDeviceNameFilter("D");
dtf2.setDeviceType("non-existing-device-type");
DeviceTypeFilter dtf2 = new DeviceTypeFilter("non-existing-device-type", "D");
EntityCountQuery edq2 = new EntityCountQuery(dtf2, Collections.emptyList());
EntityCountCmd cmd2 = new EntityCountCmd(2, edq2);
TelemetryPluginCmdsWrapper wrapper2 = new TelemetryPluginCmdsWrapper();
wrapper2.setEntityCountCmds(Collections.singletonList(cmd2));
wsClient.send(mapper.writeValueAsString(wrapper2));
getWsClient().send(cmd2);
String msg2 = wsClient.waitForReply();
EntityCountUpdate update2 = mapper.readValue(msg2, EntityCountUpdate.class);
String msg2 = getWsClient().waitForReply();
EntityCountUpdate update2 = getWsClient().parseCountReply(getWsClient().waitForReply());
Assert.assertEquals(2, update2.getCmdId());
Assert.assertEquals(0, update2.getCount());
@ -301,19 +202,12 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
highTemperatureFilter.setPredicate(predicate);
highTemperatureFilter.setValueType(EntityKeyValueType.NUMERIC);
DeviceTypeFilter dtf3 = new DeviceTypeFilter();
dtf3.setDeviceNameFilter("D");
dtf3.setDeviceType("default");
DeviceTypeFilter dtf3 = new DeviceTypeFilter("default", "D");
EntityCountQuery edq3 = new EntityCountQuery(dtf3, Collections.singletonList(highTemperatureFilter));
EntityCountCmd cmd3 = new EntityCountCmd(3, edq3);
getWsClient().send(cmd3);
TelemetryPluginCmdsWrapper wrapper3 = new TelemetryPluginCmdsWrapper();
wrapper3.setEntityCountCmds(Collections.singletonList(cmd3));
wsClient.send(mapper.writeValueAsString(wrapper3));
String msg3 = wsClient.waitForReply();
EntityCountUpdate update3 = mapper.readValue(msg3, EntityCountUpdate.class);
EntityCountUpdate update3 = getWsClient().parseCountReply(getWsClient().waitForReply());
Assert.assertEquals(3, update3.getCmdId());
Assert.assertEquals(1, update3.getCount());
@ -325,47 +219,26 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
highTemperatureFilter2.setPredicate(predicate2);
highTemperatureFilter2.setValueType(EntityKeyValueType.NUMERIC);
DeviceTypeFilter dtf4 = new DeviceTypeFilter();
dtf4.setDeviceNameFilter("D");
dtf4.setDeviceType("default");
DeviceTypeFilter dtf4 = new DeviceTypeFilter("default", "D");
EntityCountQuery edq4 = new EntityCountQuery(dtf4, Collections.singletonList(highTemperatureFilter2));
EntityCountCmd cmd4 = new EntityCountCmd(4, edq4);
TelemetryPluginCmdsWrapper wrapper4 = new TelemetryPluginCmdsWrapper();
wrapper4.setEntityCountCmds(Collections.singletonList(cmd4));
wsClient.send(mapper.writeValueAsString(wrapper4));
getWsClient().send(cmd4);
String msg4 = wsClient.waitForReply();
EntityCountUpdate update4 = mapper.readValue(msg4, EntityCountUpdate.class);
EntityCountUpdate update4 = getWsClient().parseCountReply(getWsClient().waitForReply());
Assert.assertEquals(4, update4.getCmdId());
Assert.assertEquals(0, update4.getCount());
}
@Test
public void testEntityDataLatestWidgetFlow() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
long now = System.currentTimeMillis();
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null), Collections.emptyList(),
Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")), Collections.emptyList());
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, null, null);
List<EntityKey> keys = List.of(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
long now = System.currentTimeMillis() - 100;
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), keys, Collections.emptyList());
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
EntityDataUpdate update = getWsClient().sendEntityDataQuery(edq);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
@ -379,17 +252,7 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
List<TsKvEntry> tsData = Arrays.asList(dataPoint1);
sendTelemetry(device, tsData);
Thread.sleep(100);
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")));
cmd = new EntityDataCmd(1, null, null, latestCmd, null);
wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
msg = wsClient.waitForReply();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().subscribeLatestUpdate(keys);
Assert.assertEquals(1, update.getCmdId());
@ -404,11 +267,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
now = System.currentTimeMillis();
TsKvEntry dataPoint2 = new BasicTsKvEntry(now, new LongDataEntry("temperature", 52L));
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().parseDataReply(getWsClient().waitForUpdate());
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
@ -419,43 +281,25 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(new TsValue(dataPoint2.getTs(), dataPoint2.getValueAsString()), tsValue);
//Sending update from the past, while latest value has new timestamp;
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint1));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
String msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending duplicate update again
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
}
@Test
public void testEntityDataLatestTsWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
long now = System.currentTimeMillis();
List<EntityKey> keys = List.of(new EntityKey(EntityKeyType.TIME_SERIES, "temperature"));
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null), Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.TIME_SERIES, "temperature")));
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf);
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
@ -471,13 +315,7 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Thread.sleep(100);
cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
msg = wsClient.waitForReply();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().subscribeLatestUpdate(keys, dtf);
Assert.assertEquals(1, update.getCmdId());
@ -492,11 +330,9 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
now = System.currentTimeMillis();
TsKvEntry dataPoint2 = new BasicTsKvEntry(now, new LongDataEntry("temperature", 52L));
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate();
update = mapper.readValue(msg, EntityDataUpdate.class);
update = getWsClient().parseDataReply(getWsClient().waitForUpdate());
Assert.assertEquals(1, update.getCmdId());
List<EntityData> eData = update.getUpdate();
Assert.assertNotNull(eData);
@ -507,45 +343,24 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(new TsValue(dataPoint2.getTs(), dataPoint2.getValueAsString()), tsValue);
//Sending update from the past, while latest value has new timestamp;
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint1));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
String msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending duplicate update again
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendTelemetry(device, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
}
@Test
public void testEntityDataLatestAttrWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
long now = System.currentTimeMillis();
List<EntityKey> keys = List.of(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, "serverAttributeKey"));
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(Collections.singletonList(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, "serverAttributeKey")));
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
Assert.assertNotNull(msg);
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
@ -555,15 +370,14 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(0, pageData.getData().get(0).getLatest().get(EntityKeyType.SERVER_ATTRIBUTE).get("serverAttributeKey").getTs());
Assert.assertEquals("", pageData.getData().get(0).getLatest().get(EntityKeyType.SERVER_ATTRIBUTE).get("serverAttributeKey").getValue());
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 42L));
List<AttributeKvEntry> tsData = Arrays.asList(dataPoint1);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData);
msg = wsClient.waitForUpdate();
String msg = getWsClient().waitForUpdate();
Assert.assertNotNull(msg);
update = mapper.readValue(msg, EntityDataUpdate.class);
@ -579,10 +393,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
now = System.currentTimeMillis();
AttributeKvEntry dataPoint2 = new BaseAttributeKvEntry(now, new LongDataEntry("serverAttributeKey", 52L));
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate();
msg = getWsClient().waitForUpdate();
Assert.assertNotNull(msg);
update = mapper.readValue(msg, EntityDataUpdate.class);
@ -596,51 +410,31 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(new TsValue(dataPoint2.getLastUpdateTs(), dataPoint2.getValueAsString()), tsValue);
//Sending update from the past, while latest value has new timestamp;
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint1));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending duplicate update again
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
Thread.sleep(500);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
}
@Test
public void testEntityDataLatestAttrTypesWsCmd() throws Exception {
Device device = new Device();
device.setName("Device");
device.setType("default");
device.setLabel("testLabel" + (int) (Math.random() * 1000));
device = doPost("/api/device", device, Device.class);
long now = System.currentTimeMillis();
List<EntityKey> keys = List.of(
new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, "serverAttributeKey"),
new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, "clientAttributeKey"),
new EntityKey(EntityKeyType.SHARED_ATTRIBUTE, "sharedAttributeKey"),
new EntityKey(EntityKeyType.ATTRIBUTE, "anyAttributeKey"));
EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf);
DeviceTypeFilter dtf = new DeviceTypeFilter();
dtf.setDeviceNameFilter("D");
dtf.setDeviceType("default");
EntityDataQuery edq = new EntityDataQuery(dtf, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
LatestValueCmd latestCmd = new LatestValueCmd();
List<EntityKey> keys = new ArrayList<>();
keys.add(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, "serverAttributeKey"));
keys.add(new EntityKey(EntityKeyType.CLIENT_ATTRIBUTE, "clientAttributeKey"));
keys.add(new EntityKey(EntityKeyType.SHARED_ATTRIBUTE, "sharedAttributeKey"));
keys.add(new EntityKey(EntityKeyType.ATTRIBUTE, "anyAttributeKey"));
latestCmd.setKeys(keys);
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
String msg = wsClient.waitForReply();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
PageData<EntityData> pageData = update.getData();
Assert.assertNotNull(pageData);
@ -659,14 +453,14 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(0, pageData.getData().get(0).getLatest().get(EntityKeyType.ATTRIBUTE).get("anyAttributeKey").getTs());
Assert.assertEquals("", pageData.getData().get(0).getLatest().get(EntityKeyType.ATTRIBUTE).get("anyAttributeKey").getValue());
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint1 = new BaseAttributeKvEntry(now - TimeUnit.MINUTES.toMillis(1), new LongDataEntry("serverAttributeKey", 42L));
List<AttributeKvEntry> tsData = Arrays.asList(dataPoint1);
Thread.sleep(100);
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData);
msg = wsClient.waitForUpdate();
String msg = getWsClient().waitForUpdate();
Assert.assertNotNull(msg);
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
@ -679,22 +473,22 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
Assert.assertEquals(new TsValue(dataPoint1.getLastUpdateTs(), dataPoint1.getValueAsString()), attrValue);
//Sending update from the past, while latest value has new timestamp;
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendAttributes(device, TbAttributeSubscriptionScope.SHARED_SCOPE, Arrays.asList(dataPoint1));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending duplicate update again
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
sendAttributes(device, TbAttributeSubscriptionScope.CLIENT_SCOPE, Arrays.asList(dataPoint1));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
Assert.assertNull(msg);
//Sending update from the past, while latest value has new timestamp;
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint2 = new BaseAttributeKvEntry(now, new LongDataEntry("sharedAttributeKey", 42L));
sendAttributes(device, TbAttributeSubscriptionScope.SHARED_SCOPE, Arrays.asList(dataPoint2));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
eData = update.getUpdate();
@ -705,10 +499,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
attrValue = eData.get(0).getLatest().get(EntityKeyType.SHARED_ATTRIBUTE).get("sharedAttributeKey");
Assert.assertEquals(new TsValue(dataPoint2.getLastUpdateTs(), dataPoint2.getValueAsString()), attrValue);
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint3 = new BaseAttributeKvEntry(now, new LongDataEntry("clientAttributeKey", 42L));
sendAttributes(device, TbAttributeSubscriptionScope.CLIENT_SCOPE, Arrays.asList(dataPoint3));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
eData = update.getUpdate();
@ -719,10 +513,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
attrValue = eData.get(0).getLatest().get(EntityKeyType.CLIENT_ATTRIBUTE).get("clientAttributeKey");
Assert.assertEquals(new TsValue(dataPoint3.getLastUpdateTs(), dataPoint3.getValueAsString()), attrValue);
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint4 = new BaseAttributeKvEntry(now, new LongDataEntry("anyAttributeKey", 42L));
sendAttributes(device, TbAttributeSubscriptionScope.CLIENT_SCOPE, Arrays.asList(dataPoint4));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
eData = update.getUpdate();
@ -733,10 +527,10 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest {
attrValue = eData.get(0).getLatest().get(EntityKeyType.ATTRIBUTE).get("anyAttributeKey");
Assert.assertEquals(new TsValue(dataPoint4.getLastUpdateTs(), dataPoint4.getValueAsString()), attrValue);
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
AttributeKvEntry dataPoint5 = new BaseAttributeKvEntry(now, new LongDataEntry("anyAttributeKey", 43L));
sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, Arrays.asList(dataPoint5));
msg = wsClient.waitForUpdate(TimeUnit.SECONDS.toMillis(1));
msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1));
update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
eData = update.getUpdate();

119
application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java

@ -18,9 +18,25 @@ package org.thingsboard.server.controller;
import lombok.extern.slf4j.Slf4j;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.kv.Aggregation;
import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery;
import org.thingsboard.server.common.data.query.EntityFilter;
import org.thingsboard.server.common.data.query.EntityKey;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityCountUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataUpdate;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityHistoryCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.LatestValueCmd;
import org.thingsboard.server.service.telemetry.cmd.v2.TimeSeriesCmd;
import java.net.URI;
import java.nio.channels.NotYetConnectedException;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@ -73,6 +89,18 @@ public class TbTestWebSocketClient extends WebSocketClient {
super.send(text);
}
public void send(EntityDataCmd cmd) throws NotYetConnectedException {
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
this.send(JacksonUtil.toString(wrapper));
}
public void send(EntityCountCmd cmd) throws NotYetConnectedException {
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityCountCmds(Collections.singletonList(cmd));
this.send(JacksonUtil.toString(wrapper));
}
public String waitForUpdate() {
return waitForUpdate(TimeUnit.SECONDS.toMillis(3));
}
@ -94,4 +122,95 @@ public class TbTestWebSocketClient extends WebSocketClient {
}
return lastMsg;
}
public EntityDataUpdate parseDataReply(String msg) {
return JacksonUtil.fromString(msg, EntityDataUpdate.class);
}
public EntityCountUpdate parseCountReply(String msg) {
return JacksonUtil.fromString(msg, EntityCountUpdate.class);
}
public EntityDataUpdate subscribeLatestUpdate(List<EntityKey> keys, EntityFilter entityFilter) {
EntityDataQuery edq = new EntityDataQuery(entityFilter, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
return subscribeLatestUpdate(keys, edq);
}
public EntityDataUpdate subscribeLatestUpdate(List<EntityKey> keys) {
return subscribeLatestUpdate(keys, (EntityDataQuery) null);
}
public EntityDataUpdate subscribeLatestUpdate(List<EntityKey> keys, EntityDataQuery edq) {
LatestValueCmd latestCmd = new LatestValueCmd();
latestCmd.setKeys(keys);
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, latestCmd, null);
send(cmd);
return parseDataReply(waitForReply());
}
public EntityDataUpdate subscribeTsUpdate(List<String> keys, long startTs, long timeWindow) {
return subscribeTsUpdate(keys, startTs, timeWindow, (EntityDataQuery) null);
}
public EntityDataUpdate subscribeTsUpdate(List<String> keys, long startTs, long timeWindow, EntityFilter entityFilter) {
EntityDataQuery edq = new EntityDataQuery(entityFilter, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
return subscribeTsUpdate(keys, startTs, timeWindow, edq);
}
public EntityDataUpdate subscribeTsUpdate(List<String> keys, long startTs, long timeWindow, EntityDataQuery edq) {
TimeSeriesCmd tsCmd = new TimeSeriesCmd();
tsCmd.setKeys(keys);
tsCmd.setAgg(Aggregation.NONE);
tsCmd.setLimit(1000);
tsCmd.setStartTs(startTs - timeWindow);
tsCmd.setTimeWindow(timeWindow);
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, null, tsCmd);
send(cmd);
return parseDataReply(waitForReply());
}
public EntityDataUpdate sendHistoryCmd(List<String> keys, long startTs, long timeWindow) {
return sendHistoryCmd(keys, startTs, timeWindow, (EntityDataQuery) null);
}
public EntityDataUpdate sendHistoryCmd(List<String> keys, long startTs, long timeWindow, EntityFilter entityFilter) {
EntityDataQuery edq = new EntityDataQuery(entityFilter,
new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
return sendHistoryCmd(keys, startTs, timeWindow, edq);
}
public EntityDataUpdate sendHistoryCmd(List<String> keys, long startTs, long timeWindow, EntityDataQuery edq) {
EntityHistoryCmd historyCmd = new EntityHistoryCmd();
historyCmd.setKeys(keys);
historyCmd.setAgg(Aggregation.NONE);
historyCmd.setLimit(1000);
historyCmd.setStartTs(startTs - timeWindow);
historyCmd.setEndTs(startTs);
EntityDataCmd cmd = new EntityDataCmd(1, edq, historyCmd, null, null);
send(cmd);
return parseDataReply(this.waitForReply());
}
public EntityDataUpdate sendEntityDataQuery(EntityDataQuery edq) {
log.warn("sendEntityDataQuery {}", edq);
EntityDataCmd cmd = new EntityDataCmd(1, edq, null, null, null);
send(cmd);
String msg = this.waitForReply();
return parseDataReply(msg);
}
public EntityDataUpdate sendEntityDataQuery(EntityFilter entityFilter) {
log.warn("sendEntityDataQuery {}", entityFilter);
EntityDataQuery edq = new EntityDataQuery(entityFilter, new EntityDataPageLink(1, 0, null, null),
Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
return sendEntityDataQuery(edq);
}
}

50
application/src/test/java/org/thingsboard/server/service/script/MockJsInvokeService.java

@ -0,0 +1,50 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.service.script;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
import java.util.UUID;
@Slf4j
@Service
@ConditionalOnProperty(prefix = "js", value = "evaluator", havingValue = "mock")
public class MockJsInvokeService implements JsInvokeService {
@Override
public ListenableFuture<UUID> eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) {
log.warn("eval {} {} {} {}", tenantId, scriptType, scriptBody, argNames);
return Futures.immediateFuture(UUID.randomUUID());
}
@Override
public ListenableFuture<Object> invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) {
log.warn("invokeFunction {} {} {} {}", tenantId, customerId, scriptId, args);
return Futures.immediateFuture("{}");
}
@Override
public ListenableFuture<Void> release(UUID scriptId) {
log.warn("release {}", scriptId);
return Futures.immediateFuture(null);
}
}

2
application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java

@ -56,8 +56,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
@DaoSqlTest
public class SequentialTimeseriesPersistenceTest extends AbstractControllerTest {
static final int TIMEOUT = 30;
final String TOTALIZER = "Totalizer";
final int TTL = 99999;
final String GENERIC_CUMULATIVE_OBJ = "genericCumulativeObj";

24
application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java

@ -60,8 +60,7 @@ import org.thingsboard.server.common.data.query.EntityKeyType;
import org.thingsboard.server.common.data.query.SingleEntityFilter;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.controller.AbstractWebsocketTest;
import org.thingsboard.server.controller.TbTestWebSocketClient;
import org.thingsboard.server.controller.AbstractControllerTest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.service.telemetry.cmd.TelemetryPluginCmdsWrapper;
import org.thingsboard.server.service.telemetry.cmd.v2.EntityDataCmd;
@ -104,7 +103,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfil
})
@Slf4j
@DaoSqlTest
public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest {
public abstract class AbstractLwM2MIntegrationTest extends AbstractControllerTest {
@SpyBean
DefaultLwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
@ -176,7 +175,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
protected final Set<Lwm2mTestHelper.LwM2MClientState> expectedStatusesRegistrationBsSuccess = new HashSet<>(Arrays.asList(ON_INIT, ON_BOOTSTRAP_STARTED, ON_BOOTSTRAP_SUCCESS, ON_REGISTRATION_STARTED, ON_REGISTRATION_SUCCESS));
protected DeviceProfile deviceProfile;
protected ScheduledExecutorService executor;
protected TbTestWebSocketClient wsClient;
protected LwM2MTestClient lwM2MTestClient;
private String[] resources;
@ -187,17 +185,16 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
@After
public void after() {
wsClient.close();
clientDestroy();
executor.shutdownNow();
}
@AfterClass
public static void afterClass () {
public static void afterClass() {
awaitServersDestroy();
}
private void init () throws Exception {
private void init() throws Exception {
executor = Executors.newScheduledThreadPool(10, ThingsBoardThreadFactory.forName("test-lwm2m-scheduled"));
loginTenantAdmin();
for (String resourceName : this.resources) {
@ -212,7 +209,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
});
Assert.assertNotNull(lwModel);
}
wsClient = buildAndConnectWebSocketClient();
}
public void basicTestConnectionObserveTelemetry(Security security,
@ -234,12 +230,12 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper();
wrapper.setEntityDataCmds(Collections.singletonList(cmd));
wsClient.send(mapper.writeValueAsString(wrapper));
wsClient.waitForReply();
getWsClient().send(mapper.writeValueAsString(wrapper));
getWsClient().waitForReply();
wsClient.registerWaitForUpdate();
getWsClient().registerWaitForUpdate();
createNewClient(security, coapConfig, false, endpoint, false, null);
String msg = wsClient.waitForUpdate();
String msg = getWsClient().waitForUpdate();
EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class);
Assert.assertEquals(1, update.getCmdId());
@ -371,7 +367,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
return credentials;
}
private static void awaitServersDestroy() {
private static void awaitServersDestroy() {
await("One of servers ports number is not free")
.atMost(3000, TimeUnit.MILLISECONDS)
.until(() -> isServerPortsAvailable() == null);
@ -390,7 +386,7 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest
return null;
}
private static void awaitClientDestroy(LeshanClient leshanClient) {
private static void awaitClientDestroy(LeshanClient leshanClient) {
await("Destroy LeshanClient: delete All is registered Servers.")
.atMost(2000, TimeUnit.MILLISECONDS)
.until(() -> leshanClient.getRegisteredServers().size() == 0);

2
application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java

@ -52,8 +52,6 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MProfil
@Slf4j
public class OtaLwM2MIntegrationTest extends AbstractOtaLwM2MIntegrationTest {
public static final int TIMEOUT = 30;
protected final String OBSERVE_ATTRIBUTES_WITH_PARAMS_OTA =
" {\n" +

35
application/src/test/resources/application-test.properties

@ -20,3 +20,38 @@ transport.mqtt.enabled=false
transport.coap.enabled=false
transport.lwm2m.enabled=false
transport.snmp.enabled=false
# Low latency settings to perform tests as fast as possible
sql.attributes.batch_max_delay=5
sql.attributes.batch_threads=2
sql.ts.batch_max_delay=5
sql.ts.batch_threads=2
sql.ts_latest.batch_max_delay=5
sql.ts_latest.batch_threads=2
sql.events.batch_max_delay=5
sql.events.batch_threads=2
actors.system.tenant_dispatcher_pool_size=4
actors.system.device_dispatcher_pool_size=8
actors.system.rule_dispatcher_pool_size=12
transport.sessions.report_timeout=10000
queue.transport_api.request_poll_interval=5
queue.transport_api.response_poll_interval=5
queue.transport.poll_interval=5
queue.core.poll-interval=5
queue.core.partitions=2
queue.rule-engine.poll-interval=5
queue.rule-engine.queues[0].poll-interval=5
queue.rule-engine.queues[0].partitions=2
queue.rule-engine.queues[0].processing-strategy.retries=1
queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[1].poll-interval=5
queue.rule-engine.queues[1].partitions=2
queue.rule-engine.queues[1].processing-strategy.retries=1
queue.rule-engine.queues[1].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[1].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[2].poll-interval=5
queue.rule-engine.queues[2].partitions=2
queue.rule-engine.queues[2].processing-strategy.retries=1
queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0

4
common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceTypeFilter.java

@ -15,8 +15,12 @@
*/
package org.thingsboard.server.common.data.query;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@Data
public class DeviceTypeFilter implements EntityFilter {

2
common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java

@ -17,7 +17,9 @@ package org.thingsboard.server.common.msg;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
@ToString
@EqualsAndHashCode
public abstract class TbRuleEngineActorMsg implements TbActorMsg {

2
common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java

@ -29,7 +29,7 @@ import java.util.Set;
/**
* Created by ashvayka on 15.03.18.
*/
@ToString
@ToString(callSuper = true)
@EqualsAndHashCode(callSuper = true)
public final class QueueToRuleEngineMsg extends TbRuleEngineActorMsg {

76
dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java

@ -16,11 +16,16 @@
package org.thingsboard.server.dao.service;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.id.TenantId;
@ -29,17 +34,22 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.dao.exception.DataValidationException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
static final int TIMEOUT = 30;
private IdComparator<Customer> idComparator = new IdComparator<>();
ListeningExecutorService executor;
private TenantId tenantId;
@Before
public void before() {
executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass()));
Tenant tenant = new Tenant();
tenant.setTitle("My tenant");
Tenant savedTenant = tenantService.saveTenant(tenant);
@ -49,6 +59,8 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
@After
public void after() {
executor.shutdownNow();
tenantService.deleteTenant(tenantId);
}
@ -130,22 +142,24 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
}
@Test
public void testFindCustomersByTenantId() {
public void testFindCustomersByTenantId() throws Exception {
Tenant tenant = new Tenant();
tenant.setTitle("Test tenant");
tenant = tenantService.saveTenant(tenant);
TenantId tenantId = tenant.getId();
List<Customer> customers = new ArrayList<>();
List<ListenableFuture<Customer>> futures = new ArrayList<>(135);
for (int i = 0; i < 135; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
customer.setTitle("Customer" + i);
customers.add(customerService.saveCustomer(customer));
futures.add(executor.submit(() ->
customerService.saveCustomer(customer)));
}
List<Customer> customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Customer> loadedCustomers = new ArrayList<>();
List<Customer> loadedCustomers = new ArrayList<>(135);
PageLink pageLink = new PageLink(23);
PageData<Customer> pageData = null;
do {
@ -156,10 +170,7 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
}
} while (pageData.hasNext());
Collections.sort(customers, idComparator);
Collections.sort(loadedCustomers, idComparator);
Assert.assertEquals(customers, loadedCustomers);
assertThat(customers).containsExactlyInAnyOrderElementsOf(loadedCustomers);
customerService.deleteCustomersByTenantId(tenantId);
@ -172,31 +183,34 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
}
@Test
public void testFindCustomersByTenantIdAndTitle() {
public void testFindCustomersByTenantIdAndTitle() throws Exception {
String title1 = "Customer title 1";
List<Customer> customersTitle1 = new ArrayList<>();
List<ListenableFuture<Customer>> futures = new ArrayList<>(143);
for (int i = 0; i < 143; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title1 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle1.add(customerService.saveCustomer(customer));
futures.add(executor.submit(() -> customerService.saveCustomer(customer)));
}
List<Customer> customersTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
String title2 = "Customer title 2";
List<Customer> customersTitle2 = new ArrayList<>();
futures = new ArrayList<>(175);
for (int i = 0; i < 175; i++) {
Customer customer = new Customer();
customer.setTenantId(tenantId);
String suffix = RandomStringUtils.randomAlphanumeric((int)(5 + Math.random()*10));
String suffix = RandomStringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
String title = title2 + suffix;
title = i % 2 == 0 ? title.toLowerCase() : title.toUpperCase();
customer.setTitle(title);
customersTitle2.add(customerService.saveCustomer(customer));
futures.add(executor.submit(() -> customerService.saveCustomer(customer)));
}
List<Customer> customersTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS);
List<Customer> loadedCustomersTitle1 = new ArrayList<>();
List<Customer> loadedCustomersTitle1 = new ArrayList<>(143);
PageLink pageLink = new PageLink(15, 0, title1);
PageData<Customer> pageData = null;
do {
@ -207,12 +221,9 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
}
} while (pageData.hasNext());
Collections.sort(customersTitle1, idComparator);
Collections.sort(loadedCustomersTitle1, idComparator);
Assert.assertEquals(customersTitle1, loadedCustomersTitle1);
assertThat(customersTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle1);
List<Customer> loadedCustomersTitle2 = new ArrayList<>();
List<Customer> loadedCustomersTitle2 = new ArrayList<>(175);
pageLink = new PageLink(4, 0, title2);
do {
pageData = customerService.findCustomersByTenantId(tenantId, pageLink);
@ -222,23 +233,30 @@ public abstract class BaseCustomerServiceTest extends AbstractServiceTest {
}
} while (pageData.hasNext());
Collections.sort(customersTitle2, idComparator);
Collections.sort(loadedCustomersTitle2, idComparator);
Assert.assertEquals(customersTitle2, loadedCustomersTitle2);
assertThat(customersTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle2);
List<ListenableFuture<Void>> deleteFutures = new ArrayList<>(143);
for (Customer customer : loadedCustomersTitle1) {
customerService.deleteCustomer(tenantId, customer.getId());
deleteFutures.add(executor.submit(() -> {
customerService.deleteCustomer(tenantId, customer.getId());
return null;
}));
}
Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title1);
pageData = customerService.findCustomersByTenantId(tenantId, pageLink);
Assert.assertFalse(pageData.hasNext());
Assert.assertEquals(0, pageData.getData().size());
deleteFutures = new ArrayList<>(175);
for (Customer customer : loadedCustomersTitle2) {
customerService.deleteCustomer(tenantId, customer.getId());
deleteFutures.add(executor.submit(() -> {
customerService.deleteCustomer(tenantId, customer.getId());
return null;
}));
}
Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS);
pageLink = new PageLink(4, 0, title2);
pageData = customerService.findCustomersByTenantId(tenantId, pageLink);

45
dao/src/test/resources/application-test.properties

@ -60,4 +60,47 @@ database.ts_max_intervals=700
sql.remove_null_chars=true
edges.enabled=true
# Edge disabled to speed up the context init. Will be enabled by @TestPropertySource in respective tests
edges.enabled=false
# Transports disabled to speed up the context init. Particular transport will be enabled with @TestPropertySource in respective tests
transport.http.enabled=false
transport.mqtt.enabled=false
transport.coap.enabled=false
transport.lwm2m.enabled=false
transport.snmp.enabled=false
# Low latency settings to perform tests as fast as possible
sql.attributes.batch_max_delay=5
sql.attributes.batch_threads=2
sql.ts.batch_max_delay=5
sql.ts.batch_threads=2
sql.ts_latest.batch_max_delay=5
sql.ts_latest.batch_threads=2
sql.events.batch_max_delay=5
sql.events.batch_threads=2
actors.system.tenant_dispatcher_pool_size=4
actors.system.device_dispatcher_pool_size=8
actors.system.rule_dispatcher_pool_size=12
transport.sessions.report_timeout=10000
queue.transport_api.request_poll_interval=5
queue.transport_api.response_poll_interval=5
queue.transport.poll_interval=5
queue.core.poll-interval=5
queue.core.partitions=2
queue.rule-engine.poll-interval=5
queue.rule-engine.queues[0].poll-interval=5
queue.rule-engine.queues[0].partitions=2
queue.rule-engine.queues[0].processing-strategy.retries=1
queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[1].poll-interval=5
queue.rule-engine.queues[1].partitions=2
queue.rule-engine.queues[1].processing-strategy.retries=1
queue.rule-engine.queues[1].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[1].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[2].poll-interval=5
queue.rule-engine.queues[2].partitions=2
queue.rule-engine.queues[2].processing-strategy.retries=1
queue.rule-engine.queues[2].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[2].processing-strategy.max-pause-between-retries=0

11
dao/src/test/resources/nosql-test.properties

@ -17,3 +17,14 @@ spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:12.8:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize = 50
queue.rule-engine.queues[0].name=Main
queue.rule-engine.queues[0].topic=tb_rule_engine.main
queue.rule-engine.queues[0].poll-interval=5
queue.rule-engine.queues[0].partitions=2
queue.rule-engine.queues[0].pack-processing-timeout=3000
queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES
queue.rule-engine.queues[0].processing-strategy.retries=1
queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[0].submit-strategy.type=BURST

7
dao/src/test/resources/sql-test.properties

@ -45,10 +45,13 @@ queue.rule-engine.pack-processing-timeout=3000
queue.rule-engine.queues[0].name=Main
queue.rule-engine.queues[0].topic=tb_rule_engine.main
queue.rule-engine.queues[0].poll-interval=25
queue.rule-engine.queues[0].partitions=3
queue.rule-engine.queues[0].poll-interval=5
queue.rule-engine.queues[0].partitions=2
queue.rule-engine.queues[0].pack-processing-timeout=3000
queue.rule-engine.queues[0].processing-strategy.type=SKIP_ALL_FAILURES
queue.rule-engine.queues[0].processing-strategy.retries=1
queue.rule-engine.queues[0].processing-strategy.pause-between-retries=0
queue.rule-engine.queues[0].processing-strategy.max-pause-between-retries=0
queue.rule-engine.queues[0].submit-strategy.type=BURST
sql.log_entity_queries=true

32
msa/black-box-tests/src/test/resources/logback.xml

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE configuration>
<configuration scan="true" scanPeriod="10 seconds">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{ISO8601} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT"/>
</root>
</configuration>

23
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java

@ -20,6 +20,7 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.MoreExecutors;
import lombok.extern.slf4j.Slf4j;
import org.checkerframework.checker.nullness.qual.Nullable;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.rule.engine.api.RuleNode;
import org.thingsboard.rule.engine.api.ScriptEngine;
import org.thingsboard.rule.engine.api.TbContext;
@ -30,8 +31,6 @@ import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.plugin.ComponentType;
import org.thingsboard.server.common.msg.TbMsg;
import static org.thingsboard.common.util.DonAsynchron.withCallback;
@Slf4j
@RuleNode(
type = ComponentType.ACTION,
@ -49,15 +48,22 @@ public class TbLogNode implements TbNode {
private TbLogNodeConfiguration config;
private ScriptEngine jsEngine;
private boolean standard;
@Override
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException {
this.config = TbNodeUtils.convert(configuration, TbLogNodeConfiguration.class);
this.jsEngine = ctx.createJsScriptEngine(config.getJsScript());
this.standard = new TbLogNodeConfiguration().defaultConfiguration().getJsScript().equals(config.getJsScript());
this.jsEngine = this.standard ? null : ctx.createJsScriptEngine(config.getJsScript());
}
@Override
public void onMsg(TbContext ctx, TbMsg msg) {
if (standard) {
logStandard(ctx, msg);
return;
}
ctx.logJsEvalRequest();
Futures.addCallback(jsEngine.executeToStringAsync(msg), new FutureCallback<String>() {
@Override
@ -75,6 +81,17 @@ public class TbLogNode implements TbNode {
}, MoreExecutors.directExecutor()); //usually js responses runs on js callback executor
}
void logStandard(TbContext ctx, TbMsg msg) {
log.info(toLogMessage(msg));
ctx.tellSuccess(msg);
}
String toLogMessage(TbMsg msg) {
return "\n" +
"Incoming message:\n" + msg.getData() + "\n" +
"Incoming metadata:\n" + JacksonUtil.toString(msg.getMetaData().getData());
}
@Override
public void destroy() {
if (jsEngine != null) {

2
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNodeConfiguration.java

@ -26,7 +26,7 @@ public class TbLogNodeConfiguration implements NodeConfiguration {
@Override
public TbLogNodeConfiguration defaultConfiguration() {
TbLogNodeConfiguration configuration = new TbLogNodeConfiguration();
configuration.setJsScript("return 'Incoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);");
configuration.setJsScript("return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);");
return configuration;
}
}

80
rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java

@ -0,0 +1,80 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.rule.engine.action;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.msg.TbMsg;
import org.thingsboard.server.common.msg.TbMsgMetaData;
import java.util.Collections;
import java.util.Map;
import static org.assertj.core.api.Assertions.assertThat;
@Slf4j
public class TbLogNodeTest {
@Test
void givenMsg_whenToLog_thenReturnString() {
TbLogNode node = new TbLogNode();
String data = "{\"key\": \"value\"}";
TbMsgMetaData metaData = new TbMsgMetaData(Map.of("mdKey1", "mdValue1", "mdKey2", "23"));
TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, data);
String logMessage = node.toLogMessage(msg);
log.info(logMessage);
assertThat(logMessage).isEqualTo("\n" +
"Incoming message:\n" +
"{\"key\": \"value\"}\n" +
"Incoming metadata:\n" +
"{\"mdKey1\":\"mdValue1\",\"mdKey2\":\"23\"}");
}
@Test
void givenEmptyDataMsg_whenToLog_thenReturnString() {
TbLogNode node = new TbLogNode();
TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap());
TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, "");
String logMessage = node.toLogMessage(msg);
log.info(logMessage);
assertThat(logMessage).isEqualTo("\n" +
"Incoming message:\n" +
"\n" +
"Incoming metadata:\n" +
"{}");
}
@Test
void givenNullDataMsg_whenToLog_thenReturnString() {
TbLogNode node = new TbLogNode();
TbMsgMetaData metaData = new TbMsgMetaData(Collections.emptyMap());
TbMsg msg = TbMsg.newMsg("POST_TELEMETRY", TenantId.SYS_TENANT_ID, metaData, null);
String logMessage = node.toLogMessage(msg);
log.info(logMessage);
assertThat(logMessage).isEqualTo("\n" +
"Incoming message:\n" +
"null\n" +
"Incoming metadata:\n" +
"{}");
}
}
Loading…
Cancel
Save