Browse Source

Merge pull request #14884 from thingsboard/lts-4.3

LTS to RC
pull/14885/head
Viacheslav Klimov 8 months ago
committed by GitHub
parent
commit
33dcbceb85
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 31
      TEST_FAST.md
  2. 12
      application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
  3. 97
      application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java
  4. 14
      application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java
  5. 14
      application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java
  6. 10
      application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java
  7. 19
      application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java
  8. 28
      application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java
  9. 5
      application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
  10. 14
      application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
  11. 3
      application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java
  12. 37
      application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java
  13. 14
      application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java
  14. 70
      application/src/test/java/org/thingsboard/server/transport/lwm2m/AbstractLwM2MIntegrationTest.java
  15. 2
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java
  16. 41
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java
  17. 1
      application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java
  18. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java
  19. 4
      application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java
  20. 13
      application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
  21. 10
      application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java
  22. 6
      application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java
  23. 6
      application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java
  24. 42
      application/src/test/java/org/thingsboard/server/utils/PortFinder.java
  25. 1
      application/src/test/resources/application-test.properties
  26. 6
      application/src/test/resources/logback-test.xml
  27. 3
      common/cache/src/test/resources/logback-test.xml
  28. 3
      common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java
  29. 7
      dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java
  30. 16
      dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java
  31. 1
      dao/src/test/resources/application-test.properties
  32. 3
      dao/src/test/resources/logback-test.xml
  33. 4
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java
  34. 42
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java
  35. 8
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java
  36. 21
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java
  37. 3
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java
  38. 21
      pom.xml

31
TEST_FAST.md

@ -0,0 +1,31 @@
## Running tests in parallel with a reasonable memory usage
```bash
export MAVEN_OPTS="-Xmx1024m"
export NODE_OPTIONS="--max_old_space_size=4096"
export SUREFIRE_JAVA_OPTS="-Xmx1200m -Xss256k -XX:+ExitOnOutOfMemoryError"
mvn clean install -T6 -DskipTests
mvn test -pl='!application,!dao,!ui-ngx,!msa/js-executor,!msa/web-ui' -T4
mvn test -pl dao -Dparallel=packages -DforkCount=4
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.controller.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.edge.**' -DforkCount=4 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.service.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.mqtt.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.coap.**' -DforkCount=6 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='org.thingsboard.server.transport.lwm2m.**' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
mvn test -pl application -Dsurefire.excludes='**/nosql/*Test.java' -Dtest='**/*TestSuite.java' -DforkCount=4 -Dparallel=classes -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
#the rest of application tests
mvn test -pl application -Dtest='
!**/nosql/*Test.java,
!org.thingsboard.server.controller.**,
!org.thingsboard.server.edge.**,
!org.thingsboard.server.service.**,
!org.thingsboard.server.transport.mqtt.**,
!org.thingsboard.server.transport.coap.**,
!org.thingsboard.server.transport.lwm2m.**
' -DforkCount=6 -Dparallel=packages -Dsurefire.rerunFailingTestsCount=2 -Dsurefire.failOnFlakeCount=5
```

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

