From 668634f890e0f58bd4b844c1ba6d785e52b97f13 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Mon, 18 Apr 2022 11:02:48 +0300 Subject: [PATCH 01/28] Tests: improved performance on tenant tests with concurrent creation and deletion --- .../controller/BaseTenantControllerTest.java | 189 ++++++++++++------ 1 file changed, 123 insertions(+), 66 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java index 21905a12c6..d89cc4c3c5 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -16,24 +16,53 @@ 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.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.Collection; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +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; +@Slf4j public abstract class BaseTenantControllerTest extends AbstractControllerTest { - - private IdComparator idComparator = new IdComparator<>(); + + static final TypeReference> PAGE_DATA_TENANT_TYPE_REF = new TypeReference<>(){}; + static final TypeReference> PAGE_DATA_TENANT_INFO_TYPE_REF = new TypeReference<>(){}; + static final int TIMEOUT = 30; + + List> createFutures = new ArrayList<>(); + List> deleteFutures = new ArrayList<>(); + 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,7 +76,7 @@ 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()); @@ -60,14 +89,14 @@ 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()) @@ -86,7 +115,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) .andExpect(status().isOk()); } - + @Test public void testSaveTenantWithEmptyTitle() throws Exception { loginSysAdmin(); @@ -95,7 +124,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Tenant title should be specified"))); } - + @Test public void testSaveTenantWithInvalidEmail() throws Exception { loginSysAdmin(); @@ -106,7 +135,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { .andExpect(status().isBadRequest()) .andExpect(statusReason(containsString("Invalid email address format"))); } - + @Test public void testDeleteTenant() throws Exception { loginSysAdmin(); @@ -114,17 +143,17 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { tenant.setTitle("My tenant"); Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); + .andExpect(status().isOk()); doGet("/api/tenant/"+savedTenant.getId().getId().toString()) .andExpect(status().isNotFound()); } - + @Test public void testFindTenants() throws Exception { loginSysAdmin(); - List tenants = new ArrayList<>(); + Collection tenants = new ConcurrentLinkedQueue<>(); PageLink pageLink = new PageLink(17); - PageData pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, pageLink); + PageData pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); tenants.addAll(pageData.getData()); @@ -132,119 +161,146 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { for (int i=0;i<56;i++) { Tenant tenant = new Tenant(); tenant.setTitle("Tenant"+i); - tenants.add(doPost("/api/tenant", tenant, Tenant.class)); + createFutures.add(executor.submit(() -> + tenants.add(doPost("/api/tenant", tenant, Tenant.class)))); } - + Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List loadedTenants = new ArrayList<>(); pageLink = new PageLink(17); do { - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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); - + + assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants); + for (Tenant tenant : loadedTenants) { if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - doDelete("/api/tenant/"+tenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(()-> + doDelete("/api/tenant/"+tenant.getId().getId().toString()) + .andExpect(status().isOk()))); } } - + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + pageLink = new PageLink(17); - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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 tenantsTitle1 = new ArrayList<>(); + Collection tenantsTitle1 = new ConcurrentLinkedQueue<>(); + createFutures.clear(); for (int i=0;i<134;i++) { Tenant tenant = new Tenant(); 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(() -> + tenantsTitle1.add(doPost("/api/tenant", tenant, Tenant.class)))); } + + Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("saved '{}', qty {}", title1, 134); + String title2 = "Tenant title 2"; - List tenantsTitle2 = new ArrayList<>(); + Collection tenantsTitle2 = new ConcurrentLinkedQueue<>(); + createFutures.clear(); for (int i=0;i<127;i++) { Tenant tenant = new Tenant(); 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(() -> + tenantsTitle2.add(doPost("/api/tenant", tenant, Tenant.class)))); } - + + Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("saved '{}', qty {}", title2, 127); + List loadedTenantsTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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); - + + log.debug("found by name '{}', step 15 {}", title1, loadedTenantsTitle1.size()); + + assertThat(tenantsTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedTenantsTitle1); + log.debug("asserted"); + List loadedTenantsTitle2 = new ArrayList<>(); pageLink = new PageLink(4, 0, title2); do { - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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"); + deleteFutures.clear(); for (Tenant tenant : loadedTenantsTitle1) { - doDelete("/api/tenant/"+tenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(()-> + doDelete("/api/tenant/"+tenant.getId().getId().toString()) + .andExpect(status().isOk()))); } - + + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("deleted '{}', size {}", title1, loadedTenantsTitle1.size()); + pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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", title1); + + deleteFutures.clear(); for (Tenant tenant : loadedTenantsTitle2) { - doDelete("/api/tenant/"+tenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(()-> + doDelete("/api/tenant/"+tenant.getId().getId().toString()) + .andExpect(status().isOk()))); } - + + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("deleted '{}', size {}", title2, loadedTenantsTitle2.size()); + pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/tenants?", new TypeReference>(){}, 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 public void testFindTenantInfos() throws Exception { loginSysAdmin(); - List tenants = new ArrayList<>(); + Collection tenants = new ConcurrentLinkedQueue<>(); PageLink pageLink = new PageLink(17); - PageData pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference>(){}, pageLink); + PageData 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()); @@ -252,33 +308,34 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { 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")); + createFutures.add(executor.submit(() -> + tenants.add(new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default")))); } + Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); List loadedTenants = new ArrayList<>(); pageLink = new PageLink(17); do { - pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference>(){}, 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); - - Assert.assertEquals(tenants, loadedTenants); + + assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants); for (TenantInfo tenant : loadedTenants) { if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - doDelete("/api/tenant/"+tenant.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(()-> + doDelete("/api/tenant/"+tenant.getId().getId().toString()) + .andExpect(status().isOk()))); } } - + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + pageLink = new PageLink(17); - pageData = doGetTypedWithPageLink("/api/tenantInfos?", new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); } From bf98d1b68bd6df879c9a8ef1cd3a492481176fb1 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 12:50:52 +0300 Subject: [PATCH 02/28] QueueToRuleEngineMsg improved toString with call super --- .../org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java | 2 ++ .../server/common/msg/queue/QueueToRuleEngineMsg.java | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java index 2552f8de4a..d6761d1ba7 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/TbRuleEngineActorMsg.java +++ b/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 { diff --git a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java b/common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java index 7c28a1ad9e..b5c43f20a3 100644 --- a/common/message/src/main/java/org/thingsboard/server/common/msg/queue/QueueToRuleEngineMsg.java +++ b/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 { From f6e71e4b9340017705c8920305cfc4b8e6d6c67c Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 12:54:45 +0300 Subject: [PATCH 03/28] AbstractWebTest exception wrapper into runtime exception to use in async futures without try/catch overhead --- .../server/controller/AbstractWebTest.java | 16 ++++++++++++---- .../queue/memory/InMemoryTbQueueConsumer.java | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index cc561e31a8..97e5c749af 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -503,16 +503,24 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { return readResponse(doGet(urlTemplate, vars).andExpect(status().isOk()), responseType); } - protected T doPost(String urlTemplate, Class responseClass, String... params) throws Exception { - return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass); + protected T doPost(String urlTemplate, Class responseClass, String... params) { + try { + return readResponse(doPost(urlTemplate, params).andExpect(status().isOk()), responseClass); + } catch (Exception e) { + throw new RuntimeException(e); + } } protected T doPost(String urlTemplate, T content, Class responseClass, ResultMatcher resultMatcher, String... params) throws Exception { return readResponse(doPost(urlTemplate, content, params).andExpect(resultMatcher), responseClass); } - protected T doPost(String urlTemplate, T content, Class responseClass, String... params) throws Exception { - return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); + protected T doPost(String urlTemplate, T content, Class responseClass, String... params) { + try { + return readResponse(doPost(urlTemplate, content, params).andExpect(status().isOk()), responseClass); + } catch (Exception e) { + throw new RuntimeException(e); + } } protected R doPostWithResponse(String urlTemplate, T content, Class responseClass, String... params) throws Exception { diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java index 76ce8f6572..810774d096 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java @@ -64,6 +64,9 @@ public class InMemoryTbQueueConsumer implements TbQueueCon @Override public List poll(long durationInMillis) { + if (topic.contains("main")){ + log.warn("poll {} {}", topic, durationInMillis); + } if (subscribed) { @SuppressWarnings("unchecked") List messages = partitions From b3a20fb2b0227c24dacc7cf18bbe4002bf8aae4b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 12:56:21 +0300 Subject: [PATCH 04/28] BaseEntityViewControllerTest refactored in async style --- .../BaseEntityViewControllerTest.java | 259 +++++++++++------- 1 file changed, 154 insertions(+), 105 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index 05312b04b6..fb6ded866a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/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; @@ -27,7 +32,10 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; 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.Customer; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.EntityView; @@ -43,20 +51,21 @@ import org.thingsboard.server.common.data.page.PageLink; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.model.ModelConstants; +import org.thingsboard.server.queue.memory.InMemoryStorage; 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 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; @@ -65,17 +74,27 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; }) @Slf4j public abstract class BaseEntityViewControllerTest extends AbstractControllerTest { + static final int TIMEOUT = 30; + static final TypeReference> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() { + }; - private IdComparator idComparator; private Tenant savedTenant; private User tenantAdmin; private Device testDevice; private TelemetryEntityView telemetry; + List> deleteFutures = new ArrayList<>(); + ListeningExecutorService executor; + + @Autowired + InMemoryStorage inMemoryStorage; + @Before public void beforeTest() throws Exception { + log.warn("beforeTest"); + executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(16, getClass())); + loginSysAdmin(); - idComparator = new IdComparator<>(); savedTenant = doPost("/api/tenant", getNewTenant("My tenant"), Tenant.class); Assert.assertNotNull(savedTenant); @@ -94,19 +113,22 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes 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 { + executor.shutdownNow(); + loginSysAdmin(); doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) .andExpect(status().isOk()); + log.warn("after test"); } @Test @@ -244,21 +266,18 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes CustomerId customerId = customer.getId(); String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViewInfos?"; - List views = new ArrayList<>(); + List> 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 entityViewInfos = Futures.allAsList(viewFutures).get(TIMEOUT, SECONDS); List loadedViews = loadListOfInfo(new PageLink(23), urlTemplate); - Collections.sort(views, idComparator); - Collections.sort(loadedViews, idComparator); - - assertEquals(views, loadedViews); + assertThat(entityViewInfos).containsExactlyInAnyOrderElementsOf(loadedViews); } @Test @@ -267,33 +286,37 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes String urlTemplate = "/api/customer/" + customerId.getId().toString() + "/entityViews?"; String name1 = "Entity view name1"; - List namesOfView1 = fillListOf(125, name1, "/api/customer/" + customerId.getId().toString() - + "/entityView/"); + List namesOfView1 = Futures.allAsList(fillListByTemplate(125, name1, "/api/customer/" + customerId.getId().toString() + + "/entityView/")).get(TIMEOUT, SECONDS); List 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 NamesOfView2 = fillListOf(143, name2, "/api/customer/" + customerId.getId().toString() - + "/entityView/"); + List namesOfView2 = Futures.allAsList(fillListByTemplate(143, name2, "/api/customer/" + customerId.getId().toString() + + "/entityView/")).get(TIMEOUT, SECONDS); List 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()))); } + Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); + PageData pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() { }, 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()))); } + Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); + pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() { }, new PageLink(4, 0, name2)); @@ -303,49 +326,52 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testGetTenantEntityViews() throws Exception { - - List views = new ArrayList<>(); + List> entityViewInfoFutures = new ArrayList<>(178); for (int i = 0; i < 178; i++) { - views.add(new EntityViewInfo(getNewSavedEntityView("Test entity view" + i), null, false)); + ListenableFuture entityViewFuture = getNewSavedEntityViewAsync("Test entity view" + i); + entityViewInfoFutures.add(Futures.transform(entityViewFuture, + view -> new EntityViewInfo(view, null, false), + MoreExecutors.directExecutor())); } + List entityViewInfos = Futures.allAsList(entityViewInfoFutures).get(TIMEOUT, SECONDS); List 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 namesOfView1 = fillListOf(143, name1); + List namesOfView1 = Futures.allAsList(fillListOf(143, name1)).get(TIMEOUT, SECONDS); List loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), "/api/tenant/entityViews?"); - Collections.sort(namesOfView1, idComparator); - Collections.sort(loadedNamesOfView1, idComparator); - assertEquals(namesOfView1, loadedNamesOfView1); + assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1); String name2 = "Entity view name2"; - List NamesOfView2 = fillListOf(75, name2); + List namesOfView2 = Futures.allAsList(fillListOf(75, name2)).get(TIMEOUT, SECONDS); + ; List 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()))); } + Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); + PageData pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", new TypeReference>() { }, 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>() { - }, + 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()); @@ -353,20 +379,22 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testTheCopyOfAttrsIntoTSForTheView() throws Exception { + Set expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4"); Set actualAttributesSet = - getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}"); - - Set 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.warn("got correct actualAttributesSet, saving new entity view..."); EntityView savedView = getNewSavedEntityView("Test entity view"); - Thread.sleep(1000); - - List> values = doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + - "/values/attributes?keys=" + String.join(",", actualAttributesSet), new TypeReference<>() {}); + log.warn("fetching entity view telemetry..."); + List> 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.warn("asserting..."); assertEquals("value1", getValue(values, "caKey1")); assertEquals(true, getValue(values, "caKey2")); assertEquals(42.0, getValue(values, "caKey3")); @@ -375,14 +403,13 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testTheCopyOfAttrsOutOfTSForTheView() throws Exception { + Set expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4"); Set actualAttributesSet = - getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}"); - - Set 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> 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()); @@ -394,10 +421,9 @@ 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> 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()); } @@ -433,6 +459,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes } private void uploadTelemetry(String strKvs) throws Exception { + String viewDeviceId = testDevice.getId().getId().toString(); DeviceCredentials deviceCredentials = doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class); @@ -447,34 +474,36 @@ 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(10, 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 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 getAttributeKeys(String type, String id) throws Exception { + return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/" + type + "/" + id + "/keys/attributes", new TypeReference<>() { + })); } private Map>> getTelemetryValues(String type, String id, Set 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 getAttributesByKeys(String stringKV) throws Exception { + private Set getAttributesByKeys(String stringKV, Set expectedKeySet) throws Exception { String viewDeviceId = testDevice.getId().getId().toString(); DeviceCredentials deviceCredentials = doGet("/api/device/" + viewDeviceId + "/credentials", DeviceCredentials.class); @@ -489,14 +518,22 @@ 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((stringKV).getBytes()); - client.publish("v1/devices/me/attributes", message); - Thread.sleep(1000); + IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message); + log.warn("token.message {}", token.getMessage()); + await("mqtt ack").pollInterval(10, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); + log.warn("token.message delivered {}", token.getMessage()); client.disconnect(); - return new HashSet<>(doGetAsyncTyped("/api/plugins/telemetry/DEVICE/" + viewDeviceId + "/keys/attributes", new TypeReference<>() {})); + + return await().pollInterval(20, MILLISECONDS).atMost(TIMEOUT, SECONDS) + .until(() -> { + inMemoryStorage.printStats(); + return getAttributeKeys("DEVICE", viewDeviceId); + }, + keys -> keys.containsAll(expectedKeySet)); } private Object getValue(List> values, String stringValue) { @@ -506,11 +543,15 @@ 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 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()); @@ -535,33 +576,41 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes return tenant; } - private List fillListOf(int limit, String partOfName, String urlTemplate) throws Exception { - List views = new ArrayList<>(); - for (EntityView view : fillListOf(limit, partOfName)) { - views.add(doPost(urlTemplate + view.getId().getId().toString(), EntityView.class)); + private List> fillListByTemplate(int limit, String partOfName, String urlTemplate) { + List> futures = new ArrayList<>(limit); + for (ListenableFuture 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 fillListOf(int limit, String partOfName) throws Exception { - List viewNames = new ArrayList<>(); + private List> fillListOf(int limit, String partOfName) { + List> 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 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 loadListOf(PageLink pageLink, String urlTemplate) throws Exception { List loadedItems = new ArrayList<>(); PageData pageData; do { - pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() { - }, pageLink); + pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, pageLink); loadedItems.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); @@ -600,7 +649,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes + "/entityView/" + savedEntityView.getId().getId().toString(), EntityView.class); PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/entityViews?", - new TypeReference>() {}, new PageLink(100)); + PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(100)); Assert.assertEquals(1, pageData.getData().size()); @@ -608,7 +657,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>() {}, new PageLink(100)); + PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } From 1600fa7f65a25454d52fff8ac1542f770551e06a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 13:03:45 +0300 Subject: [PATCH 05/28] BurstTbRuleEngineSubmitStrategy debug notes to fix the long-running testTheCopyOfAttrsIntoTSForTheView --- .../queue/processing/BurstTbRuleEngineSubmitStrategy.java | 1 + application/src/test/resources/logback-test.xml | 3 +++ .../java/org/thingsboard/server/actors/TbActorMailbox.java | 7 +++++++ 3 files changed, 11 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java index 5d4696bef6..655d81e16c 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java @@ -33,6 +33,7 @@ public class BurstTbRuleEngineSubmitStrategy extends AbstractTbRuleEngineSubmitS public void submitAttempt(BiConsumer> msgConsumer) { if (log.isDebugEnabled()) { log.debug("[{}] submitting [{}] messages to rule engine", queueName, orderedMsgList.size()); + orderedMsgList.forEach((pair)->log.trace("submitted uuid {}, msg {}", pair.uuid, pair.msg)); } orderedMsgList.forEach(pair -> msgConsumer.accept(pair.uuid, pair.msg)); } diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index d3301bf660..5aba1d10dd 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,6 +16,9 @@ + + + diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index 0e89ee1363..7c7800ac34 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -141,6 +141,7 @@ public final class TbActorMailbox implements TbActorCtx { log.debug("[{}] Going to process message: {}", selfId, msg); actor.process(msg); } catch (TbRuleNodeUpdateException updateException) { + log.debug("[{}] INIT_FAILED: {} {}", selfId, msg, updateException); stopReason = TbActorStopReason.INIT_FAILED; destroy(); } catch (Throwable t) { @@ -155,6 +156,12 @@ public final class TbActorMailbox implements TbActorCtx { break; } } + if (noMoreElements && (!normalPriorityMsgs.isEmpty() || !highPriorityMsgs.isEmpty())) { + log.error("noMoreElements=true, but mailbox is not empty!"); + } + if (noMoreElements == false && (normalPriorityMsgs.isEmpty() && highPriorityMsgs.isEmpty())) { + log.error("noMoreElements=false, but mailbox is empty!"); + } if (noMoreElements) { busy.set(FREE); dispatcher.getExecutor().execute(() -> tryProcessQueue(false)); From b6911bda41b63cf9a9099d8d44a7ff9a501f28bc Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 15:57:02 +0300 Subject: [PATCH 06/28] Revert "BurstTbRuleEngineSubmitStrategy debug notes to fix the long-running testTheCopyOfAttrsIntoTSForTheView" This reverts commit 1600fa7f65a25454d52fff8ac1542f770551e06a. --- .../queue/processing/BurstTbRuleEngineSubmitStrategy.java | 1 - application/src/test/resources/logback-test.xml | 3 --- .../java/org/thingsboard/server/actors/TbActorMailbox.java | 7 ------- 3 files changed, 11 deletions(-) diff --git a/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java b/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java index 655d81e16c..5d4696bef6 100644 --- a/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java +++ b/application/src/main/java/org/thingsboard/server/service/queue/processing/BurstTbRuleEngineSubmitStrategy.java @@ -33,7 +33,6 @@ public class BurstTbRuleEngineSubmitStrategy extends AbstractTbRuleEngineSubmitS public void submitAttempt(BiConsumer> msgConsumer) { if (log.isDebugEnabled()) { log.debug("[{}] submitting [{}] messages to rule engine", queueName, orderedMsgList.size()); - orderedMsgList.forEach((pair)->log.trace("submitted uuid {}, msg {}", pair.uuid, pair.msg)); } orderedMsgList.forEach(pair -> msgConsumer.accept(pair.uuid, pair.msg)); } diff --git a/application/src/test/resources/logback-test.xml b/application/src/test/resources/logback-test.xml index 5aba1d10dd..d3301bf660 100644 --- a/application/src/test/resources/logback-test.xml +++ b/application/src/test/resources/logback-test.xml @@ -16,9 +16,6 @@ - - - diff --git a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java index 7c7800ac34..0e89ee1363 100644 --- a/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java +++ b/common/actor/src/main/java/org/thingsboard/server/actors/TbActorMailbox.java @@ -141,7 +141,6 @@ public final class TbActorMailbox implements TbActorCtx { log.debug("[{}] Going to process message: {}", selfId, msg); actor.process(msg); } catch (TbRuleNodeUpdateException updateException) { - log.debug("[{}] INIT_FAILED: {} {}", selfId, msg, updateException); stopReason = TbActorStopReason.INIT_FAILED; destroy(); } catch (Throwable t) { @@ -156,12 +155,6 @@ public final class TbActorMailbox implements TbActorCtx { break; } } - if (noMoreElements && (!normalPriorityMsgs.isEmpty() || !highPriorityMsgs.isEmpty())) { - log.error("noMoreElements=true, but mailbox is not empty!"); - } - if (noMoreElements == false && (normalPriorityMsgs.isEmpty() && highPriorityMsgs.isEmpty())) { - log.error("noMoreElements=false, but mailbox is empty!"); - } if (noMoreElements) { busy.set(FREE); dispatcher.getExecutor().execute(() -> tryProcessQueue(false)); From b2fa2630273114d7772de5489f4788c117324261 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 15:59:29 +0300 Subject: [PATCH 07/28] revert extra log on poll InMemoryTbQueueConsumer --- .../server/queue/memory/InMemoryTbQueueConsumer.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java index 810774d096..76ce8f6572 100644 --- a/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java +++ b/common/queue/src/main/java/org/thingsboard/server/queue/memory/InMemoryTbQueueConsumer.java @@ -64,9 +64,6 @@ public class InMemoryTbQueueConsumer implements TbQueueCon @Override public List poll(long durationInMillis) { - if (topic.contains("main")){ - log.warn("poll {} {}", topic, durationInMillis); - } if (subscribed) { @SuppressWarnings("unchecked") List messages = partitions From 6a8800a8451ae1aea492cc597312db903cabcd20 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 16:31:22 +0300 Subject: [PATCH 08/28] BaseCustomerControllerTest refactored in async manner to improve performance --- .../BaseCustomerControllerTest.java | 101 ++++++++++-------- 1 file changed, 59 insertions(+), 42 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index dcf3f85a71..492b76374a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -15,16 +15,18 @@ */ 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.springframework.test.web.servlet.ResultActions; +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 +34,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 int TIMEOUT = 30; + static final TypeReference> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() {}; - private IdComparator 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 +75,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 +95,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 +194,28 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest public void testFindCustomers() throws Exception { TenantId tenantId = savedTenant.getId(); - List customers = new ArrayList<>(); + List> 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 customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedCustomers = new ArrayList<>(); + List loadedCustomers = new ArrayList<>(135); PageLink pageLink = new PageLink(23); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, 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); - - Assert.assertEquals(customers, loadedCustomers); + assertThat(customers).containsExactlyInAnyOrderElementsOf(loadedCustomers); } @Test @@ -212,7 +223,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest TenantId tenantId = savedTenant.getId(); String title1 = "Customer title 1"; - List customersTitle1 = new ArrayList<>(); + List> futures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Customer customer = new Customer(); customer.setTenantId(tenantId); @@ -220,10 +231,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 customersTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Customer title 2"; - List customersTitle2 = new ArrayList<>(); + futures = new ArrayList<>(175); for (int i = 0; i < 175; i++) { Customer customer = new Customer(); customer.setTenantId(tenantId); @@ -231,57 +245,60 @@ 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 customersTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); + List loadedCustomersTitle1 = new ArrayList<>(); PageLink pageLink = new PageLink(15, 0, title1); PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, 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 loadedCustomersTitle2 = new ArrayList<>(); pageLink = new PageLink(4, 0, title2); do { - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, 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); - - Assert.assertEquals(customersTitle2, loadedCustomersTitle2); + assertThat(customersTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle2); + List> deleteFutures = new ArrayList<>(143); for (Customer customer : loadedCustomersTitle1) { - doDelete("/api/customer/" + customer.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(() -> + doDelete("/api/customer/" + customer.getId().getId().toString()) + .andExpect(status().isOk()))); } + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title1); - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); + deleteFutures = new ArrayList<>(175); for (Customer customer : loadedCustomersTitle2) { - doDelete("/api/customer/" + customer.getId().getId().toString()) - .andExpect(status().isOk()); + deleteFutures.add(executor.submit(() -> + doDelete("/api/customer/" + customer.getId().getId().toString()) + .andExpect(status().isOk()))); } + Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/customers?", new TypeReference>(){}, pageLink); + pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } From 3084c2d70160f2e463630891fe2348f6b3368e81 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 16:41:40 +0300 Subject: [PATCH 09/28] BaseTenantControllerTest refactored and reformatted --- .../controller/BaseTenantControllerTest.java | 116 +++++++++--------- 1 file changed, 56 insertions(+), 60 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java index d89cc4c3c5..1538dde38f 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -34,9 +34,7 @@ import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageLink; import java.util.ArrayList; -import java.util.Collection; import java.util.List; -import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.TimeUnit; import static org.assertj.core.api.Assertions.assertThat; @@ -50,7 +48,6 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { static final TypeReference> PAGE_DATA_TENANT_INFO_TYPE_REF = new TypeReference<>(){}; static final int TIMEOUT = 30; - List> createFutures = new ArrayList<>(); List> deleteFutures = new ArrayList<>(); ListeningExecutorService executor; @@ -76,10 +73,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 @@ -96,11 +93,11 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { 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 @@ -109,10 +106,10 @@ 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()); } @@ -121,8 +118,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { 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 @@ -132,8 +129,8 @@ 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 @@ -142,29 +139,30 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { 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(); - Collection tenants = new ConcurrentLinkedQueue<>(); + List tenants = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData 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> createFutures = new ArrayList<>(56); + for (int i = 0; i < 56; i++) { Tenant tenant = new Tenant(); - tenant.setTitle("Tenant"+i); + tenant.setTitle("Tenant" + i); createFutures.add(executor.submit(() -> - tenants.add(doPost("/api/tenant", tenant, Tenant.class)))); + doPost("/api/tenant", tenant, Tenant.class))); } - Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS)); List loadedTenants = new ArrayList<>(); pageLink = new PageLink(17); @@ -180,15 +178,15 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { for (Tenant tenant : loadedTenants) { if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - deleteFutures.add(executor.submit(()-> - doDelete("/api/tenant/"+tenant.getId().getId().toString()) + deleteFutures.add(executor.submit(() -> + doDelete("/api/tenant/" + tenant.getId().getId().toString()) .andExpect(status().isOk()))); } } Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(17); - pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); + pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); } @@ -199,39 +197,36 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { loginSysAdmin(); log.debug("test started"); String title1 = "Tenant title 1"; - Collection tenantsTitle1 = new ConcurrentLinkedQueue<>(); - createFutures.clear(); - for (int i=0;i<134;i++) { + List> 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); - createFutures.add(executor.submit(() -> - tenantsTitle1.add(doPost("/api/tenant", tenant, Tenant.class)))); + doPost("/api/tenant", tenant, Tenant.class))); } - Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("saved '{}', qty {}", title1, 134); + List tenantsTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("saved '{}', qty {}", title1, tenantsTitle1.size()); String title2 = "Tenant title 2"; - Collection tenantsTitle2 = new ConcurrentLinkedQueue<>(); - createFutures.clear(); - 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); createFutures.add(executor.submit(() -> - tenantsTitle2.add(doPost("/api/tenant", tenant, Tenant.class)))); + doPost("/api/tenant", tenant, Tenant.class))); } - Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); - log.debug("saved '{}', qty {}", title2, 127); + List tenantsTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("saved '{}', qty {}", title2, tenantsTitle2.size()); - List loadedTenantsTitle1 = new ArrayList<>(); + List loadedTenantsTitle1 = new ArrayList<>(134); PageLink pageLink = new PageLink(15, 0, title1); PageData pageData = null; do { @@ -247,7 +242,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { assertThat(tenantsTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedTenantsTitle1); log.debug("asserted"); - List loadedTenantsTitle2 = new ArrayList<>(); + List loadedTenantsTitle2 = new ArrayList<>(127); pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenants?", PAGE_DATA_TENANT_TYPE_REF, pageLink); @@ -263,8 +258,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { deleteFutures.clear(); for (Tenant tenant : loadedTenantsTitle1) { - deleteFutures.add(executor.submit(()-> - doDelete("/api/tenant/"+tenant.getId().getId().toString()) + deleteFutures.add(executor.submit(() -> + doDelete("/api/tenant/" + tenant.getId().getId().toString()) .andExpect(status().isOk()))); } @@ -280,8 +275,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { deleteFutures.clear(); for (Tenant tenant : loadedTenantsTitle2) { - deleteFutures.add(executor.submit(()-> - doDelete("/api/tenant/"+tenant.getId().getId().toString()) + deleteFutures.add(executor.submit(() -> + doDelete("/api/tenant/" + tenant.getId().getId().toString()) .andExpect(status().isOk()))); } @@ -298,20 +293,21 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { @Test public void testFindTenantInfos() throws Exception { loginSysAdmin(); - Collection tenants = new ConcurrentLinkedQueue<>(); + List tenants = new ArrayList<>(); PageLink pageLink = new PageLink(17); PageData 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> createFutures = new ArrayList<>(56); + for (int i = 0; i < 56; i++) { Tenant tenant = new Tenant(); - tenant.setTitle("Tenant"+i); + tenant.setTitle("Tenant" + i); createFutures.add(executor.submit(() -> - tenants.add(new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default")))); + new TenantInfo(doPost("/api/tenant", tenant, Tenant.class), "Default"))); } - Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + tenants.addAll(Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS)); List loadedTenants = new ArrayList<>(); pageLink = new PageLink(17); @@ -322,20 +318,20 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { pageLink = pageLink.nextPageLink(); } } while (pageData.hasNext()); - + assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants); for (TenantInfo tenant : loadedTenants) { if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - deleteFutures.add(executor.submit(()-> - doDelete("/api/tenant/"+tenant.getId().getId().toString()) + deleteFutures.add(executor.submit(() -> + doDelete("/api/tenant/" + tenant.getId().getId().toString()) .andExpect(status().isOk()))); } } Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); - + pageLink = new PageLink(17); - pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink); + pageData = doGetTypedWithPageLink("/api/tenantInfos?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); } From 92f141e2ca641255f8083ff8114e142f7beb4281 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 17:20:58 +0300 Subject: [PATCH 10/28] CustomerServiceSqlTest refactored in async manner to improve performance --- .../dao/service/BaseCustomerServiceTest.java | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java b/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java index ee1d50ecc5..4dd3169579 100644 --- a/dao/src/test/java/org/thingsboard/server/dao/service/BaseCustomerServiceTest.java +++ b/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 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 customers = new ArrayList<>(); + List> 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 customers = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedCustomers = new ArrayList<>(); + List loadedCustomers = new ArrayList<>(135); PageLink pageLink = new PageLink(23); PageData 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 customersTitle1 = new ArrayList<>(); + List> 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 customersTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Customer title 2"; - List 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 customersTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedCustomersTitle1 = new ArrayList<>(); + List loadedCustomersTitle1 = new ArrayList<>(143); PageLink pageLink = new PageLink(15, 0, title1); PageData 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 loadedCustomersTitle2 = new ArrayList<>(); + List 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> 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); From e61a77af3068db3b65a284d34528af5502385d53 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 19 Apr 2022 19:50:50 +0300 Subject: [PATCH 11/28] BaseDeviceControllerTest refactored in async manner to improve performance --- .../controller/BaseDeviceControllerTest.java | 383 ++++++++++-------- 1 file changed, 206 insertions(+), 177 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 27173b3e38..7be96323c2 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -17,11 +17,18 @@ 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.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.EntitySubtype; @@ -41,22 +48,33 @@ 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 int TIMEOUT = 30; + static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>(){}; - private IdComparator idComparator = new IdComparator<>(); + ListeningExecutorService executor; + + List> createFutures; + List> deleteFutures; + PageData pageData; private Tenant savedTenant; private User tenantAdmin; @Before public void beforeTest() throws Exception { + log.warn("beforeTest"); + executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(12, getClass())); + loginSysAdmin(); Tenant tenant = new Tenant(); @@ -76,10 +94,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @After public void afterTest() throws Exception { + log.warn("afterTest..."); + executor.shutdownNow(); + loginSysAdmin(); - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) + doDelete("/api/tenant/" + savedTenant.getId().getId()) .andExpect(status().isOk()); + log.warn("afterTest done"); } @Test @@ -98,7 +120,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 +132,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 +167,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); } @@ -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 devices = new ArrayList<>(); + log.warn("testFindTenantDevices"); + createFutures = 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)); + createFutures.add(executor.submit(() -> + doPost("/api/device", device, Device.class))); } - List loadedDevices = new ArrayList<>(); + log.warn("await create devices"); + List devices = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + + log.warn("start reading"); + List loadedDevices = new ArrayList<>(178); PageLink pageLink = new PageLink(23); - PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/devices?", - new TypeReference>(){}, 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.warn("asserting"); + assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); + log.warn("delete devices async"); + deleteDevicesAsync("/api/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); + log.warn("done"); } @Test public void testFindTenantDevicesByName() throws Exception { String title1 = "Device title 1"; - List devicesTitle1 = new ArrayList<>(); + + createFutures = 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)); + createFutures.add(executor.submit(() -> + doPost("/api/device", device, Device.class))); } + List devicesTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Device title 2"; - List devicesTitle2 = new ArrayList<>(); + createFutures = new ArrayList<>(75); for (int i = 0; i < 75; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -436,68 +468,69 @@ 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)); + createFutures.add(executor.submit(() -> + doPost("/api/device", device, Device.class))); } + List devicesTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedDevicesTitle1 = new ArrayList<>(); + List loadedDevicesTitle1 = new ArrayList<>(143); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/devices?", - new TypeReference>(){}, 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); + assertThat(devicesTitle1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle1); - Assert.assertEquals(devicesTitle1, loadedDevicesTitle1); - - List loadedDevicesTitle2 = new ArrayList<>(); + List loadedDevicesTitle2 = new ArrayList<>(75); pageLink = new PageLink(4, 0, title2); do { pageData = doGetTypedWithPageLink("/api/tenant/devices?", - new TypeReference>(){}, 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); + deleteDevicesAsync("/api/device/", loadedDevicesTitle1).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>(){}, 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()); - } + deleteDevicesAsync("/api/device/", loadedDevicesTitle2).get(TIMEOUT, TimeUnit.SECONDS); + pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/tenant/devices?", - new TypeReference>(){}, pageLink); + PAGE_DATA_DEVICE_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } + ListenableFuture> deleteDevicesAsync(String urlTemplate, List loadedDevicesTitle1) { + deleteFutures = new ArrayList<>(loadedDevicesTitle1.size()); + for (Device device : loadedDevicesTitle1) { + deleteFutures.add(executor.submit(() -> + doDelete(urlTemplate + device.getId().getId()) + .andExpect(status().isOk()))); + } + return Futures.allAsList(deleteFutures); + } + @Test public void testFindTenantDevicesByType() throws Exception { String title1 = "Device title 1"; String type1 = "typeA"; - List devicesType1 = new ArrayList<>(); + createFutures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -505,11 +538,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)); + createFutures.add(executor.submit(() -> + doPost("/api/device", device, Device.class))); + if (i == 0) { + createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + } } + List devicesType1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Device title 2"; String type2 = "typeB"; - List devicesType2 = new ArrayList<>(); + createFutures = new ArrayList<>(75); for (int i = 0; i < 75; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -517,61 +556,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)); + createFutures.add(executor.submit(() -> + doPost("/api/device", device, Device.class))); + if (i == 0) { + createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + } } - List loadedDevicesType1 = new ArrayList<>(); + List devicesType2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + + List loadedDevicesType1 = new ArrayList<>(143); PageLink pageLink = new PageLink(15); - PageData pageData = null; do { pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", - new TypeReference>(){}, 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); - - Assert.assertEquals(devicesType1, loadedDevicesType1); + assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1); - List loadedDevicesType2 = new ArrayList<>(); + List loadedDevicesType2 = new ArrayList<>(75); pageLink = new PageLink(4); do { pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", - new TypeReference>(){}, 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); + assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2); - Assert.assertEquals(devicesType2, loadedDevicesType2); - - for (Device device : loadedDevicesType1) { - doDelete("/api/device/" + device.getId().getId().toString()) - .andExpect(status().isOk()); - } + deleteDevicesAsync("/api/device/", loadedDevicesType1).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", - new TypeReference>(){}, 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()); - } + deleteDevicesAsync("/api/device/", loadedDevicesType2).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", - new TypeReference>(){}, pageLink, type2); + PAGE_DATA_DEVICE_TYPE_REF, pageLink, type2); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); } @@ -583,32 +615,35 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); - List devices = new ArrayList<>(); + createFutures = 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 future = executor.submit(() -> doPost("/api/device", device, Device.class)); + createFutures.add(Futures.transform(future, (dev) -> + doPost("/api/customer/" + customerId.getId() + + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } - List loadedDevices = new ArrayList<>(); + List devices = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + + List loadedDevices = new ArrayList<>(128); PageLink pageLink = new PageLink(23); - PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", - new TypeReference>(){}, 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.warn("delete devices async"); + deleteDevicesAsync("/api/customer/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); + log.warn("done"); } @Test @@ -619,7 +654,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { CustomerId customerId = customer.getId(); String title1 = "Device title 1"; - List devicesTitle1 = new ArrayList<>(); + createFutures = new ArrayList<>(125); for (int i = 0; i < 125; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -627,12 +662,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 future = executor.submit(() -> doPost("/api/device", device, Device.class)); + createFutures.add(Futures.transform(future, (dev) -> + doPost("/api/customer/" + customerId.getId() + + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } + List devicesTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Device title 2"; - List devicesTitle2 = new ArrayList<>(); + createFutures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -640,61 +678,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 future = executor.submit(() -> doPost("/api/device", device, Device.class)); + createFutures.add(Futures.transform(future, (dev) -> + doPost("/api/customer/" + customerId.getId() + + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } + List devicesTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedDevicesTitle1 = new ArrayList<>(); + List loadedDevicesTitle1 = new ArrayList<>(125); PageLink pageLink = new PageLink(15, 0, title1); - PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", - new TypeReference>(){}, 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 loadedDevicesTitle2 = new ArrayList<>(); + List loadedDevicesTitle2 = new ArrayList<>(143); pageLink = new PageLink(4, 0, title2); do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", - new TypeReference>(){}, 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); + deleteDevicesAsync("/api/customer/device/", loadedDevicesTitle1).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>(){}, 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()); - } + deleteDevicesAsync("/api/customer/device/", loadedDevicesTitle2).get(TIMEOUT, TimeUnit.SECONDS); + pageLink = new PageLink(4, 0, title2); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?", - new TypeReference>(){}, 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 +737,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { String title1 = "Device title 1"; String type1 = "typeC"; - List devicesType1 = new ArrayList<>(); + createFutures = new ArrayList<>(125); for (int i = 0; i < 125; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -716,13 +745,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 future = executor.submit(() -> doPost("/api/device", device, Device.class)); + createFutures.add(Futures.transform(future, (dev) -> + doPost("/api/customer/" + customerId.getId() + + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); + if (i == 0) { + createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + } } + List devicesType1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + String title2 = "Device title 2"; String type2 = "typeD"; - List devicesType2 = new ArrayList<>(); + createFutures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -730,63 +765,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 future = executor.submit(() -> doPost("/api/device", device, Device.class)); + createFutures.add(Futures.transform(future, (dev) -> + doPost("/api/customer/" + customerId.getId() + + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); + if (i == 0) { + createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + } } + List devicesType2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); - List loadedDevicesType1 = new ArrayList<>(); + List loadedDevicesType1 = new ArrayList<>(125); PageLink pageLink = new PageLink(15); - PageData pageData = null; do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", - new TypeReference>(){}, 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); - - Assert.assertEquals(devicesType1, loadedDevicesType1); + assertThat(devicesType1).as(title1).containsExactlyInAnyOrderElementsOf(loadedDevicesType1); - List loadedDevicesType2 = new ArrayList<>(); + List loadedDevicesType2 = new ArrayList<>(143); pageLink = new PageLink(4); do { - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", - new TypeReference>(){}, 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); + assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2); - Assert.assertEquals(devicesType2, loadedDevicesType2); - - for (Device device : loadedDevicesType1) { - doDelete("/api/customer/device/" + device.getId().getId().toString()) - .andExpect(status().isOk()); - } + deleteDevicesAsync("/api/customer/device/", loadedDevicesType1).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", - new TypeReference>(){}, 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()); - } + deleteDevicesAsync("/api/customer/device/", loadedDevicesType2).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); - pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId().toString() + "/devices?type={type}&", - new TypeReference>(){}, 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 +855,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 +879,21 @@ 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 pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId().toString() + "/devices?", - new TypeReference>() {}, new PageLink(100)); + PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", + new TypeReference>() { + }, 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>() {}, new PageLink(100)); + pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", + new TypeReference>() { + }, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } From 8412397fd19e7bbc544629fac0b22d134c75fb34 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 20 Apr 2022 10:40:38 +0300 Subject: [PATCH 12/28] BaseDeviceControllerTest refactored --- .../controller/BaseDeviceControllerTest.java | 114 +++++++++--------- 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 7be96323c2..3139ba34b4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -59,12 +59,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @Slf4j public abstract class BaseDeviceControllerTest extends AbstractControllerTest { static final int TIMEOUT = 30; - static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>(){}; + static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { + }; ListeningExecutorService executor; - List> createFutures; - List> deleteFutures; + List> futures; PageData pageData; private Tenant savedTenant; @@ -72,8 +72,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Before public void beforeTest() throws Exception { - log.warn("beforeTest"); - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(12, getClass())); + log.debug("beforeTest"); + executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); loginSysAdmin(); @@ -94,14 +94,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @After public void afterTest() throws Exception { - log.warn("afterTest..."); + log.debug("afterTest..."); executor.shutdownNow(); loginSysAdmin(); doDelete("/api/tenant/" + savedTenant.getId().getId()) .andExpect(status().isOk()); - log.warn("afterTest done"); + log.debug("afterTest done"); } @Test @@ -202,6 +202,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()); + + deleteDevicesAsync("/api/device/", devices); } @Test @@ -410,19 +412,19 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { @Test public void testFindTenantDevices() throws Exception { - log.warn("testFindTenantDevices"); - createFutures = new ArrayList<>(178); + 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"); - createFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); } - log.warn("await create devices"); - List devices = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + log.debug("await create devices"); + List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); - log.warn("start reading"); + log.debug("start reading"); List loadedDevices = new ArrayList<>(178); PageLink pageLink = new PageLink(23); do { @@ -435,18 +437,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { } } while (pageData.hasNext()); - log.warn("asserting"); + log.debug("asserting"); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.warn("delete devices async"); + log.debug("delete devices async"); deleteDevicesAsync("/api/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); - log.warn("done"); + log.debug("done"); } @Test public void testFindTenantDevicesByName() throws Exception { String title1 = "Device title 1"; - createFutures = new ArrayList<>(143); + futures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -454,13 +456,13 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); device.setType("default"); - createFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); } - List devicesTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); String title2 = "Device title 2"; - createFutures = new ArrayList<>(75); + futures = new ArrayList<>(75); for (int i = 0; i < 75; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -468,10 +470,10 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); device.setType("default"); - createFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); } - List devicesTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); List loadedDevicesTitle1 = new ArrayList<>(143); PageLink pageLink = new PageLink(15, 0, title1); @@ -517,20 +519,20 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { } ListenableFuture> deleteDevicesAsync(String urlTemplate, List loadedDevicesTitle1) { - deleteFutures = new ArrayList<>(loadedDevicesTitle1.size()); + List> futures = new ArrayList<>(loadedDevicesTitle1.size()); for (Device device : loadedDevicesTitle1) { - deleteFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doDelete(urlTemplate + device.getId().getId()) .andExpect(status().isOk()))); } - return Futures.allAsList(deleteFutures); + return Futures.allAsList(futures); } @Test public void testFindTenantDevicesByType() throws Exception { String title1 = "Device title 1"; String type1 = "typeA"; - createFutures = new ArrayList<>(143); + futures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -538,17 +540,17 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); device.setType(type1); - createFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); if (i == 0) { - createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time } } - List devicesType1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); String title2 = "Device title 2"; String type2 = "typeB"; - createFutures = new ArrayList<>(75); + futures = new ArrayList<>(75); for (int i = 0; i < 75; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -556,14 +558,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { name = i % 2 == 0 ? name.toLowerCase() : name.toUpperCase(); device.setName(name); device.setType(type2); - createFutures.add(executor.submit(() -> + futures.add(executor.submit(() -> doPost("/api/device", device, Device.class))); if (i == 0) { - createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time } } - List devicesType2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); List loadedDevicesType1 = new ArrayList<>(143); PageLink pageLink = new PageLink(15); @@ -615,18 +617,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { customer = doPost("/api/customer", customer, Customer.class); CustomerId customerId = customer.getId(); - createFutures = new ArrayList<>(128); + futures = new ArrayList<>(128); for (int i = 0; i < 128; i++) { Device device = new Device(); device.setName("Device" + i); device.setType("default"); ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class)); - createFutures.add(Futures.transform(future, (dev) -> + futures.add(Futures.transform(future, (dev) -> doPost("/api/customer/" + customerId.getId() + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } - List devices = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devices = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); List loadedDevices = new ArrayList<>(128); PageLink pageLink = new PageLink(23); @@ -641,9 +643,9 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); - log.warn("delete devices async"); + log.debug("delete devices async"); deleteDevicesAsync("/api/customer/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); - log.warn("done"); + log.debug("done"); } @Test @@ -654,7 +656,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { CustomerId customerId = customer.getId(); String title1 = "Device title 1"; - createFutures = new ArrayList<>(125); + futures = new ArrayList<>(125); for (int i = 0; i < 125; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -663,14 +665,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName(name); device.setType("default"); ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class)); - createFutures.add(Futures.transform(future, (dev) -> + futures.add(Futures.transform(future, (dev) -> doPost("/api/customer/" + customerId.getId() + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } - List devicesTitle1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesTitle1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); String title2 = "Device title 2"; - createFutures = new ArrayList<>(143); + futures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -679,11 +681,11 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName(name); device.setType("default"); ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class)); - createFutures.add(Futures.transform(future, (dev) -> + futures.add(Futures.transform(future, (dev) -> doPost("/api/customer/" + customerId.getId() + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); } - List devicesTitle2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesTitle2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); List loadedDevicesTitle1 = new ArrayList<>(125); PageLink pageLink = new PageLink(15, 0, title1); @@ -737,7 +739,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { String title1 = "Device title 1"; String type1 = "typeC"; - createFutures = new ArrayList<>(125); + futures = new ArrayList<>(125); for (int i = 0; i < 125; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -746,18 +748,18 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName(name); device.setType(type1); ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class)); - createFutures.add(Futures.transform(future, (dev) -> + futures.add(Futures.transform(future, (dev) -> doPost("/api/customer/" + customerId.getId() + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); if (i == 0) { - createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time } } - List devicesType1 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesType1 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); String title2 = "Device title 2"; String type2 = "typeD"; - createFutures = new ArrayList<>(143); + futures = new ArrayList<>(143); for (int i = 0; i < 143; i++) { Device device = new Device(); String suffix = RandomStringUtils.randomAlphanumeric(15); @@ -766,14 +768,14 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { device.setName(name); device.setType(type2); ListenableFuture future = executor.submit(() -> doPost("/api/device", device, Device.class)); - createFutures.add(Futures.transform(future, (dev) -> + futures.add(Futures.transform(future, (dev) -> doPost("/api/customer/" + customerId.getId() + "/device/" + dev.getId().getId(), Device.class), MoreExecutors.directExecutor())); if (i == 0) { - createFutures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time + futures.get(0).get(TIMEOUT, TimeUnit.SECONDS); // wait for the device profile created first time } } - List devicesType2 = Futures.allAsList(createFutures).get(TIMEOUT, TimeUnit.SECONDS); + List devicesType2 = Futures.allAsList(futures).get(TIMEOUT, TimeUnit.SECONDS); List loadedDevicesType1 = new ArrayList<>(125); PageLink pageLink = new PageLink(15); @@ -882,9 +884,8 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { doPost("/api/edge/" + savedEdge.getId().getId() + "/device/" + savedDevice.getId().getId(), Device.class); - PageData pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", - new TypeReference>() { - }, 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()); @@ -892,8 +893,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { + "/device/" + savedDevice.getId().getId(), Device.class); pageData = doGetTypedWithPageLink("/api/edge/" + savedEdge.getId().getId() + "/devices?", - new TypeReference>() { - }, new PageLink(100)); + PAGE_DATA_DEVICE_TYPE_REF, new PageLink(100)); Assert.assertEquals(0, pageData.getData().size()); } From ce5c6f4d9c0f9c48c9b3f9574a58bbe8d952e1c6 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 20 Apr 2022 12:22:44 +0300 Subject: [PATCH 13/28] AbstractWebsocketTest refactored --- .../controller/AbstractControllerTest.java | 2 +- .../server/controller/AbstractWebTest.java | 1 + .../controller/AbstractWebsocketTest.java | 43 ++++- .../controller/BaseWebsocketApiTest.java | 170 +++++++----------- .../lwm2m/AbstractLwM2MIntegrationTest.java | 11 +- 5 files changed, 108 insertions(+), 119 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index eae6f59198..d32d9c5cd3 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java @@ -36,6 +36,6 @@ import org.springframework.test.context.web.WebAppConfiguration; @WebAppConfiguration @SpringBootTest() @Slf4j -public abstract class AbstractControllerTest extends AbstractWebTest { +public abstract class AbstractControllerTest extends AbstractWebsocketTest { } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 97e5c749af..cf14088e43 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -97,6 +97,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(); diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java index 15884f38a0..d1e50cbbce 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java @@ -16,7 +16,8 @@ package org.thingsboard.server.controller; import lombok.extern.slf4j.Slf4j; -import org.junit.Assert; +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; @@ -27,8 +28,12 @@ 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; +import java.util.concurrent.TimeUnit; + +import static org.assertj.core.api.Assertions.assertThat; @ActiveProfiles("test") @RunWith(SpringRunner.class) @@ -39,15 +44,43 @@ import java.net.URISyntaxException; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Slf4j public abstract class AbstractWebsocketTest extends AbstractWebTest { - - protected static final String WS_URL = "ws://localhost:"; + public static final String WS_URL = "ws://localhost:"; @LocalServerPort protected int wsPort; - protected TbTestWebSocketClient buildAndConnectWebSocketClient() throws URISyntaxException, InterruptedException { + 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)); - Assert.assertTrue(wsClient.connectBlocking()); + assertThat(wsClient.connectBlocking(TIMEOUT, TimeUnit.SECONDS)).isTrue(); return wsClient; } diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java index 15c1c8d3ac..d85cb30006 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java @@ -24,9 +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; @@ -46,7 +43,6 @@ 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; @@ -65,49 +61,11 @@ import java.util.List; import java.util.concurrent.CountDownLatch; 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; - @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"); - - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); - - wsClient = buildAndConnectWebSocketClient(); - } - - @After - public void afterTest() throws Exception { - wsClient.close(); - - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); - } - @Test public void testEntityDataHistoryWsCmd() throws Exception { Device device = new Device(); @@ -136,8 +94,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); @@ -154,8 +112,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { sendTelemetry(device, tsData); Thread.sleep(100); - wsClient.send(mapper.writeValueAsString(wrapper)); - msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + msg = getWsClient().waitForReply(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); List dataList = update.getUpdate(); @@ -190,8 +148,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); @@ -217,8 +175,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { cmd = new EntityDataCmd(1, null, null, null, tsCmd); wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + msg = getWsClient().waitForReply(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); List listData = update.getUpdate(); @@ -233,10 +191,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(); + msg = getWsClient().waitForUpdate(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -271,8 +229,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper1 = new TelemetryPluginCmdsWrapper(); wrapper1.setEntityCountCmds(Collections.singletonList(cmd1)); - wsClient.send(mapper.writeValueAsString(wrapper1)); - String msg1 = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper1)); + String msg1 = getWsClient().waitForReply(); EntityCountUpdate update1 = mapper.readValue(msg1, EntityCountUpdate.class); Assert.assertEquals(1, update1.getCmdId()); Assert.assertEquals(1, update1.getCount()); @@ -286,9 +244,9 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper2 = new TelemetryPluginCmdsWrapper(); wrapper2.setEntityCountCmds(Collections.singletonList(cmd2)); - wsClient.send(mapper.writeValueAsString(wrapper2)); + getWsClient().send(mapper.writeValueAsString(wrapper2)); - String msg2 = wsClient.waitForReply(); + String msg2 = getWsClient().waitForReply(); EntityCountUpdate update2 = mapper.readValue(msg2, EntityCountUpdate.class); Assert.assertEquals(2, update2.getCmdId()); Assert.assertEquals(0, update2.getCount()); @@ -310,9 +268,9 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper3 = new TelemetryPluginCmdsWrapper(); wrapper3.setEntityCountCmds(Collections.singletonList(cmd3)); - wsClient.send(mapper.writeValueAsString(wrapper3)); + getWsClient().send(mapper.writeValueAsString(wrapper3)); - String msg3 = wsClient.waitForReply(); + String msg3 = getWsClient().waitForReply(); EntityCountUpdate update3 = mapper.readValue(msg3, EntityCountUpdate.class); Assert.assertEquals(3, update3.getCmdId()); Assert.assertEquals(1, update3.getCount()); @@ -334,9 +292,9 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper4 = new TelemetryPluginCmdsWrapper(); wrapper4.setEntityCountCmds(Collections.singletonList(cmd4)); - wsClient.send(mapper.writeValueAsString(wrapper4)); + getWsClient().send(mapper.writeValueAsString(wrapper4)); - String msg4 = wsClient.waitForReply(); + String msg4 = getWsClient().waitForReply(); EntityCountUpdate update4 = mapper.readValue(msg4, EntityCountUpdate.class); Assert.assertEquals(4, update4.getCmdId()); Assert.assertEquals(0, update4.getCount()); @@ -363,8 +321,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); @@ -387,8 +345,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + msg = getWsClient().waitForReply(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -404,9 +362,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(); + msg = getWsClient().waitForUpdate(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -419,15 +377,15 @@ 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)); + 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); } @@ -453,8 +411,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); @@ -475,8 +433,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + msg = getWsClient().waitForReply(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -492,9 +450,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(); + msg = getWsClient().waitForUpdate(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -507,15 +465,15 @@ 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)); + 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); } @@ -542,8 +500,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); Assert.assertNotNull(msg); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -556,14 +514,14 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { 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 tsData = Arrays.asList(dataPoint1); sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData); - msg = wsClient.waitForUpdate(); + msg = getWsClient().waitForUpdate(); Assert.assertNotNull(msg); update = mapper.readValue(msg, EntityDataUpdate.class); @@ -579,10 +537,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,17 +554,17 @@ 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); } @@ -638,8 +596,8 @@ public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - wsClient.send(mapper.writeValueAsString(wrapper)); - String msg = wsClient.waitForReply(); + getWsClient().send(mapper.writeValueAsString(wrapper)); + String msg = getWsClient().waitForReply(); EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); @@ -659,14 +617,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 tsData = Arrays.asList(dataPoint1); Thread.sleep(100); sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData); - msg = wsClient.waitForUpdate(); + msg = getWsClient().waitForUpdate(); Assert.assertNotNull(msg); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -679,22 +637,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 +663,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 +677,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 +691,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(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index a0f3dbf750..9f81e05178 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java @@ -176,7 +176,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest protected final Set 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,7 +186,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest @After public void after() { - wsClient.close(); clientDestroy(); executor.shutdownNow(); } @@ -212,7 +210,6 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest }); Assert.assertNotNull(lwModel); } - wsClient = buildAndConnectWebSocketClient(); } public void basicTestConnectionObserveTelemetry(Security security, @@ -234,12 +231,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()); From a67950557939e42e09c4abcb6833ee5dee193b9f Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Wed, 20 Apr 2022 14:44:01 +0300 Subject: [PATCH 14/28] AbstractWebsocketTest merged into AbstractControllerTest. EnableWebSocket instead WebAppConfiguration. WebEnvironment.RANDOM_PORT --- .../controller/AbstractControllerTest.java | 57 +++++++++++- .../controller/AbstractWebsocketTest.java | 87 ------------------- .../controller/BaseWebsocketApiTest.java | 8 +- .../lwm2m/AbstractLwM2MIntegrationTest.java | 13 ++- 4 files changed, 65 insertions(+), 100 deletions(-) delete mode 100644 application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java index d32d9c5cd3..98e4c12759 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java +++ b/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 AbstractWebsocketTest { +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; + } } diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java deleted file mode 100644 index d1e50cbbce..0000000000 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebsocketTest.java +++ /dev/null @@ -1,87 +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.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 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) -@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 { - 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; - } - -} diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java index d85cb30006..111f14acb0 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java @@ -18,7 +18,6 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; -import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -62,10 +61,15 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @Slf4j -public abstract class BaseWebsocketApiTest extends AbstractWebsocketTest { +public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @Autowired private TelemetrySubscriptionService tsService; + @Before + public void setUp() throws Exception { + loginTenantAdmin(); + } + @Test public void testEntityDataHistoryWsCmd() throws Exception { Device device = new Device(); diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java index 9f81e05178..20aa0a1805 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java +++ b/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; @@ -191,11 +190,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractWebsocketTest } @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) { @@ -368,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); @@ -387,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); From c744e2cd2e7db8be5aa98149035e41549f1e553a Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 14:23:47 +0300 Subject: [PATCH 15/28] Swagger disabled for the test profile --- .../org/thingsboard/server/config/SwaggerConfiguration.java | 2 ++ .../server/common/data/query/DeviceTypeFilter.java | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java b/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java index e71b881477..b6ba0088bb 100644 --- a/application/src/main/java/org/thingsboard/server/config/SwaggerConfiguration.java +++ b/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}") diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceTypeFilter.java b/common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceTypeFilter.java index 5c5d5d83df..d616a68783 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/query/DeviceTypeFilter.java +++ b/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 { From 36429a29728d3498fccc479155fa5b2dc6397f10 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 15:12:44 +0300 Subject: [PATCH 16/28] MockJsInvokeService for the test scope implemented --- .../service/script/MockJsInvokeService.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 application/src/test/java/org/thingsboard/server/service/script/MockJsInvokeService.java diff --git a/application/src/test/java/org/thingsboard/server/service/script/MockJsInvokeService.java b/application/src/test/java/org/thingsboard/server/service/script/MockJsInvokeService.java new file mode 100644 index 0000000000..17803bac96 --- /dev/null +++ b/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 eval(TenantId tenantId, JsScriptType scriptType, String scriptBody, String... argNames) { + log.warn("eval {} {} {} {}", tenantId, scriptType, scriptBody, argNames); + return Futures.immediateFuture(UUID.randomUUID()); + } + + @Override + public ListenableFuture invokeFunction(TenantId tenantId, CustomerId customerId, UUID scriptId, Object... args) { + log.warn("invokeFunction {} {} {} {}", tenantId, customerId, scriptId, args); + return Futures.immediateFuture("{}"); + } + + @Override + public ListenableFuture release(UUID scriptId) { + log.warn("release {}", scriptId); + return Futures.immediateFuture(null); + } +} From 7cdce3d9f85340c4fa8fff7c7f1b33b34314d808 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 15:26:41 +0300 Subject: [PATCH 17/28] Timeout const removed as it placed on AbstractWebTest --- .../server/controller/BaseCustomerControllerTest.java | 1 - .../server/service/sql/SequentialTimeseriesPersistenceTest.java | 2 -- .../server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java | 2 -- 3 files changed, 5 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 492b76374a..628cf0c495 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -44,7 +44,6 @@ import static org.hamcrest.Matchers.containsString; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; public abstract class BaseCustomerControllerTest extends AbstractControllerTest { - static final int TIMEOUT = 30; static final TypeReference> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() {}; ListeningExecutorService executor; diff --git a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java b/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java index 2598bbe7d1..c1b87d9ac5 100644 --- a/application/src/test/java/org/thingsboard/server/service/sql/SequentialTimeseriesPersistenceTest.java +++ b/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"; diff --git a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java index 8173b39b5c..f7d1303674 100644 --- a/application/src/test/java/org/thingsboard/server/transport/lwm2m/ota/sql/OtaLwM2MIntegrationTest.java +++ b/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" + From 96eff9cf1a80fa91600efe6f571600962602b579 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 17:39:41 +0300 Subject: [PATCH 18/28] web socket test and client refactored to simplify usage --- .../controller/BaseWebsocketApiTest.java | 290 ++++-------------- .../controller/TbTestWebSocketClient.java | 113 +++++++ 2 files changed, 174 insertions(+), 229 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java index 111f14acb0..90d6ac7b9d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java @@ -18,12 +18,12 @@ package org.thingsboard.server.controller; import com.google.common.util.concurrent.FutureCallback; import lombok.extern.slf4j.Slf4j; import org.checkerframework.checker.nullness.qual.Nullable; +import org.junit.After; import org.junit.Assert; 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.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; @@ -44,63 +44,51 @@ import org.thingsboard.server.common.data.query.NumericFilterPredicate; import org.thingsboard.server.common.data.query.TsValue; 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; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @Slf4j public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @Autowired private TelemetrySubscriptionService tsService; + Device device; + DeviceTypeFilter dtf; + @Before public void setUp() throws Exception { loginTenantAdmin(); - } - @Test - public void testEntityDataHistoryWsCmd() throws Exception { - Device device = new Device(); + 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 tearDown() throws Exception { + doDelete("/api/device/" + device.getId().getId()) + .andExpect(status().isOk()); + } + + @Test + public void testEntityDataHistoryWsCmd() throws Exception { + List 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)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().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 pageData = update.getData(); Assert.assertNotNull(pageData); @@ -116,9 +104,8 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { sendTelemetry(device, tsData); Thread.sleep(100); - getWsClient().send(mapper.writeValueAsString(wrapper)); - msg = getWsClient().waitForReply(); - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().sendHistoryCmd(keys, now, TimeUnit.HOURS.toMillis(1), dtf); + Assert.assertEquals(1, update.getCmdId()); List dataList = update.getUpdate(); Assert.assertNotNull(dataList); @@ -133,41 +120,16 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @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); - - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); + EntityDataUpdate update = getWsClient().sendEntityDataQuery(dtf); - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().waitForReply(); - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData 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)); @@ -176,12 +138,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { sendTelemetry(device, tsData); Thread.sleep(100); - cmd = new EntityDataCmd(1, null, null, null, tsCmd); - wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - getWsClient().send(mapper.writeValueAsString(wrapper)); - msg = getWsClient().waitForReply(); - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().subscribeTsUpdate(List.of("temperature"), now, TimeUnit.HOURS.toMillis(1)); Assert.assertEquals(1, update.getCmdId()); List listData = update.getUpdate(); Assert.assertNotNull(listData); @@ -198,7 +155,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { getWsClient().registerWaitForUpdate(); Thread.sleep(100); sendTelemetry(device, Arrays.asList(dataPoint4)); - msg = getWsClient().waitForUpdate(); + String msg = getWsClient().waitForUpdate(); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); @@ -214,44 +171,26 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @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); - getWsClient().send(mapper.writeValueAsString(wrapper1)); - String msg1 = getWsClient().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)); - getWsClient().send(mapper.writeValueAsString(wrapper2)); + getWsClient().send(cmd2); String msg2 = getWsClient().waitForReply(); - EntityCountUpdate update2 = mapper.readValue(msg2, EntityCountUpdate.class); + EntityCountUpdate update2 = getWsClient().parseCountReply(getWsClient().waitForReply()); Assert.assertEquals(2, update2.getCmdId()); Assert.assertEquals(0, update2.getCount()); @@ -263,19 +202,12 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { 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)); - getWsClient().send(mapper.writeValueAsString(wrapper3)); - - String msg3 = getWsClient().waitForReply(); - EntityCountUpdate update3 = mapper.readValue(msg3, EntityCountUpdate.class); + EntityCountUpdate update3 = getWsClient().parseCountReply(getWsClient().waitForReply()); Assert.assertEquals(3, update3.getCmdId()); Assert.assertEquals(1, update3.getCount()); @@ -287,47 +219,26 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { 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)); - getWsClient().send(mapper.writeValueAsString(wrapper4)); + getWsClient().send(cmd4); - String msg4 = getWsClient().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 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()); - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().waitForReply(); - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + EntityDataUpdate update = getWsClient().sendEntityDataQuery(edq); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); Assert.assertNotNull(pageData); @@ -341,17 +252,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { List 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)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - msg = getWsClient().waitForReply(); - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().subscribeLatestUpdate(keys); Assert.assertEquals(1, update.getCmdId()); @@ -368,9 +269,8 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { getWsClient().registerWaitForUpdate(); sendTelemetry(device, Arrays.asList(dataPoint2)); - msg = getWsClient().waitForUpdate(); - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().parseDataReply(getWsClient().waitForUpdate()); Assert.assertEquals(1, update.getCmdId()); List eData = update.getUpdate(); Assert.assertNotNull(eData); @@ -383,7 +283,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { //Sending update from the past, while latest value has new timestamp; getWsClient().registerWaitForUpdate(); sendTelemetry(device, Arrays.asList(dataPoint1)); - msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1)); + String msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1)); Assert.assertNull(msg); //Sending duplicate update again @@ -395,29 +295,11 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @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 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); + EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf); - TelemetryPluginCmdsWrapper wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().waitForReply(); - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); Assert.assertNotNull(pageData); @@ -433,13 +315,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { Thread.sleep(100); - cmd = new EntityDataCmd(1, edq, null, latestCmd, null); - wrapper = new TelemetryPluginCmdsWrapper(); - wrapper.setEntityDataCmds(Collections.singletonList(cmd)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - msg = getWsClient().waitForReply(); - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().subscribeLatestUpdate(keys, dtf); Assert.assertEquals(1, update.getCmdId()); @@ -456,9 +332,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { getWsClient().registerWaitForUpdate(); sendTelemetry(device, Arrays.asList(dataPoint2)); - msg = getWsClient().waitForUpdate(); - - update = mapper.readValue(msg, EntityDataUpdate.class); + update = getWsClient().parseDataReply(getWsClient().waitForUpdate()); Assert.assertEquals(1, update.getCmdId()); List eData = update.getUpdate(); Assert.assertNotNull(eData); @@ -471,7 +345,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { //Sending update from the past, while latest value has new timestamp; getWsClient().registerWaitForUpdate(); sendTelemetry(device, Arrays.asList(dataPoint1)); - msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1)); + String msg = getWsClient().waitForUpdate(TimeUnit.SECONDS.toMillis(1)); Assert.assertNull(msg); //Sending duplicate update again @@ -483,31 +357,10 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @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 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)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().waitForReply(); - Assert.assertNotNull(msg); - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); + EntityDataUpdate update = getWsClient().subscribeLatestUpdate(keys, dtf); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); Assert.assertNotNull(pageData); @@ -517,7 +370,6 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { 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()); - getWsClient().registerWaitForUpdate(); Thread.sleep(500); @@ -525,7 +377,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { List tsData = Arrays.asList(dataPoint1); sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData); - msg = getWsClient().waitForUpdate(); + String msg = getWsClient().waitForUpdate(); Assert.assertNotNull(msg); update = mapper.readValue(msg, EntityDataUpdate.class); @@ -574,35 +426,15 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { @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 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 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)); - - getWsClient().send(mapper.writeValueAsString(wrapper)); - String msg = getWsClient().waitForReply(); - EntityDataUpdate update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); PageData pageData = update.getData(); Assert.assertNotNull(pageData); @@ -628,7 +460,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest { sendAttributes(device, TbAttributeSubscriptionScope.SERVER_SCOPE, tsData); - msg = getWsClient().waitForUpdate(); + String msg = getWsClient().waitForUpdate(); Assert.assertNotNull(msg); update = mapper.readValue(msg, EntityDataUpdate.class); Assert.assertEquals(1, update.getCmdId()); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index 3abd311aa3..013547aa24 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/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,89 @@ 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 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 keys) { + return subscribeLatestUpdate(keys, (EntityDataQuery) null); + } + + public EntityDataUpdate subscribeLatestUpdate(List 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 keys, long startTs, long timeWindow) { + return subscribeTsUpdate(keys, startTs, timeWindow, null); + } + + public EntityDataUpdate subscribeTsUpdate(List 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 keys, long startTs, long timeWindow) { + return sendHistoryCmd(keys, startTs, timeWindow, (EntityDataQuery) null); + } + + public EntityDataUpdate sendHistoryCmd(List 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 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); + } + } From 831081829b4f9102ff1b66517b049a737e50061b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 18:06:35 +0300 Subject: [PATCH 19/28] deleteEntitiesAsync added to the AbstractWebTest --- .../server/controller/AbstractWebTest.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index cf14088e43..6f88787c52 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/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; @@ -642,4 +646,15 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest { edge.setRoutingKey(RandomStringUtils.randomAlphanumeric(20)); return edge; } + + protected > ListenableFuture> deleteEntitiesAsync(String urlTemplate, List entities, ListeningExecutorService executor) { + List> 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); + } + } From db07771dc5d60c7e280ff7adfeb06f7ae7807131 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 18:10:09 +0300 Subject: [PATCH 20/28] massive controller tests refactored --- .../BaseCustomerControllerTest.java | 22 ++----- .../controller/BaseDeviceControllerTest.java | 34 ++++------ .../controller/BaseTenantControllerTest.java | 63 +++++++++---------- 3 files changed, 45 insertions(+), 74 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java index 628cf0c495..a7f623d2e4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java @@ -25,7 +25,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -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.Tenant; @@ -44,7 +43,8 @@ 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> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() {}; + static final TypeReference> PAGE_DATA_CUSTOMER_TYPE_REFERENCE = new TypeReference<>() { + }; ListeningExecutorService executor; @@ -215,6 +215,8 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest } while (pageData.hasNext()); assertThat(customers).containsExactlyInAnyOrderElementsOf(loadedCustomers); + + deleteEntitiesAsync("/api/customer/", loadedCustomers, executor).get(TIMEOUT, TimeUnit.SECONDS); } @Test @@ -275,26 +277,14 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest assertThat(customersTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedCustomersTitle2); - List> deleteFutures = new ArrayList<>(143); - for (Customer customer : loadedCustomersTitle1) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/customer/" + customer.getId().getId().toString()) - .andExpect(status().isOk()))); - } - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/", loadedCustomersTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - deleteFutures = new ArrayList<>(175); - for (Customer customer : loadedCustomersTitle2) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/customer/" + customer.getId().getId().toString()) - .andExpect(status().isOk()))); - } - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/", loadedCustomersTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customers?", PAGE_DATA_CUSTOMER_TYPE_REFERENCE, pageLink); diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java index 3139ba34b4..d6e30c644c 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java @@ -27,7 +27,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -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; @@ -58,7 +57,6 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; @Slf4j public abstract class BaseDeviceControllerTest extends AbstractControllerTest { - static final int TIMEOUT = 30; static final TypeReference> PAGE_DATA_DEVICE_TYPE_REF = new TypeReference<>() { }; @@ -203,7 +201,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals("typeB", deviceTypes.get(1).getType()); Assert.assertEquals("typeC", deviceTypes.get(2).getType()); - deleteDevicesAsync("/api/device/", devices); + deleteEntitiesAsync("/api/device/", devices, executor).get(TIMEOUT, TimeUnit.SECONDS); } @Test @@ -440,7 +438,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { log.debug("asserting"); assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); log.debug("delete devices async"); - deleteDevicesAsync("/api/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); log.debug("done"); } @@ -501,7 +499,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2); - deleteDevicesAsync("/api/device/", loadedDevicesTitle1).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/tenant/devices?", @@ -509,7 +507,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - deleteDevicesAsync("/api/device/", loadedDevicesTitle2).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/tenant/devices?", @@ -518,16 +516,6 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertEquals(0, pageData.getData().size()); } - ListenableFuture> deleteDevicesAsync(String urlTemplate, List loadedDevicesTitle1) { - List> futures = new ArrayList<>(loadedDevicesTitle1.size()); - for (Device device : loadedDevicesTitle1) { - futures.add(executor.submit(() -> - doDelete(urlTemplate + device.getId().getId()) - .andExpect(status().isOk()))); - } - return Futures.allAsList(futures); - } - @Test public void testFindTenantDevicesByType() throws Exception { String title1 = "Device title 1"; @@ -593,7 +581,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2); - deleteDevicesAsync("/api/device/", loadedDevicesType1).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", @@ -601,7 +589,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - deleteDevicesAsync("/api/device/", loadedDevicesType2).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/tenant/devices?type={type}&", @@ -644,7 +632,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devices).containsExactlyInAnyOrderElementsOf(loadedDevices); log.debug("delete devices async"); - deleteDevicesAsync("/api/customer/device/", loadedDevices).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/device/", loadedDevices, executor).get(TIMEOUT, TimeUnit.SECONDS); log.debug("done"); } @@ -713,7 +701,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devicesTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesTitle2); - deleteDevicesAsync("/api/customer/device/", loadedDevicesTitle1).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title1); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?", @@ -721,7 +709,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - deleteDevicesAsync("/api/customer/device/", loadedDevicesTitle2).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/device/", loadedDevicesTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4, 0, title2); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?", @@ -803,7 +791,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { assertThat(devicesType2).as(title2).containsExactlyInAnyOrderElementsOf(loadedDevicesType2); - deleteDevicesAsync("/api/customer/device/", loadedDevicesType1).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/device/", loadedDevicesType1, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&", @@ -811,7 +799,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest { Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(0, pageData.getData().size()); - deleteDevicesAsync("/api/customer/device/", loadedDevicesType2).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/customer/device/", loadedDevicesType2, executor).get(TIMEOUT, TimeUnit.SECONDS); pageLink = new PageLink(4); pageData = doGetTypedWithPageLink("/api/customer/" + customerId.getId() + "/devices?type={type}&", diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java index 1538dde38f..1ae3f497e9 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseTenantControllerTest.java @@ -26,6 +26,7 @@ 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; @@ -36,19 +37,23 @@ import org.thingsboard.server.common.data.page.PageLink; import java.util.ArrayList; 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 { - static final TypeReference> PAGE_DATA_TENANT_TYPE_REF = new TypeReference<>(){}; - static final TypeReference> PAGE_DATA_TENANT_INFO_TYPE_REF = new TypeReference<>(){}; - static final int TIMEOUT = 30; + static final TypeReference> PAGE_DATA_TENANT_TYPE_REF = new TypeReference<>() { + }; + static final TypeReference> PAGE_DATA_TENANT_INFO_TYPE_REF = new TypeReference<>() { + }; - List> deleteFutures = new ArrayList<>(); ListeningExecutorService executor; @Before @@ -176,14 +181,9 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants); - for (Tenant tenant : loadedTenants) { - if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/tenant/" + tenant.getId().getId().toString()) - .andExpect(status().isOk()))); - } - } - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + 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?", PAGE_DATA_TENANT_TYPE_REF, pageLink); @@ -256,14 +256,8 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { assertThat(tenantsTitle2).as(title2).containsExactlyInAnyOrderElementsOf(loadedTenantsTitle2); log.debug("asserted"); - deleteFutures.clear(); - for (Tenant tenant : loadedTenantsTitle1) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/tenant/" + tenant.getId().getId().toString()) - .andExpect(status().isOk()))); - } - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/tenant/", loadedTenantsTitle1, executor).get(TIMEOUT, TimeUnit.SECONDS); log.debug("deleted '{}', size {}", title1, loadedTenantsTitle1.size()); pageLink = new PageLink(4, 0, title1); @@ -273,14 +267,7 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { log.debug("tried to search another '{}', step 4", title1); - deleteFutures.clear(); - for (Tenant tenant : loadedTenantsTitle2) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/tenant/" + tenant.getId().getId().toString()) - .andExpect(status().isOk()))); - } - - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + deleteEntitiesAsync("/api/tenant/", loadedTenantsTitle2, executor).get(TIMEOUT, TimeUnit.SECONDS); log.debug("deleted '{}', size {}", title2, loadedTenantsTitle2.size()); pageLink = new PageLink(4, 0, title2); @@ -321,18 +308,24 @@ public abstract class BaseTenantControllerTest extends AbstractControllerTest { assertThat(tenants).containsExactlyInAnyOrderElementsOf(loadedTenants); - for (TenantInfo tenant : loadedTenants) { - if (!tenant.getTitle().equals(TEST_TENANT_NAME)) { - deleteFutures.add(executor.submit(() -> - doDelete("/api/tenant/" + tenant.getId().getId().toString()) - .andExpect(status().isOk()))); - } - } - Futures.allAsList(deleteFutures).get(TIMEOUT, TimeUnit.SECONDS); + 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?", PAGE_DATA_TENANT_INFO_TYPE_REF, pageLink); Assert.assertFalse(pageData.hasNext()); Assert.assertEquals(1, pageData.getData().size()); } + + ListenableFuture> deleteTenantsAsync(String urlTemplate, List tenants) { + List> 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); + } + } From 74a78ddc612ee36d253edf052e3db4eb50a4edf8 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 18:55:49 +0300 Subject: [PATCH 21/28] test entity view refactored from sleep to WSW --- .../BaseEntityViewControllerTest.java | 121 ++++++++++-------- .../controller/TbTestWebSocketClient.java | 8 +- 2 files changed, 77 insertions(+), 52 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index fb6ded866a..0887b58ce4 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -48,16 +48,22 @@ 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.query.EntityViewTypeFilter; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.queue.memory.InMemoryStorage; +import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.util.ArrayList; 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; @@ -71,12 +77,14 @@ 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 int TIMEOUT = 30; static final TypeReference> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() { }; + static final TypeReference> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() { + }; private Tenant savedTenant; private User tenantAdmin; @@ -86,13 +94,10 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes List> deleteFutures = new ArrayList<>(); ListeningExecutorService executor; - @Autowired - InMemoryStorage inMemoryStorage; - @Before public void beforeTest() throws Exception { - log.warn("beforeTest"); - executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(16, getClass())); + log.debug("beforeTest"); + executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); loginSysAdmin(); @@ -108,7 +113,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); Device device = new Device(); - device.setName("Test device"); + device.setName("Test device 4view"); device.setType("default"); testDevice = doPost("/api/device", device, Device.class); @@ -128,7 +133,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) .andExpect(status().isOk()); - log.warn("after test"); + log.debug("after test"); } @Test @@ -304,9 +309,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes } Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); - PageData pageData = doGetTypedWithPageLink(urlTemplate, - new TypeReference>() { - }, new PageLink(4, 0, name1)); + PageData pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, + new PageLink(4, 0, name1)); Assert.assertFalse(pageData.hasNext()); assertEquals(0, pageData.getData().size()); @@ -317,8 +321,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes } Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); - pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() { - }, + pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_TYPE_REF, new PageLink(4, 0, name2)); Assert.assertFalse(pageData.hasNext()); assertEquals(0, pageData.getData().size()); @@ -358,9 +361,8 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes } Futures.allAsList(deleteFutures).get(TIMEOUT, SECONDS); - PageData pageData = doGetTypedWithPageLink("/api/tenant/entityViews?", - new TypeReference>() { - }, new PageLink(4, 0, name1)); + PageData 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()); @@ -382,11 +384,10 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes Set expectedActualAttributesSet = Set.of("caKey1", "caKey2", "caKey3", "caKey4"); Set actualAttributesSet = getAttributesByKeys("{\"caKey1\":\"value1\", \"caKey2\":true, \"caKey3\":42.0, \"caKey4\":73}", expectedActualAttributesSet); - - log.warn("got correct actualAttributesSet, saving new entity view..."); + log.debug("got correct actualAttributesSet, saving new entity view..."); EntityView savedView = getNewSavedEntityView("Test entity view"); - log.warn("fetching entity view telemetry..."); + log.debug("fetching entity view telemetry..."); List> values = await("telemetry/ENTITY_VIEW") .atMost(TIMEOUT, SECONDS) .until(() -> doGetAsyncTyped("/api/plugins/telemetry/ENTITY_VIEW/" + savedView.getId().getId().toString() + @@ -394,7 +395,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes }), x -> x.size() >= expectedActualAttributesSet.size()); - log.warn("asserting..."); + log.debug("asserting..."); assertEquals("value1", getValue(values, "caKey1")); assertEquals(true, getValue(values, "caKey2")); assertEquals(42.0, getValue(values, "caKey3")); @@ -430,18 +431,40 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @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 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 keys = getTelemetryKeys("DEVICE", deviceId); - Thread.sleep(1000); EntityView view = createEntityView("Test entity view", startTimeMs, endTimeMs); EntityView savedView = doPost("/api/entityView", view, EntityView.class); @@ -458,15 +481,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()); @@ -478,7 +494,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes MqttMessage message = new MqttMessage(); message.setPayload(strKvs.getBytes()); IMqttDeliveryToken token = client.publish("v1/devices/me/telemetry", message); - await("mqtt ack").pollInterval(10, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); + await("mqtt ack").pollInterval(5, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); client.disconnect(); } @@ -504,7 +520,15 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes } private Set getAttributesByKeys(String stringKV, Set expectedKeySet) throws Exception { + DeviceTypeFilter dtf = new DeviceTypeFilter(testDevice.getType(), testDevice.getName()); + List 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()); @@ -512,6 +536,7 @@ 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()); @@ -519,21 +544,16 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes options.setUserName(accessToken); client.connect(options); awaitConnected(client, SECONDS.toMillis(30)); - + log.debug("mqtt connected..."); MqttMessage message = new MqttMessage(); message.setPayload((stringKV).getBytes()); + getWsClient().registerWaitForUpdate(); IMqttDeliveryToken token = client.publish("v1/devices/me/attributes", message); - log.warn("token.message {}", token.getMessage()); - await("mqtt ack").pollInterval(10, MILLISECONDS).atMost(TIMEOUT, SECONDS).until(() -> token.getMessage() == null); - log.warn("token.message delivered {}", token.getMessage()); - client.disconnect(); - - return await().pollInterval(20, MILLISECONDS).atMost(TIMEOUT, SECONDS) - .until(() -> { - inMemoryStorage.printStats(); - return getAttributeKeys("DEVICE", viewDeviceId); - }, - keys -> keys.containsAll(expectedKeySet)); + 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> values, String stringValue) { @@ -624,8 +644,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes List loadedItems = new ArrayList<>(); PageData pageData; do { - pageData = doGetTypedWithPageLink(urlTemplate, new TypeReference>() { - }, pageLink); + pageData = doGetTypedWithPageLink(urlTemplate, PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF, pageLink); loadedItems.addAll(pageData.getData()); if (pageData.hasNext()) { pageLink = pageLink.nextPageLink(); diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java index 013547aa24..6af6176b06 100644 --- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java +++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java @@ -150,7 +150,13 @@ public class TbTestWebSocketClient extends WebSocketClient { } public EntityDataUpdate subscribeTsUpdate(List keys, long startTs, long timeWindow) { - return subscribeTsUpdate(keys, startTs, timeWindow, null); + return subscribeTsUpdate(keys, startTs, timeWindow, (EntityDataQuery) null); + } + + public EntityDataUpdate subscribeTsUpdate(List 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 keys, long startTs, long timeWindow, EntityDataQuery edq) { From eed9a8723e3be28d674fe8a519a8f3f6b7a48db6 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 18:56:06 +0300 Subject: [PATCH 22/28] test properties: Low latency settings to perform tests as fast as possible --- .../resources/application-test.properties | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index d803e67012..c462406bf3 100644 --- a/application/src/test/resources/application-test.properties +++ b/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.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 +queue.transport.poll_interval=5 From fce7284f6cd731f47ea7342e3bb0e493d11707f6 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 21 Apr 2022 22:42:10 +0300 Subject: [PATCH 23/28] rollback test #queue.rule-engine properties as NoSqlTest failed --- .../resources/application-test.properties | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index c462406bf3..cdd3ab8f6a 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -39,19 +39,19 @@ queue.transport_api.response_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 +#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 queue.transport.poll_interval=5 From 5f54887be876fe6dee68ccfc95b02e7bcacdb2be Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Apr 2022 11:41:59 +0300 Subject: [PATCH 24/28] entity view test performance improvements by Andrew's patch --- .../server/controller/AbstractWebTest.java | 2 +- .../BaseEntityViewControllerTest.java | 40 +++++-------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java index 6f88787c52..7e3d49293a 100644 --- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java @@ -111,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"; diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java index 0887b58ce4..b196dfb23d 100644 --- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java @@ -32,7 +32,6 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.TestPropertySource; import org.springframework.test.web.servlet.ResultActions; import org.thingsboard.common.util.ThingsBoardExecutors; @@ -51,11 +50,9 @@ 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.query.EntityViewTypeFilter; import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.dao.model.ModelConstants; -import org.thingsboard.server.service.telemetry.TelemetrySubscriptionService; import java.util.ArrayList; import java.util.HashSet; @@ -86,8 +83,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes static final TypeReference> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() { }; - private Tenant savedTenant; - private User tenantAdmin; private Device testDevice; private TelemetryEntityView telemetry; @@ -99,18 +94,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes log.debug("beforeTest"); executor = MoreExecutors.listeningDecorator(ThingsBoardExecutors.newWorkStealingPool(8, getClass())); - loginSysAdmin(); - - savedTenant = doPost("/api/tenant", getNewTenant("My 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"); - tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); + loginTenantAdmin(); Device device = new Device(); device.setName("Test device 4view"); @@ -128,12 +112,6 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @After public void afterTest() throws Exception { executor.shutdownNow(); - - loginSysAdmin(); - - doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) - .andExpect(status().isOk()); - log.debug("after test"); } @Test @@ -151,7 +129,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(savedView.getName(), savedView.getName()); @@ -252,7 +230,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"); @@ -344,12 +322,12 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes @Test public void testGetTenantEntityViewsByName() throws Exception { String name1 = "Entity view name1"; - List namesOfView1 = Futures.allAsList(fillListOf(143, name1)).get(TIMEOUT, SECONDS); - List loadedNamesOfView1 = loadListOf(new PageLink(15, 0, name1), "/api/tenant/entityViews?"); + List namesOfView1 = Futures.allAsList(fillListOf(17, name1)).get(TIMEOUT, SECONDS); + List loadedNamesOfView1 = loadListOf(new PageLink(5, 0, name1), "/api/tenant/entityViews?"); assertThat(namesOfView1).as(name1).containsExactlyInAnyOrderElementsOf(loadedNamesOfView1); String name2 = "Entity view name2"; - List namesOfView2 = Futures.allAsList(fillListOf(75, name2)).get(TIMEOUT, SECONDS); + List namesOfView2 = Futures.allAsList(fillListOf(15, name2)).get(TIMEOUT, SECONDS); ; List loadedNamesOfView2 = loadListOf(new PageLink(4, 0, name2), "/api/tenant/entityViews?"); assertThat(namesOfView2).as(name2).containsExactlyInAnyOrderElementsOf(loadedNamesOfView2); @@ -414,7 +392,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes 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); @@ -434,7 +412,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes DeviceTypeFilter dtf = new DeviceTypeFilter(testDevice.getType(), testDevice.getName()); List tsKeys = List.of("tsKey1", "tsKey2", "tsKey3"); - DeviceCredentials deviceCredentials =doGet("/api/device/" + testDevice.getId().getId() + "/credentials", DeviceCredentials.class); + DeviceCredentials deviceCredentials = doGet("/api/device/" + testDevice.getId().getId() + "/credentials", DeviceCredentials.class); assertEquals(testDevice.getId(), deviceCredentials.getDeviceId()); String accessToken = deviceCredentials.getCredentialsId(); assertNotNull(accessToken); @@ -575,7 +553,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes 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); From e760d45bd653c4acddbec521a432ab447964235b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Apr 2022 11:49:30 +0300 Subject: [PATCH 25/28] application-test.properties low latency to speed up tests --- .../resources/application-test.properties | 32 ++++++------- .../resources/application-test.properties | 45 ++++++++++++++++++- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/application/src/test/resources/application-test.properties b/application/src/test/resources/application-test.properties index cdd3ab8f6a..518c9b42d3 100644 --- a/application/src/test/resources/application-test.properties +++ b/application/src/test/resources/application-test.properties @@ -36,22 +36,22 @@ 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 -queue.transport.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 diff --git a/dao/src/test/resources/application-test.properties b/dao/src/test/resources/application-test.properties index a257c8fbce..5d02ae84d5 100644 --- a/dao/src/test/resources/application-test.properties +++ b/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 From fcb0cd93e388df6f1e688bd4cdb478e0fe2a4fc1 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Apr 2022 14:29:39 +0300 Subject: [PATCH 26/28] nosql-test.properties added Main queue to handle the init issue --- dao/src/test/resources/nosql-test.properties | 11 +++++++++++ dao/src/test/resources/sql-test.properties | 7 +++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index b81f5db8ac..293ec2235d 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/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 diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index d2add71eea..52033b3b27 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/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 From ca8df0130f515b72822c885fd288e910fdbca99e Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Tue, 26 Apr 2022 15:26:31 +0300 Subject: [PATCH 27/28] TbLogNode.java standard implementation without calling JS when JsScript equals default config --- .../rule/engine/action/TbLogNode.java | 23 +++++- .../engine/action/TbLogNodeConfiguration.java | 2 +- .../rule/engine/action/TbLogNodeTest.java | 80 +++++++++++++++++++ 3 files changed, 101 insertions(+), 4 deletions(-) create mode 100644 rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java index e3802bcdec..431d63a674 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNode.java +++ b/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() { @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) { diff --git a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNodeConfiguration.java b/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNodeConfiguration.java index a176046c19..453ade72a7 100644 --- a/rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/action/TbLogNodeConfiguration.java +++ b/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; } } diff --git a/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java b/rule-engine/rule-engine-components/src/test/java/org/thingsboard/rule/engine/action/TbLogNodeTest.java new file mode 100644 index 0000000000..fd1a5af6c1 --- /dev/null +++ b/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" + + "{}"); + } + +} From 00b6c8095cfaa1c04a1dcb480f3e5b000fa0bd76 Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 28 Apr 2022 14:11:19 +0300 Subject: [PATCH 28/28] logback added to the msa/black-bo-tests to reduce logger overhead --- .../src/test/resources/logback.xml | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 msa/black-box-tests/src/test/resources/logback.xml diff --git a/msa/black-box-tests/src/test/resources/logback.xml b/msa/black-box-tests/src/test/resources/logback.xml new file mode 100644 index 0000000000..cdf87aab92 --- /dev/null +++ b/msa/black-box-tests/src/test/resources/logback.xml @@ -0,0 +1,32 @@ + + + + + + + + %d{ISO8601} [%thread] %-5level %logger{36} - %msg%n + + + + + + + +