From bcfe4c26417d09a55d33d11e8b0879dd50de5a5b Mon Sep 17 00:00:00 2001 From: Sergey Matvienko Date: Thu, 22 Jan 2026 03:18:48 +0100 Subject: [PATCH] tests: coap transport tests refactored to use dynamic UPD ports to be able to test in parallel --- .../AbstractTransportIntegrationTest.java | 2 -- .../coap/AbstractCoapIntegrationTest.java | 14 ++++++++ .../server/transport/coap/CoapTestClient.java | 3 +- .../AbstractCoapSecurityIntegrationTest.java | 36 ++++++++++++------- .../server/msa/AbstractCoapClientTest.java | 4 +-- 5 files changed, 42 insertions(+), 17 deletions(-) diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java index bc06d81588..b0a1d21992 100644 --- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java @@ -30,8 +30,6 @@ public abstract class AbstractTransportIntegrationTest extends AbstractControlle protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30; - protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/"; - protected static final AtomicInteger atomicInteger = new AtomicInteger(2); public static final String DEVICE_TELEMETRY_PROTO_SCHEMA = "syntax =\"proto3\";\n" + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java index 7240ab0afa..cf23a5be97 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java +++ b/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.info("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," + diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java b/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java index ce72d68fc6..df7e9babc9 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/CoapTestClient.java +++ b/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; diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java index cfb449aa33..9c42d87369 100644 --- a/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java +++ b/application/src/test/java/org/thingsboard/server/transport/coap/security/AbstractCoapSecurityIntegrationTest.java @@ -21,7 +21,10 @@ 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.springframework.test.util.TestSocketUtils; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.CoapDeviceType; import org.thingsboard.server.common.data.Device; @@ -37,9 +40,11 @@ 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.DatagramSocket; import java.net.ServerSocket; import java.security.GeneralSecurityException; import java.security.KeyStore; @@ -70,7 +75,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.info("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,17 +175,20 @@ 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)); + .until(() -> isUDPPortAvailable(fixedPort)); CoapClientX509Test clientX509_01 = clientX509UpdateTest(FeatureType.ATTRIBUTES, certPrivateKey_01, "CoapX509TrustNo_" + FeatureType.TELEMETRY.name() + "_01", deviceProfile.getId(), fixedPort, PAYLOAD_VALUES_STR_01); clientX509_01.disconnect(); + await("Await to make port " + fixedPort + " free") + .atMost(40, TimeUnit.SECONDS) + .until(() -> isUDPPortAvailable(fixedPort)); } private CoapClientX509Test clientX509UpdateTest(FeatureType featureType, CertPrivateKey certPrivateKey, @@ -274,15 +292,9 @@ 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); + private static boolean isUDPPortAvailable(int port) { + try (DatagramSocket socket = new DatagramSocket(port)) { + socket.setReuseAddress(true); return true; } catch (IOException e) { return false; diff --git a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java b/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java index d4d9d08474..e409a82600 100644 --- a/msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractCoapClientTest.java +++ b/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)