@ -60,6 +60,7 @@ import org.springframework.test.web.servlet.ResultActions;
import org.springframework.test.web.servlet.ResultMatcher; import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext; import org.springframework.web.context.WebApplicationContext;
@ -349,7 +350,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
if (this.mockMvc == null) { if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(webApplicationContext) this.mockMvc = webAppContextSetup(webApplicationContext)
.apply(springSecurity()).build(); .apply(springSecurity())
// conditional printing of non 2xx responses
.alwaysDo(result -> {
if (result.getResponse().getStatus() >= 400) {
MockMvcResultHandlers.log().handle(result);
}
})
.build();
} }
loginSysAdmin(); loginSysAdmin();
@ -423,7 +431,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
jdbcTemplate.execute("TRUNCATE TABLE notification"); jdbcTemplate.execute("TRUNCATE TABLE notification");
log.info("Executed web test teardown"); log.debug("Executed web test teardown");
} }
private void verifyNoTenantsLeft() throws Exception { private void verifyNoTenantsLeft() throws Exception {

97
application/src/test/java/org/thingsboard/server/controller/EdgeControllerTest.java

@ -36,7 +36,10 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
@ -99,6 +102,7 @@ import org.thingsboard.server.service.edge.instructions.EdgeUpgradeInstructionsS
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -121,7 +125,12 @@ import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT
public class EdgeControllerTest extends AbstractControllerTest { public class EdgeControllerTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost"; public static final String EDGE_HOST = "localhost";
public static final int EDGE_PORT = 7070; public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.debug("edges.rpc.port = {}", EDGE_PORT);
registry.add("edges.rpc.port", () -> EDGE_PORT);
}
private IdComparator<Edge> idComparator = new IdComparator<>(); private IdComparator<Edge> idComparator = new IdComparator<>();
@ -916,11 +925,11 @@ public class EdgeControllerTest extends AbstractControllerTest {
verifyFetchersMsgs(edgeImitator, savedDevice); verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs // verify queue msgs
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
printQueueMsgsIfNotEmpty(edgeImitator); printQueueMsgsIfNotEmpty(edgeImitator);
// 17 connect messages // 17 connect messages
@ -1000,32 +1009,32 @@ public class EdgeControllerTest extends AbstractControllerTest {
} }
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) { private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main")); Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain")); Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator))); Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "general"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "mail"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "connectivity"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt")); Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "jwt"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId)); Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), currentUserId));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN)); Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public")); Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1")); Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId())); Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default")); Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1")); Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId)); Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgsDeque(), tenantId));
Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId)); Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgsDeque(), tenantProfileId));
Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs())); Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgsDeque()));
} }
private boolean popQueueMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popQueueMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof QueueUpdateMsg queueUpdateMsg) { if (message instanceof QueueUpdateMsg queueUpdateMsg) {
Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true); Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true);
@ -1039,7 +1048,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popRuleChainMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popRuleChainMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) { if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) {
RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true); RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
@ -1055,7 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popRuleChainMetadataMsg(List<AbstractMessage> messages, UpdateMsgType msgType, RuleChainId ruleChainId) { private boolean popRuleChainMetadataMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, RuleChainId ruleChainId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) { if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) {
RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true); RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true);
@ -1070,7 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAdminSettingsMsg(List<AbstractMessage> messages, String key) { private boolean popAdminSettingsMsg(Deque<AbstractMessage> messages, String key) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) { if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true); AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true);
@ -1084,7 +1093,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popDeviceProfileMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) { if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true); DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true);
@ -1099,7 +1108,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popDeviceMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceUpdateMsg deviceUpdateMsg) { if (message instanceof DeviceUpdateMsg deviceUpdateMsg) {
Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true); Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
@ -1114,7 +1123,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popDeviceCredentialsMsg(List<AbstractMessage> messages, DeviceId deviceId) { private boolean popDeviceCredentialsMsg(Deque<AbstractMessage> messages, DeviceId deviceId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) { if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true); DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true);
@ -1128,7 +1137,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAssetProfileMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popAssetProfileMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) { if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {
AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true); AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true);
@ -1143,7 +1152,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popAssetMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String name) { private boolean popAssetMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String name) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof AssetUpdateMsg assetUpdateMsg) { if (message instanceof AssetUpdateMsg assetUpdateMsg) {
Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true); Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true);
@ -1158,7 +1167,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popUserCredentialsMsg(List<AbstractMessage> messages, UserId userId) { private boolean popUserCredentialsMsg(Deque<AbstractMessage> messages, UserId userId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) { if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true); UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
@ -1172,7 +1181,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popUserMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) { private boolean popUserMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String email, Authority authority) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof UserUpdateMsg userUpdateMsg) { if (message instanceof UserUpdateMsg userUpdateMsg) {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
@ -1188,7 +1197,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popCustomerMsg(List<AbstractMessage> messages, UpdateMsgType msgType, String title) { private boolean popCustomerMsg(Deque<AbstractMessage> messages, UpdateMsgType msgType, String title) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof CustomerUpdateMsg customerUpdateMsg) { if (message instanceof CustomerUpdateMsg customerUpdateMsg) {
Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true); Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true);
@ -1203,7 +1212,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popTenantMsg(List<AbstractMessage> messages, TenantId tenantId1) { private boolean popTenantMsg(Deque<AbstractMessage> messages, TenantId tenantId1) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg tenantUpdateMsg) { if (message instanceof TenantUpdateMsg tenantUpdateMsg) {
Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true); Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true);
@ -1218,7 +1227,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popTenantProfileMsg(List<AbstractMessage> messages, TenantProfileId tenantProfileId) { private boolean popTenantProfileMsg(Deque<AbstractMessage> messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) { if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) {
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true); TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
@ -1233,7 +1242,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false; return false;
} }
private boolean popSyncCompletedMsg(List<AbstractMessage> messages) { private boolean popSyncCompletedMsg(Deque<AbstractMessage> messages) {
for (AbstractMessage message : messages) { for (AbstractMessage message : messages) {
if (message instanceof SyncCompletedMsg) { if (message instanceof SyncCompletedMsg) {
messages.remove(message); messages.remove(message);

14
application/src/test/java/org/thingsboard/server/controller/EntityViewControllerTest.java

@ -38,6 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.web.servlet.ResultActions; import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -88,6 +90,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID; import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_URL;
@TestPropertySource(properties = { @TestPropertySource(properties = {
"transport.mqtt.enabled=true", "transport.mqtt.enabled=true",
@ -97,6 +101,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ContextConfiguration(classes = {EntityViewControllerTest.Config.class}) @ContextConfiguration(classes = {EntityViewControllerTest.Config.class})
@DaoSqlTest @DaoSqlTest
public class EntityViewControllerTest extends AbstractControllerTest { public class EntityViewControllerTest extends AbstractControllerTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
static final TypeReference<PageData<EntityView>> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<EntityView>> PAGE_DATA_ENTITY_VIEW_TYPE_REF = new TypeReference<>() {
}; };
static final TypeReference<PageData<EntityViewInfo>> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() { static final TypeReference<PageData<EntityViewInfo>> PAGE_DATA_ENTITY_VIEW_INFO_TYPE_REF = new TypeReference<>() {
@ -683,7 +693,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
String viewDeviceId = testDevice.getId().getId().toString(); String viewDeviceId = testDevice.getId().getId().toString();
String clientId = MqttAsyncClient.generateClientId(); String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions(); MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken); options.setUserName(accessToken);
@ -734,7 +744,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
assertNotNull(accessToken); assertNotNull(accessToken);
String clientId = MqttAsyncClient.generateClientId(); String clientId = MqttAsyncClient.generateClientId();
MqttAsyncClient client = new MqttAsyncClient("tcp://localhost:1883", clientId, new MemoryPersistence()); MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence());
MqttConnectOptions options = new MqttConnectOptions(); MqttConnectOptions options = new MqttConnectOptions();
options.setUserName(accessToken); options.setUserName(accessToken);

14
application/src/test/java/org/thingsboard/server/edge/AbstractEdgeTest.java

@ -27,7 +27,10 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings; import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
@ -118,6 +121,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
}) })
@Slf4j @Slf4j
abstract public class AbstractEdgeTest extends AbstractControllerTest { abstract public class AbstractEdgeTest extends AbstractControllerTest {
public static final String EDGE_HOST = "localhost";
public static final int EDGE_PORT = TestSocketUtils.findAvailableTcpPort();
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.debug("edges.rpc.port = {}", EDGE_PORT);
registry.add("edges.rpc.port", () -> EDGE_PORT);
}
public static final Integer CONNECT_MESSAGE_COUNT = 17; public static final Integer CONNECT_MESSAGE_COUNT = 17;
public static final Integer INSTALLATION_MESSAGE_COUNT = 8; public static final Integer INSTALLATION_MESSAGE_COUNT = 8;
public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT; public static final Integer SYNC_MESSAGE_COUNT = CONNECT_MESSAGE_COUNT + INSTALLATION_MESSAGE_COUNT;
@ -144,7 +156,7 @@ abstract public class AbstractEdgeTest extends AbstractControllerTest {
//8 installation messages //8 installation messages
installation(); installation();
edgeImitator = new EdgeImitator("localhost", 7070, edge.getRoutingKey(), edge.getSecret()); edgeImitator = new EdgeImitator(EDGE_HOST, EDGE_PORT, edge.getRoutingKey(), edge.getSecret());
// 17 connect messages + 8 installation messages // 17 connect messages + 8 installation messages
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT); edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT);
edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class); edgeImitator.ignoreType(OAuth2ClientUpdateMsg.class);

10
application/src/test/java/org/thingsboard/server/edge/DeviceEdgeTest.java

@ -22,10 +22,13 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.protobuf.AbstractMessage; import com.google.protobuf.AbstractMessage;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.adaptor.JsonConverter; import org.thingsboard.server.common.adaptor.JsonConverter;
@ -84,12 +87,19 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE; import static org.thingsboard.server.gen.edge.v1.UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@TestPropertySource(properties = { @TestPropertySource(properties = {
"transport.mqtt.enabled=true" "transport.mqtt.enabled=true"
}) })
@Slf4j
@DaoSqlTest @DaoSqlTest
public class DeviceEdgeTest extends AbstractEdgeTest { public class DeviceEdgeTest extends AbstractEdgeTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
private static final String DEFAULT_DEVICE_TYPE = "default"; private static final String DEFAULT_DEVICE_TYPE = "default";

19
application/src/test/java/org/thingsboard/server/edge/imitator/EdgeImitator.java

@ -69,10 +69,12 @@ import org.thingsboard.server.gen.edge.v1.WidgetsBundleUpdateMsg;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -103,8 +105,17 @@ public class EdgeImitator {
@Getter @Getter
private EdgeConfiguration configuration; private EdgeConfiguration configuration;
@Getter private final ConcurrentLinkedDeque<AbstractMessage> downlinkMsgs;
private final List<AbstractMessage> downlinkMsgs;
//Returns collection copy as Unmodifiable list
//This addressing the issue: DeviceEdgeTest>AbstractEdgeTest.setupEdgeTest:212->AbstractEdgeTest.verifyEdgeConnectionAndInitialData:306->AbstractEdgeTest.validateMsgsCnt:387 » ConcurrentModification
public List<AbstractMessage> getDownlinkMsgs() {
return downlinkMsgs.stream().toList();
}
public Deque<AbstractMessage> getDownlinkMsgsDeque() {
return downlinkMsgs;
}
@Getter @Getter
private UplinkResponseMsg latestResponseMsg; private UplinkResponseMsg latestResponseMsg;
@ -113,7 +124,7 @@ public class EdgeImitator {
edgeRpcClient = new EdgeGrpcClient(); edgeRpcClient = new EdgeGrpcClient();
messagesLatch = new CountDownLatch(0); messagesLatch = new CountDownLatch(0);
responsesLatch = new CountDownLatch(0); responsesLatch = new CountDownLatch(0);
downlinkMsgs = new ArrayList<>(); downlinkMsgs = new ConcurrentLinkedDeque<>();
ignoredTypes = new ArrayList<>(); ignoredTypes = new ArrayList<>();
this.routingKey = routingKey; this.routingKey = routingKey;
this.routingSecret = routingSecret; this.routingSecret = routingSecret;
@ -458,7 +469,7 @@ public class EdgeImitator {
} }
public AbstractMessage getLatestMessage() { public AbstractMessage getLatestMessage() {
return downlinkMsgs.get(downlinkMsgs.size() - 1); return downlinkMsgs.peekLast();
} }
public void ignoreType(Class<? extends AbstractMessage> type) { public void ignoreType(Class<? extends AbstractMessage> type) {

28
application/src/test/java/org/thingsboard/server/service/notification/NotificationRuleApiTest.java

@ -238,12 +238,12 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE); .set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
await().atMost(10, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
long ts = System.currentTimeMillis(); long ts = System.currentTimeMillis();
await().atMost(15, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null)); .until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null));
clients.forEach((expectedDelay, wsClient) -> { clients.forEach((expectedDelay, wsClient) -> {
Notification notification = wsClient.getLastDataUpdate().getUpdate(); Notification notification = wsClient.getLastDataUpdate().getUpdate();
@ -291,8 +291,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
alarm.setOriginator(device.getId()); alarm.setOriginator(device.getId());
alarm = doPost("/api/alarm", alarm, Alarm.class); alarm = doPost("/api/alarm", alarm, Alarm.class);
await().atMost(15, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.pollDelay(2, TimeUnit.SECONDS) .pollDelay(1, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
List<Notification> notifications = getMyNotifications(false, 10); List<Notification> notifications = getMyNotifications(false, 10);
assertThat(notifications).singleElement().matches(notification -> { assertThat(notifications).singleElement().matches(notification -> {
@ -345,7 +345,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE); .set("createAlarm", BooleanNode.TRUE);
postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString()); postAttributes(device.getId(), AttributeScope.SERVER_SCOPE, attr.toString());
await().atMost(10, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null); .until(() -> alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType) != null);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType); Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
getWsClient().waitForUpdate(true); getWsClient().waitForUpdate(true);
@ -355,14 +355,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class)) assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class))
.extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId()); .extracting(AlarmNotificationInfo::getAlarmId).isEqualTo(alarm.getUuidId());
await().atMost(10, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size()); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> findNotificationRequests(EntityType.ALARM).getTotalElements() == escalationTable.size());
NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream() NotificationRequestInfo scheduledNotificationRequest = findNotificationRequests(EntityType.ALARM).getData().stream()
.filter(NotificationRequest::isScheduled) .filter(NotificationRequest::isScheduled)
.findFirst().orElse(null); .findFirst().orElse(null);
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo()); assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null); alarmSubscriptionService.clearAlarm(tenantId, alarm.getId(), System.currentTimeMillis(), null);
await().atMost(5, TimeUnit.SECONDS).untilAsserted(() -> { await().atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> {
assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty(); assertThat(findNotificationRequests(EntityType.ALARM).getData()).filteredOn(NotificationRequest::isScheduled).isEmpty();
}); });
} }
@ -480,7 +480,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
} }
loginTenantAdmin(); loginTenantAdmin();
List<Notification> notifications = await().atMost(15, TimeUnit.SECONDS) List<Notification> notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream() .until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 3); .collect(Collectors.toList()), list -> list.size() == 3);
@ -500,7 +500,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
}); });
loginSysAdmin(); loginSysAdmin();
notifications = await().atMost(15, TimeUnit.SECONDS) notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream() .until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS) .filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 1); .collect(Collectors.toList()), list -> list.size() == 1);
@ -721,7 +721,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.updateInfo(new UpdateMessage(true, "CHANGED", "test", .updateInfo(new UpdateMessage(true, "CHANGED", "test",
"test", "test", "test")) "test", "test", "test"))
.build()); .build());
await().atMost(5, TimeUnit.SECONDS) await().atMost(TIMEOUT, TimeUnit.SECONDS)
.untilAsserted(() -> { .untilAsserted(() -> {
assertThat(getMyNotifications(false, 100)).size().isEqualTo(2); assertThat(getMyNotifications(false, 100)).size().isEqualTo(2);
}); });
@ -809,7 +809,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService); method.invoke(systemInfoService);
} }
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -836,7 +836,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.build()); .build());
TimeUnit.MILLISECONDS.sleep(300); TimeUnit.MILLISECONDS.sleep(300);
} }
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -848,7 +848,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.serviceType("serviceType") .serviceType("serviceType")
.serviceId("serviceId") .serviceId("serviceId")
.build()); .build());
await("").atMost(5, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne()); await("").atMost(TIMEOUT, TimeUnit.SECONDS).untilAsserted(() -> assertThat(getMyNotifications(false, 100)).size().isOne());
} }
@Test @Test
@ -887,7 +887,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
method.invoke(systemInfoService); method.invoke(systemInfoService);
await().atMost(10, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1); await().atMost(TIMEOUT, TimeUnit.SECONDS).until(() -> getMyNotifications(false, 100).size() == 1);
Notification notification = getMyNotifications(false, 100).get(0); Notification notification = getMyNotifications(false, 100).get(0);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage"); assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage"); assertThat(notification.getText()).isEqualTo("RAM shortage");

