Browse Source

Merge pull request #14884 from thingsboard/lts-4.3

LTS to RC
pull/14885/head
Viacheslav Klimov 4 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.request.MockHttpServletRequestBuilder;
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.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
@ -349,7 +350,14 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
if (this.mockMvc == null) {
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();
@ -423,7 +431,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
jdbcTemplate.execute("TRUNCATE TABLE notification");
log.info("Executed web test teardown");
log.debug("Executed web test teardown");
}
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.Primary;
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.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardExecutors;
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.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -121,7 +125,12 @@ import static org.thingsboard.server.edge.AbstractEdgeTest.CONNECT_MESSAGE_COUNT
public class EdgeControllerTest extends AbstractControllerTest {
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<>();
@ -916,11 +925,11 @@ public class EdgeControllerTest extends AbstractControllerTest {
verifyFetchersMsgs(edgeImitator, savedDevice);
// verify queue msgs
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
printQueueMsgsIfNotEmpty(edgeImitator);
// 17 connect messages
@ -1000,32 +1009,32 @@ public class EdgeControllerTest extends AbstractControllerTest {
}
private void verifyFetchersMsgs(EdgeImitator edgeImitator, Device savedDevice) {
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "general"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "mail"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "connectivity"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgs(), "jwt"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgs(), currentUserId));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgs(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgs(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgs(), tenantId));
Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgs(), tenantProfileId));
Assert.assertTrue(popSyncCompletedMsg(edgeImitator.getDownlinkMsgs()));
Assert.assertTrue(popQueueMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Main"));
Assert.assertTrue(popRuleChainMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Edge Root Rule Chain"));
Assert.assertTrue(popRuleChainMetadataMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, getEdgeRootRuleChainId(edgeImitator)));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "general"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "mail"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "connectivity"));
Assert.assertTrue(popAdminSettingsMsg(edgeImitator.getDownlinkMsgsDeque(), "jwt"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popUserCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), currentUserId));
Assert.assertTrue(popUserMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, TENANT_ADMIN_EMAIL, Authority.TENANT_ADMIN));
Assert.assertTrue(popCustomerMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Public"));
Assert.assertTrue(popDeviceProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popDeviceMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Device 1"));
Assert.assertTrue(popDeviceCredentialsMsg(edgeImitator.getDownlinkMsgsDeque(), savedDevice.getId()));
Assert.assertTrue(popAssetProfileMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "default"));
Assert.assertTrue(popAssetMsg(edgeImitator.getDownlinkMsgsDeque(), UpdateMsgType.ENTITY_CREATED_RPC_MESSAGE, "Test Sync Edge Asset 1"));
Assert.assertTrue(popTenantMsg(edgeImitator.getDownlinkMsgsDeque(), tenantId));
Assert.assertTrue(popTenantProfileMsg(edgeImitator.getDownlinkMsgsDeque(), tenantProfileId));
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) {
if (message instanceof QueueUpdateMsg queueUpdateMsg) {
Queue queue = JacksonUtil.fromString(queueUpdateMsg.getEntity(), Queue.class, true);
@ -1039,7 +1048,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof RuleChainUpdateMsg ruleChainUpdateMsg) {
RuleChain ruleChain = JacksonUtil.fromString(ruleChainUpdateMsg.getEntity(), RuleChain.class, true);
@ -1055,7 +1064,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof RuleChainMetadataUpdateMsg ruleChainMetadataUpdateMsg) {
RuleChainMetaData ruleChainMetaData = JacksonUtil.fromString(ruleChainMetadataUpdateMsg.getEntity(), RuleChainMetaData.class, true);
@ -1070,7 +1079,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popAdminSettingsMsg(List<AbstractMessage> messages, String key) {
private boolean popAdminSettingsMsg(Deque<AbstractMessage> messages, String key) {
for (AbstractMessage message : messages) {
if (message instanceof AdminSettingsUpdateMsg adminSettingsUpdateMsg) {
AdminSettings adminSettings = JacksonUtil.fromString(adminSettingsUpdateMsg.getEntity(), AdminSettings.class, true);
@ -1084,7 +1093,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof DeviceProfileUpdateMsg deviceProfileUpdateMsg) {
DeviceProfile deviceProfile = JacksonUtil.fromString(deviceProfileUpdateMsg.getEntity(), DeviceProfile.class, true);
@ -1099,7 +1108,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof DeviceUpdateMsg deviceUpdateMsg) {
Device device = JacksonUtil.fromString(deviceUpdateMsg.getEntity(), Device.class, true);
@ -1114,7 +1123,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popDeviceCredentialsMsg(List<AbstractMessage> messages, DeviceId deviceId) {
private boolean popDeviceCredentialsMsg(Deque<AbstractMessage> messages, DeviceId deviceId) {
for (AbstractMessage message : messages) {
if (message instanceof DeviceCredentialsUpdateMsg deviceCredentialsUpdateMsg) {
DeviceCredentials deviceCredentials = JacksonUtil.fromString(deviceCredentialsUpdateMsg.getEntity(), DeviceCredentials.class, true);
@ -1128,7 +1137,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof AssetProfileUpdateMsg assetProfileUpdateMsg) {
AssetProfile assetProfile = JacksonUtil.fromString(assetProfileUpdateMsg.getEntity(), AssetProfile.class, true);
@ -1143,7 +1152,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof AssetUpdateMsg assetUpdateMsg) {
Asset asset = JacksonUtil.fromString(assetUpdateMsg.getEntity(), Asset.class, true);
@ -1158,7 +1167,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popUserCredentialsMsg(List<AbstractMessage> messages, UserId userId) {
private boolean popUserCredentialsMsg(Deque<AbstractMessage> messages, UserId userId) {
for (AbstractMessage message : messages) {
if (message instanceof UserCredentialsUpdateMsg userCredentialsUpdateMsg) {
UserCredentials userCredentials = JacksonUtil.fromString(userCredentialsUpdateMsg.getEntity(), UserCredentials.class, true);
@ -1172,7 +1181,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof UserUpdateMsg userUpdateMsg) {
User user = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);
@ -1188,7 +1197,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
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) {
if (message instanceof CustomerUpdateMsg customerUpdateMsg) {
Customer customer = JacksonUtil.fromString(customerUpdateMsg.getEntity(), Customer.class, true);
@ -1203,7 +1212,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popTenantMsg(List<AbstractMessage> messages, TenantId tenantId1) {
private boolean popTenantMsg(Deque<AbstractMessage> messages, TenantId tenantId1) {
for (AbstractMessage message : messages) {
if (message instanceof TenantUpdateMsg tenantUpdateMsg) {
Tenant tenant = JacksonUtil.fromString(tenantUpdateMsg.getEntity(), Tenant.class, true);
@ -1218,7 +1227,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popTenantProfileMsg(List<AbstractMessage> messages, TenantProfileId tenantProfileId) {
private boolean popTenantProfileMsg(Deque<AbstractMessage> messages, TenantProfileId tenantProfileId) {
for (AbstractMessage message : messages) {
if (message instanceof TenantProfileUpdateMsg tenantProfileUpdateMsg) {
TenantProfile tenantProfile = JacksonUtil.fromString(tenantProfileUpdateMsg.getEntity(), TenantProfile.class, true);
@ -1233,7 +1242,7 @@ public class EdgeControllerTest extends AbstractControllerTest {
return false;
}
private boolean popSyncCompletedMsg(List<AbstractMessage> messages) {
private boolean popSyncCompletedMsg(Deque<AbstractMessage> messages) {
for (AbstractMessage message : messages) {
if (message instanceof SyncCompletedMsg) {
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.Primary;
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.web.servlet.ResultActions;
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.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
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 = {
"transport.mqtt.enabled=true",
@ -97,6 +101,12 @@ import static org.thingsboard.server.dao.model.ModelConstants.NULL_UUID;
@ContextConfiguration(classes = {EntityViewControllerTest.Config.class})
@DaoSqlTest
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<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 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();
options.setUserName(accessToken);
@ -734,7 +744,7 @@ public class EntityViewControllerTest extends AbstractControllerTest {
assertNotNull(accessToken);
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();
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.Before;
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.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.Customer;
@ -118,6 +121,15 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
})
@Slf4j
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 INSTALLATION_MESSAGE_COUNT = 8;
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
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
edgeImitator.expectMessageAmount(SYNC_MESSAGE_COUNT);
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.protobuf.AbstractMessage;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.Assert;
import org.junit.Test;
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.thingsboard.common.util.JacksonUtil;
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.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.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@TestPropertySource(properties = {
"transport.mqtt.enabled=true"
})
@Slf4j
@DaoSqlTest
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";

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.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentLinkedDeque;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
@ -103,8 +105,17 @@ public class EdgeImitator {
@Getter
private EdgeConfiguration configuration;
@Getter
private final List<AbstractMessage> downlinkMsgs;
private final ConcurrentLinkedDeque<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
private UplinkResponseMsg latestResponseMsg;
@ -113,7 +124,7 @@ public class EdgeImitator {
edgeRpcClient = new EdgeGrpcClient();
messagesLatch = new CountDownLatch(0);
responsesLatch = new CountDownLatch(0);
downlinkMsgs = new ArrayList<>();
downlinkMsgs = new ConcurrentLinkedDeque<>();
ignoredTypes = new ArrayList<>();
this.routingKey = routingKey;
this.routingSecret = routingSecret;
@ -458,7 +469,7 @@ public class EdgeImitator {
}
public AbstractMessage getLatestMessage() {
return downlinkMsgs.get(downlinkMsgs.size() - 1);
return downlinkMsgs.peekLast();
}
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);
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);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
long ts = System.currentTimeMillis();
await().atMost(15, TimeUnit.SECONDS)
await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> clients.values().stream().allMatch(client -> client.getLastDataUpdate() != null));
clients.forEach((expectedDelay, wsClient) -> {
Notification notification = wsClient.getLastDataUpdate().getUpdate();
@ -291,8 +291,8 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
alarm.setOriginator(device.getId());
alarm = doPost("/api/alarm", alarm, Alarm.class);
await().atMost(15, TimeUnit.SECONDS)
.pollDelay(2, TimeUnit.SECONDS)
await().atMost(TIMEOUT, TimeUnit.SECONDS)
.pollDelay(1, TimeUnit.SECONDS)
.untilAsserted(() -> {
List<Notification> notifications = getMyNotifications(false, 10);
assertThat(notifications).singleElement().matches(notification -> {
@ -345,7 +345,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.set("createAlarm", BooleanNode.TRUE);
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);
Alarm alarm = alarmSubscriptionService.findLatestByOriginatorAndType(tenantId, device.getId(), alarmType);
getWsClient().waitForUpdate(true);
@ -355,14 +355,14 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
assertThat(notification.getInfo()).asInstanceOf(type(AlarmNotificationInfo.class))
.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()
.filter(NotificationRequest::isScheduled)
.findFirst().orElse(null);
assertThat(scheduledNotificationRequest).extracting(NotificationRequest::getInfo).isEqualTo(notification.getInfo());
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();
});
}
@ -480,7 +480,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
}
loginTenantAdmin();
List<Notification> notifications = await().atMost(15, TimeUnit.SECONDS)
List<Notification> notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 3);
@ -500,7 +500,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
});
loginSysAdmin();
notifications = await().atMost(15, TimeUnit.SECONDS)
notifications = await().atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> getMyNotifications(true, 10).stream()
.filter(notification -> notification.getType() == NotificationType.RATE_LIMITS)
.collect(Collectors.toList()), list -> list.size() == 1);
@ -721,7 +721,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.updateInfo(new UpdateMessage(true, "CHANGED", "test",
"test", "test", "test"))
.build());
await().atMost(5, TimeUnit.SECONDS)
await().atMost(TIMEOUT, TimeUnit.SECONDS)
.untilAsserted(() -> {
assertThat(getMyNotifications(false, 100)).size().isEqualTo(2);
});
@ -809,7 +809,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
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);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -836,7 +836,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.build());
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);
assertThat(notification.getSubject()).isEqualTo("Warning: RAM shortage");
assertThat(notification.getText()).isEqualTo("RAM shortage");
@ -848,7 +848,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
.serviceType("serviceType")
.serviceId("serviceId")
.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
@ -887,7 +887,7 @@ public class NotificationRuleApiTest extends AbstractNotificationApiTest {
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);
assertThat(notification.getSubject()).isEqualTo("Warning: 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
public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest {
protected 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/";
public static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
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;
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.util.TestSocketUtils;
import org.thingsboard.server.common.data.CoapDeviceType;
import org.thingsboard.server.common.data.Device;
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.security.DeviceCredentials;
import org.thingsboard.server.transport.AbstractTransportIntegrationTest;
import org.thingsboard.server.utils.PortFinder;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@ -52,6 +56,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j
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 CoapTestClient client;
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 static org.thingsboard.server.transport.coap.AbstractCoapIntegrationTest.COAP_BASE_URL;
public class CoapTestClient {
private static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
private static final long CLIENT_REQUEST_TIMEOUT = 60000L;
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.coap.CoAP;
import org.junit.Assert;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.springframework.test.context.TestPropertySource;
import org.thingsboard.common.util.JacksonUtil;
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.CoapClientX509Test;
import org.thingsboard.server.transport.coap.CoapTestConfigProperties;
import org.thingsboard.server.utils.PortFinder;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
@ -51,9 +53,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
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.assertNotNull;
import static org.junit.Assert.assertTrue;
@ -70,7 +70,17 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
"transport.coap.enabled=true",
})
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_CLIENT = CREDENTIALS_PATH + "client/";
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_01 = new CertPrivateKey(CREDENTIALS_PATH_CLIENT + "cert_01.pem",
CREDENTIALS_PATH_CLIENT + "key_01.pem");
Integer fixedPort = getFreePort();
int fixedPort = PortFinder.findAvailableUdpPort();
CoapClientX509Test clientX509 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name(), deviceProfile.getId(), fixedPort);
clientX509.disconnect();
await("Need to make port " + fixedPort + " free")
.atMost(40, TimeUnit.SECONDS)
.until(() -> isPortAvailable(fixedPort));
CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01,
"CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(),
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 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.scandium.config.DtlsConfig.DTLS_AUTO_HANDSHAKE_TIMEOUT;
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_RSA;
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
public class CoapClientX509Test {
@ -82,6 +85,7 @@ public class CoapClientX509Test {
private final Configuration config;
private final CertPrivateKey certPrivateKey;
private final String coapsBaseUrl;
private final Integer fixedPort;
@Getter
private CoAP.Type type = CoAP.Type.CON;
@ -90,6 +94,7 @@ public class CoapClientX509Test {
this.certPrivateKey = certPrivateKey;
this.coapsBaseUrl = coapsBaseUrl;
this.config = createConfiguration();
this.fixedPort = fixedPort;
this.dtlsConnector = createDTLSConnector(fixedPort);
this.clientX509 = createClient(getFeatureTokenUrl(featureType));
}
@ -97,6 +102,15 @@ public class CoapClientX509Test {
if (clientX509 != null) {
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 {

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.google.gson.JsonArray;
import com.google.gson.JsonElement;
import jnr.ffi.annotations.In;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.awaitility.core.ConditionTimeoutException;
@ -38,6 +39,8 @@ import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.SpyBean;
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.thingsboard.common.util.JacksonUtil;
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.uplink.DefaultLwM2mUplinkMsgHandler;
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.Arrays;
import java.util.Collections;
@ -141,21 +143,35 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
private LwM2mClientContext clientContextTest;
// Lwm2m Server
public static final int port = 5685;
public static final int securityPort = 5686;
public static final int portBs = 5687;
public static final int securityPortBs = 5688;
public static final String LWM2M_HOST = "localhost";
public static final int LWM2M_PORT = PortFinder.findAvailableUdpPort(); // 5685
public static final int LWM2MS_PORT = PortFinder.findAvailableUdpPort(); //5686
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 String COAP = "coap://";
public static final String COAPS = "coaps://";
public static final String URI = COAP + host + ":" + port;
public static final String SECURE_URI = COAPS + host + ":" + securityPort;
public static final String URI_BS = COAP + hostBs + ":" + portBs;
public static final String SECURE_URI_BS = COAPS + hostBs + ":" + securityPortBs;
public static final String URI = COAP + LWM2M_HOST + ":" + LWM2M_PORT;
public static final String SECURE_URI = COAPS + LWM2M_HOST + ":" + LWM2MS_PORT;
public static final String URI_BS = COAP + LWM2M_BOOTSTRAP_HOST + ":" + LWM2M_BOOTSTRAP_PORT;
public static final String SECURE_URI_BS = COAPS + LWM2M_BOOTSTRAP_HOST + ":" + LWM2MS_BOOTSTRAP_PORT;
public static final Security SECURITY_NO_SEC = noSec(URI, shortServerId);
protected final String OBSERVE_ATTRIBUTES_WITHOUT_PARAMS =
@ -570,12 +586,11 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
this.clientDestroy();
lwM2MTestClient = new LwM2MTestClient(this.executor, endpoint, resources);
try (ServerSocket socket = new ServerSocket(0)) {
int clientPort = socket.getLocalPort();
lwM2MTestClient.init(security, securityBs, clientPort, isRpc,
this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
}
int clientPort = PortFinder.findAvailableUdpPort();
//automatic client port assignment
lwM2MTestClient.init(security, securityBs, 0 /* clientPort */, isRpc,
this.defaultLwM2mUplinkMsgHandlerTest, this.clientContextTest,
clientDtlsCidLength, queueMode, supportFormatOnly_SenMLJSON_SenMLCBOR, value3_0_9);
lwM2MTestClient.setDeviceIdStr(deviceIdStr);
}
@ -658,10 +673,9 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
try {
if (lwM2MTestClient != null && lwM2MTestClient.getLeshanClient() != null) {
boolean serverAlive = false;
for (int port = AbstractLwM2MIntegrationTest.port; port <= securityPortBs; port++) {
try (ServerSocket socket = new ServerSocket(port)) {
log.info("Port {} is free.", port);
} catch (IOException e) {
List<Integer> ports = List.of(LWM2M_PORT, LWM2MS_PORT, LWM2MS_BOOTSTRAP_PORT, LWM2MS_BOOTSTRAP_PORT);
for (Integer port : ports) {
if (!PortFinder.isUDPPortAvailable(port)) {
log.debug("Port {} is busy — CoAP server still active.", port);
serverAlive = true;
break;
@ -730,8 +744,8 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
bootstrapServerCredential.setServerPublicKey("");
bootstrapServerCredential.setShortServerId(isBootstrap ? null : shortServerId);
bootstrapServerCredential.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? portBs : port);
bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? LWM2M_BOOTSTRAP_PORT : LWM2M_PORT);
return bootstrapServerCredential;
}
@ -746,11 +760,15 @@ public abstract class AbstractLwM2MIntegrationTest extends AbstractTransportInte
return credentials;
}
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr) throws Exception {
awaitObserveReadAll(cntObserve, deviceIdStr, "");
}
protected void awaitObserveReadAll(int cntObserve, String deviceIdStr, String assertionAlias) throws Exception {
try {
await("ObserveReadAll: countObserve " + cntObserve)
.atMost(40, TimeUnit.SECONDS)
.alias(assertionAlias)
.atMost(TIMEOUT, TimeUnit.SECONDS)
.until(() -> cntObserve == getCntObserveAll(deviceIdStr));
} catch (ConditionTimeoutException e) {
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.TimeUnit;
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.TITLE;
@ -137,6 +136,7 @@ public class FwLwM2MDevice extends BaseInstanceEnabler implements Destroyable {
@Override
public void destroy() {
scheduler.shutdownNow();
this.leshanClient = null;
}
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.observer.LwM2mClientObserver;
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.ObjectsInitializer;
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.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_RECOMMENDED_CIPHER_SUITES_ONLY;
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.SOFTWARE_MANAGEMENT;
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.Lwm2mTestHelper.BINARY_APP_DATA_CONTAINER;
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.lwm2mClientResources;
import static org.thingsboard.server.transport.lwm2m.utils.LwM2MTransportUtil.setDtlsConnectorConfigCidLength;
import static org.thingsboard.server.utils.PortFinder.isUDPPortAvailable;
@Slf4j
@ -139,14 +141,16 @@ public class LwM2MTestClient {
private Map<LwM2MClientState, Integer> clientDtlsCid;
private LwM2mUplinkMsgHandler defaultLwM2mUplinkMsgHandlerTest;
private LwM2mClientContext clientContext;
private LwM2mTemperatureSensor lwM2mTemperatureSensor12;
private LwM2mTemperatureSensor lwM2MTemperatureSensor12;
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,
LwM2mClientContext clientContext, Integer cIdLength, boolean queueMode,
boolean supportFormatOnly_SenMLJSON_SenMLCBOR, Integer value3_0_9) throws InvalidDDFFileException, IOException {
Assert.assertNull("client already initialized", leshanClient);
this.clientPort = clientPort;
this.defaultLwM2mUplinkMsgHandlerTest = defaultLwM2mUplinkMsgHandler;
this.clientContext = clientContext;
@ -156,7 +160,7 @@ public class LwM2MTestClient {
if (securityLwm2m != null && securityLwm2m.getId() != null) {
forceNullSecurityId(securityLwm2m);
}
if (securityBs!= null && securityBs.getId() != null) {
if (securityBs != null && securityBs.getId() != null) {
forceNullSecurityId(securityBs);
}
if (securityBs != null && securityLwm2m != null) {
@ -165,7 +169,7 @@ public class LwM2MTestClient {
} else if (securityBs != null) {
log.warn("Security BS only: securityBs: [{}] ", securityBs.getId());
initializer.setInstancesForObject(SECURITY, securityBs);
} else if (securityLwm2m != null){
} else if (securityLwm2m != null) {
// SECURITY
log.warn("Security Lwm2m only: security Lwm2m [{}]", securityLwm2m.getId());
initializer.setInstancesForObject(SECURITY, securityLwm2m);
@ -186,8 +190,8 @@ public class LwM2MTestClient {
locationParams.getPos();
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);
lwM2mTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2mTemperatureSensor12);
lwM2MTemperatureSensor12 = new LwM2mTemperatureSensor(executor, OBJECT_INSTANCE_ID_12);
initializer.setInstancesForObject(TEMPERATURE_SENSOR, lwM2mTemperatureSensor0, lwM2MTemperatureSensor12);
List<LwM2mObjectEnabler> enablers = initializer.createAll();
@ -208,7 +212,9 @@ public class LwM2MTestClient {
builder.setSessionListener(new DtlsSessionLogger(clientStates, clientDtlsCid));
return builder;
};
}
;
};
}
};
@ -245,7 +251,7 @@ public class LwM2MTestClient {
// Set Californium Configuration
endpointsBuilder.setConfiguration(clientCoapConfig);
endpointsBuilder.setClientAddress(new InetSocketAddress(port).getAddress());
endpointsBuilder.setClientAddress(new InetSocketAddress(clientPort).getAddress());
// creates EndpointsProvider
@ -438,12 +444,14 @@ public class LwM2MTestClient {
destroySafe(swLwM2MDevice);
destroySafe(lwM2MBinaryAppDataContainer);
destroySafe(lwM2MTemperatureSensor);
destroySafe(lwM2MTemperatureSensor12);
lwM2MDevice = null;
fwLwM2MDevice = null;
swLwM2MDevice = null;
lwM2MBinaryAppDataContainer = null;
lwM2MTemperatureSensor = null;
lwM2MTemperatureSensor12 = null;
}
@ -462,11 +470,17 @@ public class LwM2MTestClient {
public void start(boolean isStartLw) {
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();
if (isStartLw) {
this.awaitClientAfterStartConnectLw();
}
lwM2mTemperatureSensor12.setLeshanClient(leshanClient);
lwM2MTemperatureSensor12.setLeshanClient(leshanClient);
fwLwM2MDevice.setLeshanClient(leshanClient);
}
}
@ -474,6 +488,12 @@ public class LwM2MTestClient {
public void stop(boolean deregister) {
if (leshanClient != null) {
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);
}
}
}

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
public void destroy() {
this.leshanClient = null;
}
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.setBootstrapServerIs(isBootstrap);
bootstrapServerCredential.setHost(isBootstrap ? hostBs : host);
bootstrapServerCredential.setPort(isBootstrap ? securityPortBs : securityPort);
bootstrapServerCredential.setHost(isBootstrap ? LWM2M_BOOTSTRAP_HOST : LWM2M_HOST);
bootstrapServerCredential.setPort(isBootstrap ? LWM2MS_BOOTSTRAP_PORT : LWM2MS_PORT);
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,
true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "before client stops for the first time");
lwM2MTestClient.stop(true);
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/" + lwm2mDevice.getDeviceProfileId().getId().toString(), DeviceProfile.class);
@ -143,7 +143,7 @@ public class PskLwm2mIntegrationTest extends AbstractSecurityLwM2MIntegrationTes
Assert.assertNotNull(lwm2mDeviceProfileManyParams);
lwM2MTestClient.start(true);
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString());
awaitObserveReadAll(1, lwm2mDevice.getId().getId().toString(), "second after client restart");
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 lombok.extern.slf4j.Slf4j;
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.util.TestSocketUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile;
@ -58,6 +61,16 @@ import static org.junit.Assert.assertNotNull;
@Slf4j
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 String gatewayAccessToken;

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

@ -16,12 +16,15 @@
package org.thingsboard.server.transport.mqtt;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.slf4j.Slf4j;
import org.awaitility.Awaitility;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
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.thingsboard.common.util.JacksonUtil;
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.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.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
@DaoSqlTest
@TestPropertySource(properties = {
"service.integrations.supported=ALL",
"transport.mqtt.enabled=true",
})
@Slf4j
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 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 static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_HOST;
import static org.thingsboard.server.transport.mqtt.AbstractMqttIntegrationTest.MQTT_PORT;
public class MqttTestClient {
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds
public static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client;
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 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
private static final String MQTT_URL = "tcp://localhost:1883";
private static final int TIMEOUT = 30; // seconds
private static final long TIMEOUT_MS = TimeUnit.SECONDS.toMillis(TIMEOUT);
private String MQTT_URL = "tcp://" + MQTT_HOST + ":" + MQTT_PORT;
private final MqttAsyncClient client;
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
transport.lwm2m.server.security.credentials.enabled=true
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.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">
<appender-ref ref="console"/>
</root>

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

@ -9,6 +9,9 @@
<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">
<appender-ref ref="console"/>
</root>

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

@ -16,6 +16,7 @@
package org.thingsboard.server.edqs.util;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.rocksdb.Options;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksIterator;
@ -26,6 +27,7 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.util.function.BiConsumer;
@Slf4j
public class TbRocksDb {
protected final String path;
@ -45,6 +47,7 @@ public class TbRocksDb {
@SneakyThrows
public void init() {
log.debug("RocksDB init in {}", path);
Files.createDirectories(Path.of(path).getParent());
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.google.common.util.concurrent.FluentFuture;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
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));
}
@PreDestroy
private void destroy() {
cache.synchronous().invalidateAll();
cache = null;
}
@Override
public FluentFuture<TbResourceDataInfo> getResourceDataInfoAsync(TenantId tenantId, TbResourceId resourceId) {
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
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:}")
private String javaCacertsPath;
@ -371,9 +383,9 @@ public class DeviceProfileDataValidator extends AbstractHasOtaPackageValidator<D
}
int port;
if (LwM2MSecurityMode.NO_SEC.equals(serverConfig.getSecurityMode())) {
port = serverConfig.isBootstrapServerIs() ? 5687 : 5685;
port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapPort : lwm2mPort;
} else {
port = serverConfig.isBootstrapServerIs() ? 5688 : 5686;
port = serverConfig.isBootstrapServerIs() ? lwm2mBootstrapSecurePort : lwm2mSecurePort;
}
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 + "!");

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

@ -1,3 +1,4 @@
spring.main.banner-mode=off
zk.enabled=false
zk.url=localhost:2181
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.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">
<appender-ref ref="console"/>
</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{
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;
@ -54,7 +54,7 @@ public abstract class AbstractCoapClientTest extends AbstractContainerTest{
protected byte[] createCoapClientAndPublish(String deviceName) throws Exception {
String provisionRequestMsg = createTestProvisionMessage(deviceName);
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);
return client.setTimeout(CLIENT_REQUEST_TIMEOUT)
.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.DeviceCredentialsType;
import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.PortFinder;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.LwM2MTestClient;
import org.thingsboard.server.msa.connectivity.lwm2m.client.Lwm2mTestHelper.LwM2MClientState;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import java.net.ServerSocket;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.ArrayList;
@ -229,10 +229,8 @@ public class AbstractLwm2mClientTest extends AbstractContainerTest {
String endpoint, ScheduledExecutorService executor) throws Exception {
this.executor = executor;
LwM2MTestClient lwM2MTestClient = new LwM2MTestClient(executor, endpoint);
try (ServerSocket socket = new ServerSocket(0)) {
int clientPort = socket.getLocalPort();
lwM2MTestClient.init(security, clientPort);
}
int clientPort = PortFinder.findAvailableUdpPort();
lwM2MTestClient.init(security, clientPort);
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 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_RECOMMENDED_CIPHER_SUITES_ONLY;
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.SECURITY;
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.LwM2MClientState.ON_BOOTSTRAP_FAILURE;
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.shortServerId;
@Slf4j
@Data
public class LwM2MTestClient {
@ -115,8 +116,11 @@ public class LwM2MTestClient {
private int countUpdateRegistrationSuccess;
private int countReadObserveAfterUpdateRegistrationSuccess;
private int clientPort;
public void init(Security security, int clientPort) throws InvalidDDFFileException, IOException {
assertThat(leshanClient).as("client already initialized").isNull();
this.clientPort = clientPort;
List<ObjectModel> models = new ArrayList<>();
for (String resourceName : resources) {
@ -342,12 +346,27 @@ public class LwM2MTestClient {
}
});
}
public void 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() {
if (leshanClient != null) {
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) {
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 {
private static final String TOPIC = "tb/mqtt/device";
private static final String CONTAINER_MQTT_URL = "tcp://localhost:1883";
private Device device;
@ -94,7 +95,7 @@ public class MqttNodeTest extends AbstractContainerTest {
responseClient.connect();
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.setUserName(deviceCredentials.getCredentialsId());
mqttClient.connect(mqttConnectOptions);

21
pom.xml

@ -69,7 +69,7 @@
<paho.client.version>1.2.5</paho.client.version>
<paho.mqttv5.client.version>1.2.5</paho.mqttv5.client.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>
<springdoc-swagger.version>2.8.8TB</springdoc-swagger.version>
<swagger-annotations.version>2.2.30</swagger-annotations.version>
@ -127,7 +127,7 @@
<jeasy.version>5.0.0</jeasy.version>
<!-- BLACKBOX TEST SCOPE -->
<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>
<zeroturnaround.version>1.12</zeroturnaround.version>
<webdrivermanager.version>6.1.0</webdrivermanager.version>
@ -172,6 +172,16 @@
<activeByDefault>true</activeByDefault>
</activation>
</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 -->
<!-- mvn package -Pdownload-dependencies -Dclassifier=sources dependency:copy-dependencies -->
<profile>
@ -662,8 +672,11 @@
<version>${surefire.version}</version>
<configuration>
<argLine>
-XX:+UseStringDeduplication -XX:MaxGCPauseMillis=20
--add-opens java.base/java.lang.reflect=ALL-UNNAMED
-XX:+UseStringDeduplication -XX:MaxGCPauseMillis=200
--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>
</configuration>
</plugin>

Loading…
Cancel
Save