5
application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java

@ -28,10 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@Slf4j @Slf4j
public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest { public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest {
protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; public static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
protected static final String MQTT_URL = "tcp://localhost:1883";
protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
protected static final AtomicInteger atomicInteger = new AtomicInteger(2); protected static final AtomicInteger atomicInteger = new AtomicInteger(2);

14
application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java

@ -16,7 +16,10 @@
package org.thingsboard.server.transport.coap; package org.thingsboard.server.transport.coap;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -40,6 +43,7 @@ import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadCo
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration; import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.transport.AbstractTransportIntegrationTest; import org.thingsboard.server.transport.AbstractTransportIntegrationTest;
import org.thingsboard.server.utils.PortFinder;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -52,6 +56,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j @Slf4j
public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest { public abstract class AbstractCoapIntegrationTest extends AbstractTransportIntegrationTest {
public static final String COAP_HOST = "localhost";
public static final int COAP_PORT = PortFinder.findAvailableUdpPort();
public static final String COAP_BASE_URL = "coap://" + COAP_HOST + ":" + COAP_PORT + "/api/v1/";
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("coap.bind_port = {}", COAP_PORT);
registry.add("coap.bind_port", () -> COAP_PORT);
}
protected final byte[] EMPTY_PAYLOAD = new byte[0]; protected final byte[] EMPTY_PAYLOAD = new byte[0];
protected CoapTestClient client; protected CoapTestClient client;
protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," + protected static final String PAYLOAD_VALUES_STR = "{\"key1\":\"value1\", \"key2\":true, \"key3\": 3.0, \"key4\": 4," +

3
application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java

@ -28,9 +28,10 @@ import org.thingsboard.server.common.msg.session.FeatureType;
import java.io.IOException; import java.io.IOException;
import static org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest.COAP_BASE_URL;
public class CoapTestClient { public class CoapTestClient {
private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L; private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
private final CoapClient client; private final CoapClient client;

37
application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java

@ -21,6 +21,8 @@ import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapResponse; import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP; import org.eclipse.californium.core.coap.CoAP;
import org.junit.Assert; import org.junit.Assert;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.CoapDeviceType;
@ -37,10 +39,10 @@ import org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest;
import org.thingsboard.server.transport.coap.x509.CertPrivateKey; import org.thingsboard.server.transport.coap.x509.CertPrivateKey;
import org.thingsboard.server.transport.coap.x509.CoapClientX509Test; import org.thingsboard.server.transport.coap.x509.CoapClientX509Test;
import org.thingsboard.server.transport.coap.CoapTestConfigProperties; import org.thingsboard.server.transport.coap.CoapTestConfigProperties;
import org.thingsboard.server.utils.PortFinder;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.ServerSocket;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.PrivateKey; import java.security.PrivateKey;
@ -51,9 +53,7 @@ import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -70,7 +70,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
"transport.coap.enabled=true", "transport.coap.enabled=true",
}) })
public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest { public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIntegrationTest {
private static final String COAPS_BASE_URL = "coaps://localhost:5684/api/v1/";
public static final String COAPS_HOST = "localhost";
public static final int COAPS_PORT = PortFinder.findAvailableUdpPort();
public static final String COAPS_BASE_URL = "coaps://" + COAPS_HOST + ":" + COAPS_PORT + "/api/v1/";
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("coap.dtls.bind_port = {}", COAPS_PORT);
registry.add("coap.dtls.bind_port", () -> COAPS_PORT);
}
protected final String CREDENTIALS_PATH = "coap/credentials/"; protected final String CREDENTIALS_PATH = "coap/credentials/";
protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/"; protected final String CREDENTIALS_PATH_CLIENT = CREDENTIALS_PATH + "client/";
protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem"; protected final String CREDENTIALS_PATH_CLIENT_CERT_PEM = CREDENTIALS_PATH_CLIENT + "cert.pem";
@ -160,13 +170,10 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM); CertPrivateKey certPrivateKey = new CertPrivateKey(CREDENTIALS_PATH_CLIENT_CERT_PEM, CREDENTIALS_PATH_CLIENT_KEY_PEM);
CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem", CertPrivateKey certPrivateKey_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem",
CREDENTIALS_PATH_CLIENT + "key_01.pem"); CREDENTIALS_PATH_CLIENT + "key_01.pem");
Integer fixedPort = getFreePort(); int fixedPort = PortFinder.findAvailableUdpPort();
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey, CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort); "CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort);
clientX509.disconnect(); clientX509.disconnect();
await("Need to make port " + fixedPort + " free")
.atMost(40, TimeUnit.SECONDS)
.until(() -> isPortAvailable(fixedPort));
CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(),
fixedPort, PAYLOAD_VALUES_STR_01); fixedPort, PAYLOAD_VALUES_STR_01);
@ -274,19 +281,5 @@ public abstract class AbstractCoapSecurityIntegrationTest extends AbstractCoapIn
} }
} }
private static int getFreePort() throws IOException {
try (ServerSocket socket = new ServerSocket(0)) {
return socket.getLocalPort();
}
}
private static boolean isPortAvailable(int port) {
try (ServerSocket serverSocket = new ServerSocket(port)) {
serverSocket.setReuseAddress(true);
return true;
} catch (IOException e) {
return false;
}
}
} }

14
application/src/test/java/org/thingsboard/server/transport/coap/x509/CoapClientX509Test.java

@ -51,6 +51,7 @@ import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS; import static org.eclipse.californium.core.config.CoapConfig.DEFAULT_BLOCKWISE_STATUS_LIFETIME_IN_SECONDS;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CIPHER_SUITES;
@ -71,6 +72,8 @@ import static org.eclipse.californium.scandium.config.DtlsConfig.MODULE;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_ECDSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA256_WITH_RSA;
import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA; import static org.eclipse.californium.scandium.dtls.SignatureAndHashAlgorithm.SHA384_WITH_ECDSA;
import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j @Slf4j
public class CoapClientX509Test { public class CoapClientX509Test {
@ -82,6 +85,7 @@ public class CoapClientX509Test {
private final Configuration config; private final Configuration config;
private final CertPrivateKey certPrivateKey; private final CertPrivateKey certPrivateKey;
private final String coapsBaseUrl; private final String coapsBaseUrl;
private final Integer fixedPort;
@Getter @Getter
private CoAP.Type type = CoAP.Type.CON; private CoAP.Type type = CoAP.Type.CON;
@ -90,6 +94,7 @@ public class CoapClientX509Test {
this.certPrivateKey = certPrivateKey; this.certPrivateKey = certPrivateKey;
this.coapsBaseUrl = coapsBaseUrl; this.coapsBaseUrl = coapsBaseUrl;
this.config = createConfiguration(); this.config = createConfiguration();
this.fixedPort = fixedPort;
this.dtlsConnector = createDTLSConnector(fixedPort); this.dtlsConnector = createDTLSConnector(fixedPort);
this.clientX509 = createClient(getFeatureTokenUrl(featureType)); this.clientX509 = createClient(getFeatureTokenUrl(featureType));
} }
@ -97,6 +102,15 @@ public class CoapClientX509Test {
if (clientX509 != null) { if (clientX509 != null) {
clientX509.shutdown(); clientX509.shutdown();
} }
if (dtlsConnector != null) {
dtlsConnector.destroy();
if (fixedPort != null) {
log.debug("Awaiting releasing UDP fixedPort {}", fixedPort);
await("Await client UDP port " + fixedPort + " to disconnect")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(fixedPort));
}
}
} }
public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException { public CoapResponse postMethod(String requestBody) throws ConnectorException, IOException {

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

@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import jnr.ffi.annotations.In;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.awaitility.core.ConditionTimeoutException; import org.awaitility.core.ConditionTimeoutException;
@ -38,6 +39,8 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors; import org.thingsboard.common.util.ThingsBoardExecutors;
@ -84,9 +87,8 @@ import org.thingsboard.server.transport.lwm2m.server.client.LwM2mClientContext;
import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult; import org.thingsboard.server.transport.lwm2m.server.client.ResourceUpdateResult;
import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.DefaultLwM2mUplinkMsgHandler;
import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler; import org.thingsboard.server.transport.lwm2m.server.uplink.LwM2mUplinkMsgHandler;
import org.thingsboard.server.utils.PortFinder;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
@ -141,21 +143,35 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private LwM2mClientContext clientContextTest; private LwM2mClientContext clientContextTest;
// Lwm2m Server // Lwm2m Server
public static final int port = 5685; public static final String LWM2M_HOST = "localhost";
public static final int securityPort = 5686; public static final int LWM2M_PORT = PortFinder.findAvailableUdpPort(); // 5685
public static final int portBs = 5687; public static final int LWM2MS_PORT = PortFinder.findAvailableUdpPort(); //5686
public static final int securityPortBs = 5688;
public static final String LWM2M_BOOTSTRAP_HOST = "localhost";
public static final int LWM2M_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5687
public static final int LWM2MS_BOOTSTRAP_PORT = PortFinder.findAvailableUdpPort(); // 5688
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.lwm2m.server.bind_port = {}", LWM2M_PORT);
registry.add("transport.lwm2m.server.bind_port", () -> LWM2M_PORT);
log.warn("transport.lwm2m.server.security.bind_port = {}", LWM2MS_PORT);
registry.add("transport.lwm2m.server.security.bind_port", () -> LWM2MS_PORT);
log.warn("transport.lwm2m.bootstrap.bind_port = {}", LWM2M_BOOTSTRAP_PORT);
registry.add("transport.lwm2m.bootstrap.bind_port", () -> LWM2M_BOOTSTRAP_PORT);
log.warn("transport.lwm2m.bootstrap.security.bind_port = {}", LWM2MS_BOOTSTRAP_PORT);
registry.add("transport.lwm2m.bootstrap.security.bind_port", () -> LWM2MS_BOOTSTRAP_PORT);
}
public static final String host = "localhost";
public static final String hostBs = "localhost";
public static final Integer shortServerId = 123; public static final Integer shortServerId = 123;
public static final String COAP = "coap://"; public static final String COAP = "coap://";
public static final String COAPS = "coaps://"; public static final String COAPS = "coaps://";
public static final String URI = COAP + host + ":" + port; public static final String URI = COAP + LWM2M_HOST + ":" + LWM2M_PORT;
public static final String SECURE_URI = COAPS + host + ":" + securityPort; public static final String SECURE_URI = COAPS + LWM2M_HOST + ":" + LWM2MS_PORT;
public static final String URI_BS = COAP + hostBs + ":" + portBs; public static final String URI_BS = COAP + LWM2M_BOOTSTRAP_HOST + ":" + LWM2M_BOOTSTRAP_PORT;
public static final String SECURE_URI_BS = COAPS + hostBs + ":" + securityPortBs; public static final String SECURE_URI_BS = COAPS + LWM2M_BOOTSTRAP_HOST + ":" + LWM2MS_BOOTSTRAP_PORT;
public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId); public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS = protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
@ -570,12 +586,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
this.clientDestroy(); this.clientDestroy();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources); lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources);
try (ServerSocket socket = new ServerSocket(0)) { int clientPort = PortFinder.findAvailableUdpPort();
int clientPort = socket.getLocalPort(); //automatic client port assignment
lwM2MTestClient.init(security, securityBs, clientPort, isRpc, lwM2MTestClient.init(security, securityBs, 0 /* clientPort */, isRpc,
this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest, this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9); clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
}
lwM2MTestClient.setDeviceIdStr(deviceIdStr); lwM2MTestClient.setDeviceIdStr(deviceIdStr);
} }
@ -658,10 +673,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
try { try {
if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) { if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) {
boolean serverAlive = false; boolean serverAlive = false;
for (int port = AbstractLwM2MIntegrationTest.port; port <= securityPortBs; port++) { List<Integer> ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT);
try (ServerSocket socket = new ServerSocket(port)) { for (Integer port : ports) {
log.info("Port {} is free.", port); if (!PortFinder.isUDPPortAvailable(port)) {
} catch (IOException e) {
log.debug("Port {} is busy — CoAP server still active.", port); log.debug("Port {} is busy — CoAP server still active.", port);
serverAlive = true; serverAlive = true;
break; break;
@ -730,8 +744,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
bootstrapServerCredential.setServerPublicKey(""); bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port); bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT);
return bootstrapServerCredential; return bootstrapServerCredential;
} }
@ -746,11 +760,15 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
return credentials; return credentials;
} }
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception { protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
awaitObserveReadAll(cntObserve, deviceIdStr, "");
}
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr, String assertionAlias) throws Exception {
try { try {
await("ObserveReadAll: countObserve " + cntObserve) await("ObserveReadAll: countObserve " + cntObserve)
.atMost(40, TimeUnit.SECONDS) .alias(assertionAlias)
.atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> cntObserve == getCntObserveAll(deviceIdStr)); .until(() -> cntObserve == getCntObserveAll(deviceIdStr));
} catch (ConditionTimeoutException e) { } catch (ConditionTimeoutException e) {
int current = getCntObserveAll(deviceIdStr); int current = getCntObserveAll(deviceIdStr);

2
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/FwLwM2MDevice.java

@ -33,7 +33,6 @@ import java.util.List;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicInteger;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TARGET_FW_VERSION;
import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE; import static org.thingsboard.server.dao.service.OtaPackageServiceTest.TITLE;
@ -137,6 +136,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override @Override
public void destroy() { public void destroy() {
scheduler.shutdownNow(); scheduler.shutdownNow();
this.leshanClient = null;
} }
private void startDownloading() { private void startDownloading() {

41
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2MTestClient.java

@ -33,7 +33,6 @@ import org.eclipse.leshan.client.object.Security;
import org.eclipse.leshan.client.object.Server; import org.eclipse.leshan.client.object.Server;
import org.eclipse.leshan.client.observer.LwM2mClientObserver; import org.eclipse.leshan.client.observer.LwM2mClientObserver;
import org.eclipse.leshan.client.resource.DummyInstanceEnabler; import org.eclipse.leshan.client.resource.DummyInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mInstanceEnabler;
import org.eclipse.leshan.client.resource.LwM2mObjectEnabler; import org.eclipse.leshan.client.resource.LwM2mObjectEnabler;
import org.eclipse.leshan.client.resource.ObjectsInitializer; import org.eclipse.leshan.client.resource.ObjectsInitializer;
import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter; import org.eclipse.leshan.client.resource.listener.ObjectsListenerAdapter;
@ -82,6 +81,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@ -92,6 +92,7 @@ import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT; import static org.eclipse.leshan.core.LwM2mId.SOFTWARE_MANAGEMENT;
import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder; import static org.eclipse.leshan.core.node.codec.DefaultLwM2mEncoder.getDefaultPathEncoder;
import static org.thingsboard.server.transport.AbstractTransportIntegrationTest.DEFAULT_WAIT_TIMEOUT_SECONDS;
import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId; import static org.thingsboard.server.transport.lwm2m.AbstractLwM2MIntegrationTest.shortServerId;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.LwM2MClientState;
@ -119,6 +120,7 @@ import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.OBJECT_INST
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.TEMPERATURE_SENSOR;
import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources; import static org.thingsboard.server.transport.lwm2m.Lwm2mTestHelper.lwm2mClientResources;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength; import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength;
import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j @Slf4j
@ -139,14 +141,16 @@ public class LwM2MTestClient {
private Map<LwM2MClientState, Integer> clientDtlsCid; private Map<LwM2MClientState, Integer> clientDtlsCid;
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest; private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext; private LwM2mClientContext clientContext;
private LwM2mTemperatureSensor lwM2mTemperatureSensor12; private LwM2mTemperatureSensor lwM2MTemperatureSensor12;
private String deviceIdStr; private String deviceIdStr;
private int clientPort;
public void init(Security securityLwm2m, Security securityBs, int port, boolean isRpc, public void init(Security securityLwm2m, Security securityBs, int clientPort, boolean isRpc,
LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler, LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandler,
LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode, LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode,
boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException { boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient); Assert.assertNull("client already initialized", leshanClient);
this.clientPort = clientPort;
this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler; this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler;
this.clientContext = clientContext; this.clientContext = clientContext;
@ -156,7 +160,7 @@ public class LwM2MTestClient {
if (securityLwm2m != null && securityLwm2m.getId() != null) { if (securityLwm2m != null && securityLwm2m.getId() != null) {
forceNullSecurityId(securityLwm2m); forceNullSecurityId(securityLwm2m);
} }
if (securityBs!= null && securityBs.getId() != null) { if (securityBs != null && securityBs.getId() != null) {
forceNullSecurityId(securityBs); forceNullSecurityId(securityBs);
} }
if (securityBs != null && securityLwm2m != null) { if (securityBs != null && securityLwm2m != null) {
@ -165,7 +169,7 @@ public class LwM2MTestClient {
} else if (securityBs != null) { } else if (securityBs != null) {
log.warn("Security BS only: securityBs: [{}] ", securityBs.getId()); log.warn("Security BS only: securityBs: [{}] ", securityBs.getId());
initializer.setInstancesForObject(SECURITY, securityBs); initializer.setInstancesForObject(SECURITY, securityBs);
} else if (securityLwm2m != null){ } else if (securityLwm2m != null) {
// SECURITY // SECURITY
log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId()); log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId());
initializer.setInstancesForObject(SECURITY, securityLwm2m); initializer.setInstancesForObject(SECURITY, securityLwm2m);
@ -186,8 +190,8 @@ public class LwM2MTestClient {
locationParams.getPos(); locationParams.getPos();
initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0)); initializer.setInstancesForObject(LOCATION, new LwM2mLocation(locationParams.getLatitude(), locationParams.getLongitude(), locationParams.getScaleFactor(), executor, OBJECT_INSTANCE_ID_0));
LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0); LwM2mTemperatureSensor lwM2mTemperatureSensor0 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_0);
lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12); lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12); initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12);
List<LwM2mObjectEnabler> enablers = initializer.createAll(); List<LwM2mObjectEnabler> enablers = initializer.createAll();
@ -208,7 +212,9 @@ public class LwM2MTestClient {
builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid)); builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid));
return builder; return builder;
}; }
;
}; };
} }
}; };
@ -245,7 +251,7 @@ public class LwM2MTestClient {
// Set Californium Configuration // Set Californium Configuration
endpointsBuilder.setConfiguration(clientCoapConfig); endpointsBuilder.setConfiguration(clientCoapConfig);
endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress()); endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress());
// creates EndpointsProvider // creates EndpointsProvider
@ -438,12 +444,14 @@ public class LwM2MTestClient {
destroySafe(swLwM2MDevice); destroySafe(swLwM2MDevice);
destroySafe(lwM2MBinaryAppDataContainer); destroySafe(lwM2MBinaryAppDataContainer);
destroySafe(lwM2MTemperatureSensor); destroySafe(lwM2MTemperatureSensor);
destroySafe(lwM2MTemperatureSensor12);
lwM2MDevice = null; lwM2MDevice = null;
fwLwM2MDevice = null; fwLwM2MDevice = null;
swLwM2MDevice = null; swLwM2MDevice = null;
lwM2MBinaryAppDataContainer = null; lwM2MBinaryAppDataContainer = null;
lwM2MTemperatureSensor = null; lwM2MTemperatureSensor = null;
lwM2MTemperatureSensor12 = null;
} }
@ -462,11 +470,17 @@ public class LwM2MTestClient {
public void start(boolean isStartLw) { public void start(boolean isStartLw) {
if (leshanClient != null) { if (leshanClient != null) {
if (clientPort > 0) {
log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
leshanClient.start(); leshanClient.start();
if (isStartLw) { if (isStartLw) {
this.awaitClientAfterStartConnectLw(); this.awaitClientAfterStartConnectLw();
} }
lwM2mTemperatureSensor12.setLeshanClient(leshanClient); lwM2MTemperatureSensor12.setLeshanClient(leshanClient);
fwLwM2MDevice.setLeshanClient(leshanClient); fwLwM2MDevice.setLeshanClient(leshanClient);
} }
} }
@ -474,6 +488,12 @@ public class LwM2MTestClient {
public void stop(boolean deregister) { public void stop(boolean deregister) {
if (leshanClient != null) { if (leshanClient != null) {
leshanClient.stop(deregister); leshanClient.stop(deregister);
if (clientPort > 0) {
log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
.atMost(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
} }
@ -535,5 +555,6 @@ public class LwM2MTestClient {
log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e); log.error("[forceNullSecurityId] Failed to set id=null for {}", security.getClass(), e);
} }
} }
} }

1
application/src/test/java/org/thingsboard/server/transport/lwm2m/client/LwM2mTemperatureSensor.java

@ -169,6 +169,7 @@ public class LwM2mTemperatureSensor extends BaseInstanceEnabler implements Destr
@Override @Override
public void destroy() { public void destroy() {
this.leshanClient = null;
} }
private void sendCollected() { private void sendCollected() {

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/AbstractSecurityLwM2MIntegrationTest.java

@ -366,8 +366,8 @@ public abstract class AbstractSecurityLwM2MIntegrationTest extends AbstractLwM2M
} }
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId); bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap); bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host); bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort); bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT);
return bootstrapServerCredential; return bootstrapServerCredential;
} }

4
application/src/test/java/org/thingsboard/server/transport/lwm2m/security/sql/PskLwm2mIntegrationTest.java

@ -133,7 +133,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
ON_REGISTRATION_SUCCESS, ON_REGISTRATION_SUCCESS,
true); true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "before client stops for the first time");
lwM2MTestClient.stop(true); lwM2MTestClient.stop(true);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class); DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
@ -143,7 +143,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Assert.assertNotNull(lwm2mDeviceProfileManyParams); Assert.assertNotNull(lwm2mDeviceProfileManyParams);
lwM2MTestClient.start(true); lwM2MTestClient.start(true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString()); awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "second after client restart");
awaitUpdateReg(3); awaitUpdateReg(3);
} }

13
application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java

@ -19,7 +19,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.MqttException; import org.eclipse.paho.client.mqttv3.MqttException;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.springframework.test.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
@ -58,6 +61,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j @Slf4j
public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest { public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest {
public static final String MQTT_HOST = "localhost";
public static final int MQTT_PORT = TestSocketUtils.findAvailableTcpPort();
public static final String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
protected Device savedGateway; protected Device savedGateway;
protected String gatewayAccessToken; protected String gatewayAccessToken;

10
application/src/test/java/org/thingsboard/server/transport/mqtt/MqttGatewayRateLimitsTest.java

@ -16,12 +16,15 @@
package org.thingsboard.server.transport.mqtt; package org.thingsboard.server.transport.mqtt;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility; import org.awaitility.Awaitility;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.mockito.Mockito; import org.mockito.Mockito;
import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
@ -43,13 +46,20 @@ import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY;
import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE; import static org.thingsboard.server.common.data.limit.LimitedApi.TRANSPORT_MESSAGES_PER_GATEWAY_DEVICE;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@DaoSqlTest @DaoSqlTest
@TestPropertySource(properties = { @TestPropertySource(properties = {
"service.integrations.supported=ALL", "service.integrations.supported=ALL",
"transport.mqtt.enabled=true", "transport.mqtt.enabled=true",
}) })
@Slf4j
public class MqttGatewayRateLimitsTest extends AbstractControllerTest { public class MqttGatewayRateLimitsTest extends AbstractControllerTest {
@DynamicPropertySource
static void props(DynamicPropertyRegistry registry) {
log.warn("transport.mqtt.bind_port = {}", MQTT_PORT);
registry.add("transport.mqtt.bind_port", () -> MQTT_PORT);
}
private static final String GATEWAY_TOPIC = "v1/gateway/telemetry"; private static final String GATEWAY_TOPIC = "v1/gateway/telemetry";
private static final String DEVICE_TOPIC = "v1/devices/me/telemetry"; private static final String DEVICE_TOPIC = "v1/devices/me/telemetry";

6
application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/MqttTestClient.java

@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
public class MqttTestClient { public class MqttTestClient {
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds private static final int TIMEOUT = 30; // seconds
public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client; private final MqttAsyncClient client;
public void setCallback(MqttTestCallback callback) { public void setCallback(MqttTestCallback callback) {

6
application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv5/MqttV5TestClient.java

@ -27,12 +27,16 @@ import org.thingsboard.server.common.data.StringUtils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import public class MqttV5TestClient { // We should copy part of MqttV3TestClient, due to different package names in import
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds private static final int TIMEOUT = 30; // seconds
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT); private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client; private final MqttAsyncClient client;
public void setCallback(MqttCallback callback) { public void setCallback(MqttCallback callback) {

42
application/src/test/java/org/thingsboard/server/utils/PortFinder.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2026 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.utils;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketException;
@Slf4j
public class PortFinder {
public static int findAvailableUdpPort() {
try (DatagramSocket socket = new DatagramSocket(0)) {
return socket.getLocalPort();
} catch (SocketException e) {
throw new IllegalStateException("No available UDP ports found", e);
}
}
public static boolean isUDPPortAvailable(int port) {
try (DatagramSocket socket = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
log.debug("Failed to open UDP port {}", port, e);
return false;
}
}
}

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

@ -1,3 +1,4 @@
spring.main.banner-mode=off
js.evaluator=mock js.evaluator=mock
transport.lwm2m.server.security.credentials.enabled=true transport.lwm2m.server.security.credentials.enabled=true
transport.lwm2m.server.security.credentials.type=KEYSTORE transport.lwm2m.server.security.credentials.type=KEYSTORE

6
application/src/test/resources/logback-test.xml

@ -42,6 +42,12 @@
<!-- <logger name="org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mRedisRegistrationStore" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.transport.lwm2m.server.store.TbLwM2mRedisRegistrationStore" level="TRACE"/>-->
<!-- <logger name="org.thingsboard.server.transport.lwm2m.security.diffPort" level="TRACE"/>--> <!-- <logger name="org.thingsboard.server.transport.lwm2m.security.diffPort" level="TRACE"/>-->
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<!-- Log MvcMock result with status >=400 -->
<logger name="org.springframework.test.web.servlet.result" level="DEBUG"/>
<root level="WARN"> <root level="WARN">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

3
common/cache/src/test/resources/logback-test.xml

@ -9,6 +9,9 @@
<logger name="org.thingsboard.server.cache" level="TRACE"/> <logger name="org.thingsboard.server.cache" level="TRACE"/>
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<root level="INFO"> <root level="INFO">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

3
common/edqs/src/main/java/org/thingsboard/server/edqs/util/TbRocksDb.java

@ -16,6 +16,7 @@
package org.thingsboard.server.edqs.util; package org.thingsboard.server.edqs.util;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.rocksdb.Options; import org.rocksdb.Options;
import org.rocksdb.RocksDB; import org.rocksdb.RocksDB;
import org.rocksdb.RocksIterator; import org.rocksdb.RocksIterator;
@ -26,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
@Slf4j
public class TbRocksDb { public class TbRocksDb {
protected final String path; protected final String path;
@ -45,6 +47,7 @@ public class TbRocksDb {
@SneakyThrows @SneakyThrows
public void init() { public void init() {
log.debug("RocksDB init in {}", path);
Files.createDirectories(Path.of(path).getParent()); Files.createDirectories(Path.of(path).getParent());
db = RocksDB.open(dbOptions, path); db = RocksDB.open(dbOptions, path);
} }

7
dao/src/main/java/org/thingsboard/server/dao/resource/DefaultTbResourceDataCache.java

@ -19,6 +19,7 @@ import com.github.benmanes.caffeine.cache.AsyncLoadingCache;
import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.util.concurrent.FluentFuture; import com.google.common.util.concurrent.FluentFuture;
import jakarta.annotation.PostConstruct; import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
@ -55,6 +56,12 @@ public class DefaultTbResourceDataCache implements TbResourceDataCache {
.buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor)); .buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> resourceService.getResourceDataInfo(key.tenantId(), key.resourceId()), executor));
} }
@PreDestroy
private void destroy() {
cache.synchronous().invalidateAll();
cache = null;
}
@Override @Override
public FluentFuture<TbResourceDataInfo> getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) { public FluentFuture<TbResourceDataInfo> getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) {
log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId); log.trace("Retrieving resource data info by id [{}], tenant id [{}] from cache", resourceId, tenantId);

16
dao/src/main/java/org/thingsboard/server/dao/service/validator/DeviceProfileDataValidator.java

@ -100,6 +100,18 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
@Autowired @Autowired
private DashboardService dashboardService; private DashboardService dashboardService;
@Value("${transport.lwm2m.server.bind_port:5685}")
private Integer lwm2mPort;
@Value("${transport.lwm2m.server.security.bind_port:5686}")
private Integer lwm2mSecurePort;
@Value("${transport.lwm2m.bootstrap.bind_port:5687}")
private Integer lwm2mBootstrapPort;
@Value("${transport.lwm2m.bootstrap.security.bind_port:5688}")
private Integer lwm2mBootstrapSecurePort;
@Value("${security.java_cacerts.path:}") @Value("${security.java_cacerts.path:}")
private String javaCacertsPath; private String javaCacertsPath;
@ -371,9 +383,9 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
} }
int port; int port;
if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) { if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) {
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685; port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapPort : lwm2mPort;
} else { } else {
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686; port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapSecurePort : lwm2mSecurePort;
} }
if (serverConfig.getPort() == null || serverConfig.getPort() != port) { if (serverConfig.getPort() == null || serverConfig.getPort() != port) {
throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!"); throw new DeviceCredentialsValidationException(server + " \"Port\" value = " + serverConfig.getPort() + ". This value for security " + serverConfig.getSecurityMode().name() + " must be " + port + "!");

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

@ -1,3 +1,4 @@
spring.main.banner-mode=off
zk.enabled=false zk.enabled=false
zk.url=localhost:2181 zk.url=localhost:2181
zk.zk_dir=/thingsboard zk.zk_dir=/thingsboard

3
dao/src/test/resources/logback-test.xml

@ -19,6 +19,9 @@
<!-- <logger name="org.hibernate.SQL" level="DEBUG"/> --> <!-- <logger name="org.hibernate.SQL" level="DEBUG"/> -->
<!-- <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" /> --> <!-- <logger name="org.springframework.jdbc.core.JdbcTemplate" level="DEBUG" /> -->
<!-- Avoiding multiple logs like: The cache 'relations' is not recording statistics. No meters except 'cache.size' will be registered. Call 'Caffeine#recordStats()' prior to building the cache for metrics to be recorded.-->
<logger name="io.micrometer.core.instrument.binder.cache.CaffeineCacheMetrics" level="ERROR"/>
<root level="WARN"> <root level="WARN">
<appender-ref ref="console"/> <appender-ref ref="console"/>
</root> </root>

4
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java

@ -28,7 +28,7 @@ import org.thingsboard.server.common.msg.session.FeatureType;
public abstract class AbstractCoapClientTest extends AbstractContainerTest{ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; private static final String CONTAINER_COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L; private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
@ -54,7 +54,7 @@ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
protected byte[] createCoapClientAndPublish(String deviceName) throws Exception { protected byte[] createCoapClientAndPublish(String deviceName) throws Exception {
String provisionRequestMsg = createTestProvisionMessage(deviceName); String provisionRequestMsg = createTestProvisionMessage(deviceName);
Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER); Configuration.addDefaultModule(MODULE_DEFINITIONS_PROVIDER);
String featureTokenUrl = COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase(); String featureTokenUrl = CONTAINER_COAP_BASE_URL + FeatureType.PROVISION.name().toLowerCase();
client = new CoapClient(featureTokenUrl); client = new CoapClient(featureTokenUrl);
return client.setTimeout(CLIENT_REQUEST_TIMEOUT) return client.setTimeout(CLIENT_REQUEST_TIMEOUT)
.post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON) .post(provisionRequestMsg.getBytes(), MediaTypeRegistry.APPLICATION_JSON)

42
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/PortFinder.java

@ -0,0 +1,42 @@
/**
* Copyright © 2016-2026 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.msa;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.net.DatagramSocket;
import java.net.SocketException;
@Slf4j
public class PortFinder {
public static int findAvailableUdpPort() {
try (DatagramSocket socket = new DatagramSocket(0)) {
return socket.getLocalPort();
} catch (SocketException e) {
throw new IllegalStateException("No available UDP ports found", e);
}
}
public static boolean isUDPPortAvailable(int port) {
try (DatagramSocket socket = new DatagramSocket(port)) {
return true;
} catch (IOException e) {
log.debug("Failed to open UDP port {}", port, e);
return false;
}
}
}

8
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/AbstractLwm2mClientTest.java

@ -57,12 +57,12 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.common.data.security.DeviceCredentialsType; import org.thingsboard.server.common.data.security.DeviceCredentialsType;
import org.thingsboard.server.msa.AbstractContainerTest; import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.PortFinder;
import org.thingsboard.server.msa.WsClient; import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient; import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState; import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse; import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
@ -229,10 +229,8 @@ public class AbstractLwm2mClientTest extends AbstractContainerTest {
String endpoint, ScheduledExecutorService executor) throws Exception { String endpoint, ScheduledExecutorService executor) throws Exception {
this.executor = executor; this.executor = executor;
LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint); LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint);
try (ServerSocket socket = new ServerSocket(0)) { int clientPort = PortFinder.findAvailableUdpPort();
int clientPort = socket.getLocalPort(); lwM2MTestClient.init(security, clientPort);
lwM2MTestClient.init(security, clientPort);
}
return lwM2MTestClient; return lwM2MTestClient;
} }

21
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/lwm2m/client/LwM2MTestClient.java

@ -65,6 +65,7 @@ import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.awaitility.Awaitility.await;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_CONNECTION_ID_LENGTH;
import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY; import static org.eclipse.californium.scandium.config.DtlsConfig.DTLS_RECOMMENDED_CIPHER_SUITES_ONLY;
import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL; import static org.eclipse.leshan.core.LwM2mId.ACCESS_CONTROL;
@ -72,6 +73,7 @@ import static org.eclipse.leshan.core.LwM2mId.DEVICE;
import static org.eclipse.leshan.core.LwM2mId.FIRMWARE; import static org.eclipse.leshan.core.LwM2mId.FIRMWARE;
import static org.eclipse.leshan.core.LwM2mId.SECURITY; import static org.eclipse.leshan.core.LwM2mId.SECURITY;
import static org.eclipse.leshan.core.LwM2mId.SERVER; import static org.eclipse.leshan.core.LwM2mId.SERVER;
import static org.thingsboard.server.msa.PortFinder.isUDPPortAvailable;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_FAILURE;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState.ON_BOOTSTRAP_STARTED;
@ -95,7 +97,6 @@ import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelp
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.serverId;
import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId; import static org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.shortServerId;
@Slf4j @Slf4j
@Data @Data
public class LwM2MTestClient { public class LwM2MTestClient {
@ -115,8 +116,11 @@ public class LwM2MTestClient {
private int countUpdateRegistrationSuccess; private int countUpdateRegistrationSuccess;
private int countReadObserveAfterUpdateRegistrationSuccess; private int countReadObserveAfterUpdateRegistrationSuccess;
private int clientPort;
public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException { public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException {
assertThat(leshanClient).as("client already initialized").isNull(); assertThat(leshanClient).as("client already initialized").isNull();
this.clientPort = clientPort;
List<ObjectModel> models = new ArrayList<>(); List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) { for (String resourceName : resources) {
@ -342,12 +346,27 @@ public class LwM2MTestClient {
} }
}); });
}
public void start() {
leshanClient.start(); leshanClient.start();
if (clientPort > 0) {
log.error("Await UDP clientPort {} to be available before leshanClient.start()", clientPort);
await("Await UDP clientPort " + clientPort + " to be available before leshanClient.start()")
.atMost(30, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
public void destroy() { public void destroy() {
if (leshanClient != null) { if (leshanClient != null) {
leshanClient.destroy(true); leshanClient.destroy(true);
if (clientPort > 0) {
log.error("Await UDP clientPort {} to disconnect after leshanClient.stop(deregister)", clientPort);
await("Await client UDP port " + clientPort + " to disconnect after leshanClient.stop(deregister)")
.atMost(30, TimeUnit.SECONDS)
.until(() -> isUDPPortAvailable(clientPort));
}
} }
if (lwM2MDevice != null) { if (lwM2MDevice != null) {
lwM2MDevice.destroy(); lwM2MDevice.destroy();

3
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/rule/node/MqttNodeTest.java

@ -65,6 +65,7 @@ import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevi
public class MqttNodeTest extends AbstractContainerTest { public class MqttNodeTest extends AbstractContainerTest {
private static final String TOPIC = "tb/mqtt/device"; private static final String TOPIC = "tb/mqtt/device";
private static final String CONTAINER_MQTT_URL = "tcp://localhost:1883";
private Device device; private Device device;
@ -94,7 +95,7 @@ public class MqttNodeTest extends AbstractContainerTest {
responseClient.connect(); responseClient.connect();
responseClient.subscribe(TOPIC, messageListener); responseClient.subscribe(TOPIC, messageListener);
MqttClient mqttClient = new MqttClient("tcp://localhost:1883", StringUtils.randomAlphanumeric(10), new MemoryPersistence()); MqttClient mqttClient = new MqttClient(CONTAINER_MQTT_URL, StringUtils.randomAlphanumeric(10), new MemoryPersistence());
MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); MqttConnectOptions mqttConnectOptions = new MqttConnectOptions();
mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId()); mqttConnectOptions.setUserName(deviceCredentials.getCredentialsId());
mqttClient.connect(mqttConnectOptions); mqttClient.connect(mqttConnectOptions);

21
pom.xml

@ -69,7 +69,7 @@
<paho.client.version>1.2.5</paho.client.version> <paho.client.version>1.2.5</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version> <paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.version>
<os-maven-plugin.version>1.7.1</os-maven-plugin.version> <os-maven-plugin.version>1.7.1</os-maven-plugin.version>
<surefire.version>3.2.5</surefire.version> <surefire.version>3.5.4</surefire.version>
<jar-plugin.version>3.4.0</jar-plugin.version> <jar-plugin.version>3.4.0</jar-plugin.version>
<springdoc-swagger.version>2.8.8TB</springdoc-swagger.version> <springdoc-swagger.version>2.8.8TB</springdoc-swagger.version>
<swagger-annotations.version>2.2.30</swagger-annotations.version> <swagger-annotations.version>2.2.30</swagger-annotations.version>
@ -127,7 +127,7 @@
<jeasy.version>5.0.0</jeasy.version> <jeasy.version>5.0.0</jeasy.version>
<!-- BLACKBOX TEST SCOPE --> <!-- BLACKBOX TEST SCOPE -->
<testng.version>7.10.1</testng.version> <testng.version>7.10.1</testng.version>
<testcontainers.version>1.20.6</testcontainers.version> <testcontainers.version>1.21.4</testcontainers.version>
<testcontainers-junit4-mock.version>1.0.2</testcontainers-junit4-mock.version> <testcontainers-junit4-mock.version>1.0.2</testcontainers-junit4-mock.version>
<zeroturnaround.version>1.12</zeroturnaround.version> <zeroturnaround.version>1.12</zeroturnaround.version>
<webdrivermanager.version>6.1.0</webdrivermanager.version> <webdrivermanager.version>6.1.0</webdrivermanager.version>
@ -172,6 +172,16 @@
<activeByDefault>true</activeByDefault> <activeByDefault>true</activeByDefault>
</activation> </activation>
</profile> </profile>
<profile>
<id>default-surefire-java-opts</id>
<activation>
<property>
<name>!env.SUREFIRE_JAVA_OPTS</name> </property>
</activation>
<properties>
<env.SUREFIRE_JAVA_OPTS> </env.SUREFIRE_JAVA_OPTS>
</properties>
</profile>
<!-- download sources under target/dependencies --> <!-- download sources under target/dependencies -->
<!-- mvn package -Pdownload-dependencies -Dclassifier=sources dependency:copy-dependencies --> <!-- mvn package -Pdownload-dependencies -Dclassifier=sources dependency:copy-dependencies -->
<profile> <profile>
@ -662,8 +672,11 @@
<version>${surefire.version}</version> <version>${surefire.version}</version>
<configuration> <configuration>
<argLine> <argLine>
-XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20 -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200
--add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
-Dqueue.edqs.local.rocksdb_path="target/rocksdb/fork_${surefire.forkNumber}/edqs"
-Dqueue.calculated_fields.rocks_db_path="target/rocksdb/fork_${surefire.forkNumber}/cf"
${env.SUREFIRE_JAVA_OPTS}
</argLine> </argLine>
</configuration> </configuration>
</plugin> </plugin>

Loading…
Cancel
Save