Browse Source

Merge pull request #7566 from dashevchenko/testRefactoring

[3.4.2] Moved black-box tests to TestNG
pull/7646/head
Andrew Shvayka 4 years ago
committed by GitHub
parent
commit
83a517cf71
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      msa/black-box-tests/README.md
  2. 38
      msa/black-box-tests/pom.xml
  3. 130
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
  4. 221
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
  5. 53
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java
  6. 61
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java
  7. 262
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java
  8. 9
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java
  9. 112
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
  10. 226
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java
  11. 220
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java
  12. 40
      msa/black-box-tests/src/test/java/org/thingsboard/server/msa/prototypes/DevicePrototypes.java
  13. 2
      msa/black-box-tests/src/test/resources/config.properties
  14. 27
      msa/black-box-tests/src/test/resources/testNG.xml
  15. 28
      pom.xml

4
msa/black-box-tests/README.md

@ -30,5 +30,9 @@ As result, in REPOSITORY column, next images should be present:
mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.hybridMode=true mvn clean install -DblackBoxTests.skip=false -DblackBoxTests.hybridMode=true
To run the black box tests with using local env run tests in the [msa/black-box-tests](../black-box-tests) directory with runLocal property:
mvn clean install -DblackBoxTests.skip=false -DrunLocal=true

38
msa/black-box-tests/pom.xml

@ -57,11 +57,6 @@
<artifactId>httpclient</artifactId> <artifactId>httpclient</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>io.takari.junit</groupId>
<artifactId>takari-cpsuite</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
@ -72,6 +67,26 @@
<artifactId>junit-vintage-engine</artifactId> <artifactId>junit-vintage-engine</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.awaitility</groupId> <groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId> <artifactId>awaitility</artifactId>
@ -152,11 +167,18 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>
<configuration> <configuration>
<includes> <suiteXmlFiles>
<include>**/*TestSuite.java</include> <suiteXmlFile>src/test/resources/testNG.xml</suiteXmlFile>
</includes> </suiteXmlFiles>
<skipTests>${blackBoxTests.skip}</skipTests> <skipTests>${blackBoxTests.skip}</skipTests>
</configuration> </configuration>
<dependencies>
<dependency>
<groupId>org.apache.maven.surefire</groupId>
<artifactId>surefire-testng</artifactId>
<version>${surefire.version}</version>
</dependency>
</dependencies>
</plugin> </plugin>
</plugins> </plugins>
</build> </build>

130
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java

@ -15,115 +15,59 @@
*/ */
package org.thingsboard.server.msa; package org.thingsboard.server.msa;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray; import com.google.gson.JsonArray;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContextBuilder; import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.ssl.SSLContexts; import org.apache.http.ssl.SSLContexts;
import org.junit.BeforeClass; import org.testng.annotations.AfterSuite;
import org.junit.Rule; import org.testng.annotations.BeforeSuite;
import org.junit.rules.TestRule; import org.testng.annotations.Listeners;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.thingsboard.rest.client.RestClient;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import javax.net.ssl.SSLContext;
import java.net.URI; import java.net.URI;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
@Slf4j @Slf4j
@Listeners(TestListener.class)
public abstract class AbstractContainerTest { public abstract class AbstractContainerTest {
protected static final String HTTPS_URL = "https://localhost";
protected static final String WSS_URL = "wss://localhost";
protected static String TB_TOKEN;
protected static RestClient restClient;
protected static long timeoutMultiplier = 1; protected static long timeoutMultiplier = 1;
protected ObjectMapper mapper = new ObjectMapper(); protected ObjectMapper mapper = new ObjectMapper();
protected JsonParser jsonParser = new JsonParser(); private static final ContainerTestSuite containerTestSuite = ContainerTestSuite.getInstance();
protected static TestRestClient testRestClient;
@BeforeClass
public static void before() throws Exception {
restClient = new RestClient(HTTPS_URL);
restClient.getRestTemplate().setRequestFactory(getRequestFactoryForSelfSignedCert());
@BeforeSuite
public void beforeSuite() {
if ("false".equals(System.getProperty("runLocal", "false"))) {
containerTestSuite.start();
}
testRestClient = new TestRestClient(TestProperties.getBaseUrl());
if (!"kafka".equals(System.getProperty("blackBoxTests.queue", "kafka"))) { if (!"kafka".equals(System.getProperty("blackBoxTests.queue", "kafka"))) {
timeoutMultiplier = 10; timeoutMultiplier = 10;
} }
} }
@Rule @AfterSuite
public TestRule watcher = new TestWatcher() { public void afterSuite() {
protected void starting(Description description) { if (containerTestSuite.isActive()) {
log.info("================================================="); containerTestSuite.stop();
log.info("STARTING TEST: {}" , description.getMethodName());
log.info("=================================================");
} }
/**
* Invoked when a test succeeds
*/
protected void succeeded(Description description) {
log.info("=================================================");
log.info("SUCCEEDED TEST: {}" , description.getMethodName());
log.info("=================================================");
}
/**
* Invoked when a test fails
*/
protected void failed(Throwable e, Description description) {
log.info("=================================================");
log.info("FAILED TEST: {}" , description.getMethodName(), e);
log.info("=================================================");
}
};
protected Device createGatewayDevice() throws JsonProcessingException {
String isGateway = "{\"gateway\":true}";
ObjectMapper objectMapper = new ObjectMapper();
JsonNode additionalInfo = objectMapper.readTree(isGateway);
Device gatewayDeviceTemplate = new Device();
gatewayDeviceTemplate.setName("mqtt_gateway");
gatewayDeviceTemplate.setType("gateway");
gatewayDeviceTemplate.setAdditionalInfo(additionalInfo);
return restClient.saveDevice(gatewayDeviceTemplate);
}
protected Device createDevice(String name) {
Device device = new Device();
device.setName(name + StringUtils.randomAlphanumeric(7));
device.setType("DEFAULT");
return restClient.saveDevice(device);
} }
protected WsClient subscribeToWebSocket(DeviceId deviceId, String scope, CmdsType property) throws Exception { protected WsClient subscribeToWebSocket(DeviceId deviceId, String scope, CmdsType property) throws Exception {
WsClient wsClient = new WsClient(new URI(WSS_URL + "/api/ws/plugins/telemetry?token=" + restClient.getToken()), timeoutMultiplier); String webSocketUrl = TestProperties.getWebSocketUrl();
SSLContextBuilder builder = SSLContexts.custom(); WsClient wsClient = new WsClient(new URI(webSocketUrl + "/api/ws/plugins/telemetry?token=" + testRestClient.getToken()), timeoutMultiplier);
builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true); if (webSocketUrl.matches("^(wss)://.*$")) {
wsClient.setSocketFactory(builder.build().getSocketFactory()); SSLContextBuilder builder = SSLContexts.custom();
builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
wsClient.setSocketFactory(builder.build().getSocketFactory());
}
wsClient.connectBlocking(); wsClient.connectBlocking();
JsonObject cmdsObject = new JsonObject(); JsonObject cmdsObject = new JsonObject();
@ -150,16 +94,6 @@ public abstract class AbstractContainerTest {
.build(); .build();
} }
protected boolean verify(WsTelemetryResponse wsTelemetryResponse, String key, Long expectedTs, String expectedValue) {
List<Object> list = wsTelemetryResponse.getDataValuesByKey(key);
return expectedTs.equals(list.get(0)) && expectedValue.equals(list.get(1));
}
protected boolean verify(WsTelemetryResponse wsTelemetryResponse, String key, String expectedValue) {
List<Object> list = wsTelemetryResponse.getDataValuesByKey(key);
return expectedValue.equals(list.get(1));
}
protected JsonObject createGatewayConnectPayload(String deviceName){ protected JsonObject createGatewayConnectPayload(String deviceName){
JsonObject payload = new JsonObject(); JsonObject payload = new JsonObject();
payload.addProperty("device", deviceName); payload.addProperty("device", deviceName);
@ -216,20 +150,4 @@ public abstract class AbstractContainerTest {
} }
} }
private static HttpComponentsClientHttpRequestFactory getRequestFactoryForSelfSignedCert() throws Exception {
SSLContextBuilder builder = SSLContexts.custom();
builder.loadTrustMaterial(null, (TrustStrategy) (chain, authType) -> true);
SSLContext sslContext = builder.build();
SSLConnectionSocketFactory sslSelfSigned = new SSLConnectionSocketFactory(sslContext, (s, sslSession) -> true);
Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder
.<ConnectionSocketFactory>create()
.register("https", sslSelfSigned)
.build();
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
} }

221
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java

@ -17,9 +17,6 @@ package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils; import org.apache.commons.io.FileUtils;
import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.testcontainers.containers.DockerComposeContainer; import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.wait.strategy.Wait; import org.testcontainers.containers.wait.strategy.Wait;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
@ -41,10 +38,8 @@ import static org.hamcrest.CoreMatchers.containsString;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.fail; import static org.testng.Assert.fail;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.msa.*Test"})
@Slf4j @Slf4j
public class ContainerTestSuite { public class ContainerTestSuite {
final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster"));
@ -57,107 +52,133 @@ public class ContainerTestSuite {
private static final String TB_JS_EXECUTOR_LOG_REGEXP = ".*template started.*"; private static final String TB_JS_EXECUTOR_LOG_REGEXP = ".*template started.*";
private static final Duration CONTAINER_STARTUP_TIMEOUT = Duration.ofSeconds(400); private static final Duration CONTAINER_STARTUP_TIMEOUT = Duration.ofSeconds(400);
private static DockerComposeContainer<?> testContainer; private DockerComposeContainer<?> testContainer;
private ThingsBoardDbInstaller installTb;
@ClassRule private boolean isActive;
public static ThingsBoardDbInstaller installTb = new ThingsBoardDbInstaller();
private static ContainerTestSuite containerTestSuite;
@ClassRule
public static DockerComposeContainer getTestContainer() { public boolean isActive() {
if (testContainer == null) { return isActive;
log.info("System property of blackBoxTests.redisCluster is {}", IS_REDIS_CLUSTER); }
log.info("System property of blackBoxTests.hybridMode is {}", IS_HYBRID_MODE);
boolean skipTailChildContainers = Boolean.valueOf(System.getProperty("blackBoxTests.skipTailChildContainers")); public void setActive(boolean active) {
try { isActive = active;
final String targetDir = FileUtils.getTempDirectoryPath() + "/" + "ContainerTestSuite-" + UUID.randomUUID() + "/"; }
log.info("targetDir {}", targetDir);
FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir)); private ContainerTestSuite() {
replaceInFile(targetDir + "docker-compose.yml", " container_name: \"${LOAD_BALANCER_NAME}\"", "", "container_name"); }
FileUtils.copyDirectory(new File("src/test/resources"), new File(targetDir)); public static ContainerTestSuite getInstance() {
if (containerTestSuite == null) {
class DockerComposeContainerImpl<SELF extends DockerComposeContainer<SELF>> extends DockerComposeContainer<SELF> { containerTestSuite = new ContainerTestSuite();
public DockerComposeContainerImpl(List<File> composeFiles) { }
super(composeFiles); return containerTestSuite;
} }
@Override public void start() {
public void stop() { installTb = new ThingsBoardDbInstaller();
super.stop(); installTb.createVolumes();
tryDeleteDir(targetDir); log.info("System property of blackBoxTests.redisCluster is {}", IS_REDIS_CLUSTER);
} log.info("System property of blackBoxTests.hybridMode is {}", IS_HYBRID_MODE);
} boolean skipTailChildContainers = Boolean.valueOf(System.getProperty("blackBoxTests.skipTailChildContainers"));
try {
final String targetDir = FileUtils.getTempDirectoryPath() + "/" + "ContainerTestSuite-" + UUID.randomUUID() + "/";
log.info("targetDir {}", targetDir);
FileUtils.copyDirectory(new File(SOURCE_DIR), new File(targetDir));
replaceInFile(targetDir + "docker-compose.yml", " container_name: \"${LOAD_BALANCER_NAME}\"", "", "container_name");
List<File> composeFiles = new ArrayList<>(Arrays.asList( FileUtils.copyDirectory(new File("src/test/resources"), new File(targetDir));
new File(targetDir + "docker-compose.yml"),
new File(targetDir + "docker-compose.volumes.yml"), class DockerComposeContainerImpl<SELF extends DockerComposeContainer<SELF>> extends DockerComposeContainer<SELF> {
new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid.yml" : "docker-compose.postgres.yml")), public DockerComposeContainerImpl(List<File> composeFiles) {
new File(targetDir + "docker-compose.postgres.volumes.yml"), super(composeFiles);
new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"),
new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")),
new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml"))
));
Map<String, String> queueEnv = new HashMap<>();
queueEnv.put("TB_QUEUE_TYPE", QUEUE_TYPE);
switch (QUEUE_TYPE) {
case "kafka":
composeFiles.add(new File(targetDir + "docker-compose.kafka.yml"));
break;
case "aws-sqs":
replaceInFile(targetDir, "queue-aws-sqs.env",
Map.of("YOUR_KEY", getSysProp("blackBoxTests.awsKey"),
"YOUR_SECRET", getSysProp("blackBoxTests.awsSecret"),
"YOUR_REGION", getSysProp("blackBoxTests.awsRegion")));
break;
case "rabbitmq":
composeFiles.add(new File(targetDir + "docker-compose.rabbitmq-server.yml"));
replaceInFile(targetDir, "queue-rabbitmq.env",
Map.of("localhost", "rabbitmq"));
break;
case "service-bus":
replaceInFile(targetDir, "queue-service-bus.env",
Map.of("YOUR_NAMESPACE_NAME", getSysProp("blackBoxTests.serviceBusNamespace"),
"YOUR_SAS_KEY_NAME", getSysProp("blackBoxTests.serviceBusSASPolicy")));
replaceInFile(targetDir, "queue-service-bus.env",
Map.of("YOUR_SAS_KEY", getSysProp("blackBoxTests.serviceBusPrimaryKey")));
break;
case "pubsub":
replaceInFile(targetDir, "queue-pubsub.env",
Map.of("YOUR_PROJECT_ID", getSysProp("blackBoxTests.pubSubProjectId"),
"YOUR_SERVICE_ACCOUNT", getSysProp("blackBoxTests.pubSubServiceAccount")));
break;
default:
throw new RuntimeException("Unsupported queue type: " + QUEUE_TYPE);
} }
if (IS_HYBRID_MODE) { @Override
composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml")); public void stop() {
super.stop();
tryDeleteDir(targetDir);
} }
}
testContainer = new DockerComposeContainerImpl<>(composeFiles) List<File> composeFiles = new ArrayList<>(Arrays.asList(
.withPull(false) new File(targetDir + "docker-compose.yml"),
.withLocalCompose(true) new File(targetDir + "docker-compose.volumes.yml"),
.withTailChildContainers(!skipTailChildContainers) new File(targetDir + (IS_HYBRID_MODE ? "docker-compose.hybrid.yml" : "docker-compose.postgres.yml")),
.withEnv(installTb.getEnv()) new File(targetDir + "docker-compose.postgres.volumes.yml"),
.withEnv(queueEnv) new File(targetDir + "docker-compose." + QUEUE_TYPE + ".yml"),
.withEnv("LOAD_BALANCER_NAME", "") new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.yml" : "docker-compose.redis.yml")),
.withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) new File(targetDir + (IS_REDIS_CLUSTER ? "docker-compose.redis-cluster.volumes.yml" : "docker-compose.redis.volumes.yml"))
.waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) ));
.waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) Map<String, String> queueEnv = new HashMap<>();
.waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) queueEnv.put("TB_QUEUE_TYPE", QUEUE_TYPE);
.waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) switch (QUEUE_TYPE) {
.waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) case "kafka":
.waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) composeFiles.add(new File(targetDir + "docker-compose.kafka.yml"));
.waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)) break;
.waitingFor("tb-js-executor", Wait.forLogMessage(TB_JS_EXECUTOR_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT)); case "aws-sqs":
} catch (Exception e) { replaceInFile(targetDir, "queue-aws-sqs.env",
log.error("Failed to create test container", e); Map.of("YOUR_KEY", getSysProp("blackBoxTests.awsKey"),
fail("Failed to create test container"); "YOUR_SECRET", getSysProp("blackBoxTests.awsSecret"),
"YOUR_REGION", getSysProp("blackBoxTests.awsRegion")));
break;
case "rabbitmq":
composeFiles.add(new File(targetDir + "docker-compose.rabbitmq-server.yml"));
replaceInFile(targetDir, "queue-rabbitmq.env",
Map.of("localhost", "rabbitmq"));
break;
case "service-bus":
replaceInFile(targetDir, "queue-service-bus.env",
Map.of("YOUR_NAMESPACE_NAME", getSysProp("blackBoxTests.serviceBusNamespace"),
"YOUR_SAS_KEY_NAME", getSysProp("blackBoxTests.serviceBusSASPolicy")));
replaceInFile(targetDir, "queue-service-bus.env",
Map.of("YOUR_SAS_KEY", getSysProp("blackBoxTests.serviceBusPrimaryKey")));
break;
case "pubsub":
replaceInFile(targetDir, "queue-pubsub.env",
Map.of("YOUR_PROJECT_ID", getSysProp("blackBoxTests.pubSubProjectId"),
"YOUR_SERVICE_ACCOUNT", getSysProp("blackBoxTests.pubSubServiceAccount")));
break;
default:
throw new RuntimeException("Unsupported queue type: " + QUEUE_TYPE);
} }
if (IS_HYBRID_MODE) {
composeFiles.add(new File(targetDir + "docker-compose.cassandra.volumes.yml"));
}
testContainer = new DockerComposeContainerImpl<>(composeFiles)
.withPull(false)
.withLocalCompose(true)
.withTailChildContainers(!skipTailChildContainers)
.withEnv(installTb.getEnv())
.withEnv(queueEnv)
.withEnv("LOAD_BALANCER_NAME", "")
.withExposedService("haproxy", 80, Wait.forHttp("/swagger-ui.html").withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-core1", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-core2", Wait.forLogMessage(TB_CORE_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-http-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-http-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-mqtt-transport1", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-mqtt-transport2", Wait.forLogMessage(TRANSPORTS_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-vc-executor1", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-vc-executor2", Wait.forLogMessage(TB_VC_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT))
.waitingFor("tb-js-executor", Wait.forLogMessage(TB_JS_EXECUTOR_LOG_REGEXP, 1).withStartupTimeout(CONTAINER_STARTUP_TIMEOUT));
testContainer.start();
setActive(true);
} catch (Exception e) {
log.error("Failed to create test container", e);
fail("Failed to create test container");
}
}
public void stop() {
if (isActive) {
testContainer.stop();
installTb.savaLogsAndRemoveVolumes();
setActive(false);
} }
return testContainer;
} }
private static void replaceInFile(String targetDir, String fileName, Map<String, String> replacements) throws IOException { private static void replaceInFile(String targetDir, String fileName, Map<String, String> replacements) throws IOException {

53
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestListener.java

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j;
import org.testng.ITestContext;
import org.testng.ITestResult;
import org.testng.TestListenerAdapter;
import static org.testng.internal.Utils.log;
@Slf4j
public class TestListener extends TestListenerAdapter {
@Override
public void onTestStart(ITestResult result) {
super.onTestStart(result);
log.info("===>>> Test started: " + result.getName());
}
/**
* Invoked when a test succeeds
*/
@Override
public void onTestSuccess(ITestResult result) {
super.onTestSuccess(result);
if (result != null) {
log.info("<<<=== Test completed successfully: " + result.getName());
}
}
/**
* Invoked when a test fails
*/
@Override
public void onTestFailure(ITestResult result) {
super.onTestFailure(result);
log.info("<<<=== Test failed: " + result.getName());
}
}

61
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestProperties.java

@ -0,0 +1,61 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
@Slf4j
public class TestProperties {
private static final String HTTPS_URL = "https://localhost";
private static final String WSS_URL = "wss://localhost";
private static final ContainerTestSuite instance = ContainerTestSuite.getInstance();
private static Properties properties;
public static String getBaseUrl() {
if (instance.isActive()) {
return HTTPS_URL;
}
return getProperties().getProperty("tb.baseUrl");
}
public static String getWebSocketUrl() {
if (instance.isActive()) {
return WSS_URL;
}
return getProperties().getProperty("tb.wsUrl");
}
private static Properties getProperties() {
if (properties == null) {
try (InputStream input = TestProperties.class.getClassLoader().getResourceAsStream("config.properties")) {
properties = new Properties();
properties.load(input);
} catch (IOException ex) {
log.error("Exception while reading test properties " + ex.getMessage());
}
}
return properties;
}
}

262
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/TestRestClient.java

@ -0,0 +1,262 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.msa;
import com.fasterxml.jackson.databind.JsonNode;
import io.restassured.RestAssured;
import io.restassured.common.mapper.TypeRef;
import io.restassured.config.HeaderConfig;
import io.restassured.config.RestAssuredConfig;
import io.restassured.filter.log.RequestLoggingFilter;
import io.restassured.filter.log.ResponseLoggingFilter;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import io.restassured.response.ValidatableResponse;
import io.restassured.specification.RequestSpecification;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.relation.EntityRelation;
import org.thingsboard.server.common.data.relation.RelationTypeGroup;
import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static io.restassured.RestAssured.given;
import static java.net.HttpURLConnection.HTTP_NOT_FOUND;
import static java.net.HttpURLConnection.HTTP_OK;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.AnyOf.anyOf;
import static org.thingsboard.server.common.data.StringUtils.isEmpty;
public class TestRestClient {
private static final String JWT_TOKEN_HEADER_PARAM = "X-Authorization";
private static final String CONTENT_TYPE_HEADER = "Content-Type";
private final RequestSpecification requestSpec;
private String token;
private String refreshToken;
public TestRestClient(String url) {
RestAssured.filters(new RequestLoggingFilter(), new ResponseLoggingFilter());
requestSpec = given().baseUri(url)
.contentType(ContentType.JSON)
.config(RestAssuredConfig.config()
.headerConfig(HeaderConfig.headerConfig()
.overwriteHeadersWithName(JWT_TOKEN_HEADER_PARAM, CONTENT_TYPE_HEADER)));
if (url.matches("^(https)://.*$")) {
requestSpec.relaxedHTTPSValidation();
}
}
public void login(String username, String password) {
Map<String, String> loginRequest = new HashMap<>();
loginRequest.put("username", username);
loginRequest.put("password", password);
JsonPath jsonPath = given().spec(requestSpec).body(loginRequest)
.post( "/api/auth/login")
.getBody().jsonPath();
token = jsonPath.get("token");
refreshToken = jsonPath.get("refreshToken");
requestSpec.header(JWT_TOKEN_HEADER_PARAM, "Bearer " + token);
}
public Device postDevice(String accessToken, Device device) {
return given().spec(requestSpec).body(device)
.pathParams("accessToken", accessToken)
.post("/api/device?accessToken={accessToken}")
.then()
.statusCode(HTTP_OK)
.extract()
.as(Device.class);
}
public ValidatableResponse getDeviceById(DeviceId deviceId, int statusCode) {
return given().spec(requestSpec)
.pathParams("deviceId", deviceId.getId())
.get("/api/device/{deviceId}")
.then()
.statusCode(statusCode);
}
public Device getDeviceById(DeviceId deviceId) {
return getDeviceById(deviceId, HTTP_OK)
.extract()
.as(Device.class);
}
public DeviceCredentials getDeviceCredentialsByDeviceId(DeviceId deviceId) {
return given().spec(requestSpec).get("/api/device/{deviceId}/credentials", deviceId.getId())
.then()
.assertThat()
.statusCode(HTTP_OK)
.extract()
.as(DeviceCredentials.class);
}
public ValidatableResponse postTelemetry(String credentialsId, JsonNode telemetry) {
return given().spec(requestSpec).body(telemetry)
.post("/api/v1/{credentialsId}/telemetry", credentialsId)
.then()
.statusCode(HTTP_OK);
}
public ValidatableResponse deleteDevice(DeviceId deviceId) {
return given().spec(requestSpec)
.delete("/api/device/{deviceId}", deviceId.getId())
.then()
.statusCode(HTTP_OK);
}
public ValidatableResponse deleteDeviceIfExists(DeviceId deviceId) {
return given().spec(requestSpec)
.delete("/api/device/{deviceId}", deviceId.getId())
.then()
.statusCode(anyOf(is(HTTP_OK),is(HTTP_NOT_FOUND)));
}
public ValidatableResponse postTelemetryAttribute(String entityType, DeviceId deviceId, String scope, JsonNode attribute) {
return given().spec(requestSpec).body(attribute)
.post("/api/plugins/telemetry/{entityType}/{entityId}/attributes/{scope}", entityType, deviceId.getId(), scope)
.then()
.statusCode(HTTP_OK);
}
public ValidatableResponse postAttribute(String accessToken, JsonNode attribute) {
return given().spec(requestSpec).body(attribute)
.post("/api/v1/{accessToken}/attributes/", accessToken)
.then()
.statusCode(HTTP_OK);
}
public JsonNode getAttributes(String accessToken, String clientKeys, String sharedKeys) {
return given().spec(requestSpec)
.queryParam("clientKeys", clientKeys)
.queryParam("sharedKeys", sharedKeys)
.get("/api/v1/{accessToken}/attributes", accessToken)
.then()
.statusCode(HTTP_OK)
.extract()
.as(JsonNode.class);
}
public PageData<RuleChain> getRuleChains(PageLink pageLink) {
Map<String, String> params = new HashMap<>();
addPageLinkToParam(params, pageLink);
return given().spec(requestSpec).queryParams(params)
.get("/api/ruleChains")
.then()
.statusCode(HTTP_OK)
.extract()
.as(new TypeRef<PageData<RuleChain>>() {});
}
public RuleChain postRootRuleChain(RuleChain ruleChain) {
return given().spec(requestSpec)
.body(ruleChain)
.post("/api/ruleChain")
.then()
.statusCode(HTTP_OK)
.extract()
.as(RuleChain.class);
}
public RuleChainMetaData postRuleChainMetadata(RuleChainMetaData ruleChainMetaData) {
return given().spec(requestSpec)
.body(ruleChainMetaData)
.post("/api/ruleChain/metadata")
.then()
.statusCode(HTTP_OK)
.extract()
.as(RuleChainMetaData.class);
}
public void setRootRuleChain(RuleChainId ruleChainId) {
given().spec(requestSpec)
.post("/api/ruleChain/{ruleChainId}/root", ruleChainId.getId())
.then()
.statusCode(HTTP_OK);
}
public void deleteRuleChain(RuleChainId ruleChainId) {
given().spec(requestSpec)
.delete("/api/ruleChain/{ruleChainId}", ruleChainId.getId())
.then()
.statusCode(HTTP_OK);
}
private String getUrlParams(PageLink pageLink) {
String urlParams = "pageSize={pageSize}&page={page}";
if (!isEmpty(pageLink.getTextSearch())) {
urlParams += "&textSearch={textSearch}";
}
if (pageLink.getSortOrder() != null) {
urlParams += "&sortProperty={sortProperty}&sortOrder={sortOrder}";
}
return urlParams;
}
private void addPageLinkToParam(Map<String, String> params, PageLink pageLink) {
params.put("pageSize", String.valueOf(pageLink.getPageSize()));
params.put("page", String.valueOf(pageLink.getPage()));
if (!isEmpty(pageLink.getTextSearch())) {
params.put("textSearch", pageLink.getTextSearch());
}
if (pageLink.getSortOrder() != null) {
params.put("sortProperty", pageLink.getSortOrder().getProperty());
params.put("sortOrder", pageLink.getSortOrder().getDirection().name());
}
}
public List<EntityRelation> findRelationByFrom(EntityId fromId, RelationTypeGroup relationTypeGroup) {
Map<String, String> params = new HashMap<>();
params.put("fromId", fromId.getId().toString());
params.put("fromType", fromId.getEntityType().name());
params.put("relationTypeGroup", relationTypeGroup.name());
return given().spec(requestSpec)
.pathParams(params)
.get("/api/relations?fromId={fromId}&fromType={fromType}&relationTypeGroup={relationTypeGroup}")
.then()
.statusCode(HTTP_OK)
.extract()
.as(new TypeRef<List<EntityRelation>>() {});
}
public JsonNode postServerSideRpc(DeviceId deviceId, JsonNode serverRpcPayload) {
return given().spec(requestSpec)
.body(serverRpcPayload)
.post("/api/rpc/twoway/{deviceId}", deviceId.getId())
.then()
.statusCode(HTTP_OK)
.extract()
.as(JsonNode.class);
}
public String getToken() {
return token;
}
public String getRefreshToken() {
return refreshToken;
}
}

9
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/ThingsBoardDbInstaller.java

@ -16,7 +16,6 @@
package org.thingsboard.server.msa; package org.thingsboard.server.msa;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.rules.ExternalResource;
import org.testcontainers.utility.Base58; import org.testcontainers.utility.Base58;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
@ -30,7 +29,7 @@ import java.util.stream.Collectors;
import java.util.stream.IntStream; import java.util.stream.IntStream;
@Slf4j @Slf4j
public class ThingsBoardDbInstaller extends ExternalResource { public class ThingsBoardDbInstaller {
final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster")); final static boolean IS_REDIS_CLUSTER = Boolean.parseBoolean(System.getProperty("blackBoxTests.redisCluster"));
final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode")); final static boolean IS_HYBRID_MODE = Boolean.parseBoolean(System.getProperty("blackBoxTests.hybridMode"));
@ -129,8 +128,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
return env; return env;
} }
@Override public void createVolumes() {
protected void before() throws Throwable {
try { try {
dockerCompose.withCommand("volume create " + postgresDataVolume); dockerCompose.withCommand("volume create " + postgresDataVolume);
@ -192,8 +190,7 @@ public class ThingsBoardDbInstaller extends ExternalResource {
} }
} }
@Override public void savaLogsAndRemoveVolumes() {
protected void after() {
copyLogs(tbLogVolume, "./target/tb-logs/"); copyLogs(tbLogVolume, "./target/tb-logs/");
copyLogs(tbCoapTransportLogVolume, "./target/tb-coap-transport-logs/"); copyLogs(tbCoapTransportLogVolume, "./target/tb-coap-transport-logs/");
copyLogs(tbLwm2mTransportLogVolume, "./target/tb-lwm2m-transport-logs/"); copyLogs(tbLwm2mTransportLogVolume, "./target/tb-lwm2m-transport-logs/");

112
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java

@ -16,109 +16,79 @@
package org.thingsboard.server.msa.connectivity; package org.thingsboard.server.msa.connectivity;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets; import org.testng.annotations.AfterMethod;
import org.junit.Assert; import org.testng.annotations.BeforeMethod;
import org.junit.Test; import org.testng.annotations.Test;
import org.springframework.http.ResponseEntity;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.security.DeviceCredentials; import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest; import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.WsClient; import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.mapper.WsTelemetryResponse; import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import java.util.Arrays;
import java.util.Optional;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals; import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.thingsboard.server.common.data.DataConstants.DEVICE; import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE; import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype;
public class HttpClientTest extends AbstractContainerTest { public class HttpClientTest extends AbstractContainerTest {
private Device device;
@BeforeMethod
public void setUp() throws Exception {
testRestClient.login("tenant@thingsboard.org", "tenant");
device = testRestClient.postDevice("", defaultDevicePrototype("http_"));
}
@AfterMethod
public void tearDown() {
testRestClient.deleteDeviceIfExists(device.getId());
}
@Test @Test
public void telemetryUpload() throws Exception { public void telemetryUpload() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("http_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
ResponseEntity deviceTelemetryResponse = restClient.getRestTemplate() testRestClient.postTelemetry(deviceCredentials.getCredentialsId(), mapper.readTree(createPayload().toString()));
.postForEntity(HTTPS_URL + "/api/v1/{credentialsId}/telemetry",
mapper.readTree(createPayload().toString()),
ResponseEntity.class,
deviceCredentials.getCredentialsId());
Assert.assertTrue(deviceTelemetryResponse.getStatusCode().is2xxSuccessful());
WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage(); WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(Sets.newHashSet("booleanKey", "stringKey", "doubleKey", "longKey"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("booleanKey", "stringKey", "doubleKey", "longKey"));
actualLatestTelemetry.getLatestValues().keySet());
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", Boolean.TRUE.toString()));
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", "value1"));
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0)));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73)));
restClient.deleteDevice(device.getId()); assertThat(actualLatestTelemetry.getDataValuesByKey("booleanKey").get(1)).isEqualTo(Boolean.TRUE.toString());
assertThat(actualLatestTelemetry.getDataValuesByKey("stringKey").get(1)).isEqualTo("value1");
assertThat(actualLatestTelemetry.getDataValuesByKey("doubleKey").get(1)).isEqualTo(Double.toString(42.0));
assertThat(actualLatestTelemetry.getDataValuesByKey("longKey").get(1)).isEqualTo(Long.toString(73));
} }
@Test @Test
public void getAttributes() throws Exception { public void getAttributes() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); String accessToken = testRestClient.getDeviceCredentialsByDeviceId(device.getId()).getCredentialsId();
TB_TOKEN = restClient.getToken(); assertThat(accessToken).isNotNull();
Device device = createDevice("test"); JsonNode sharedAattribute = mapper.readTree(createPayload().toString());
String accessToken = restClient.getDeviceCredentialsByDeviceId(device.getId()).get().getCredentialsId(); testRestClient.postTelemetryAttribute(DEVICE, device.getId(), SHARED_SCOPE, sharedAattribute);
assertNotNull(accessToken);
ResponseEntity deviceSharedAttributes = restClient.getRestTemplate() JsonNode clientAttribute = mapper.readTree(createPayload().toString());
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/" + DEVICE + "/" + device.getId().toString() + "/attributes/" + SHARED_SCOPE, mapper.readTree(createPayload().toString()), testRestClient.postAttribute(accessToken, clientAttribute);
ResponseEntity.class,
accessToken);
Assert.assertTrue(deviceSharedAttributes.getStatusCode().is2xxSuccessful());
ResponseEntity deviceClientsAttributes = restClient.getRestTemplate()
.postForEntity(HTTPS_URL + "/api/v1/" + accessToken + "/attributes/", mapper.readTree(createPayload().toString()),
ResponseEntity.class,
accessToken);
Assert.assertTrue(deviceClientsAttributes.getStatusCode().is2xxSuccessful());
TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); TimeUnit.SECONDS.sleep(3 * timeoutMultiplier);
@SuppressWarnings("deprecation") JsonNode attributes = testRestClient.getAttributes(accessToken, null, null);
Optional<JsonNode> allOptional = restClient.getAttributes(accessToken, null, null); assertThat(attributes.get("shared")).isEqualTo(sharedAattribute);
assertTrue(allOptional.isPresent()); assertThat(attributes.get("client")).isEqualTo(clientAttribute);
JsonNode all = allOptional.get();
assertEquals(2, all.size());
assertEquals(mapper.readTree(createPayload().toString()), all.get("shared"));
assertEquals(mapper.readTree(createPayload().toString()), all.get("client"));
@SuppressWarnings("deprecation")
Optional<JsonNode> sharedOptional = restClient.getAttributes(accessToken, null, "stringKey");
assertTrue(sharedOptional.isPresent());
JsonNode shared = sharedOptional.get();
assertEquals(shared.get("shared").get("stringKey"), mapper.readTree(createPayload().get("stringKey").toString()));
assertFalse(shared.has("client"));
@SuppressWarnings("deprecation") JsonNode attributes2 = testRestClient.getAttributes(accessToken, null, "stringKey");
Optional<JsonNode> clientOptional = restClient.getAttributes(accessToken, "longKey,stringKey", null); assertThat(attributes2.get("shared").get("stringKey")).isEqualTo(sharedAattribute.get("stringKey"));
assertTrue(clientOptional.isPresent()); assertThat(attributes2.has("client")).isFalse();
JsonNode client = clientOptional.get(); JsonNode attributes3 = testRestClient.getAttributes(accessToken, "longKey,stringKey", null);
assertFalse(client.has("shared"));
assertEquals(mapper.readTree(createPayload().get("longKey").toString()), client.get("client").get("longKey"));
assertEquals(client.get("client").get("stringKey"), mapper.readTree(createPayload().get("stringKey").toString()));
restClient.deleteDevice(device.getId()); assertThat(attributes3.has("shared")).isFalse();
assertThat(attributes3.get("client").get("longKey")).isEqualTo(clientAttribute.get("longKey"));
assertThat(attributes3.get("client").get("stringKey")).isEqualTo(clientAttribute.get("stringKey"));
} }
} }

226
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java

@ -16,7 +16,6 @@
package org.thingsboard.server.msa.connectivity; package org.thingsboard.server.msa.connectivity;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
@ -26,19 +25,19 @@ import io.netty.buffer.Unpooled;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.Assert; import org.testng.annotations.AfterMethod;
import org.junit.Test; import org.testng.annotations.BeforeMethod;
import org.springframework.core.ParameterizedTypeReference; import org.testng.annotations.Test;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClient;
import org.thingsboard.mqtt.MqttClientConfig; import org.thingsboard.mqtt.MqttClientConfig;
import org.thingsboard.mqtt.MqttHandler; import org.thingsboard.mqtt.MqttHandler;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.RuleChainId; import org.thingsboard.server.common.data.id.RuleChainId;
import org.thingsboard.server.common.data.page.PageData; import org.thingsboard.server.common.data.page.PageData;
import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.NodeConnectionInfo; import org.thingsboard.server.common.data.rule.NodeConnectionInfo;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.rule.RuleChainMetaData; import org.thingsboard.server.common.data.rule.RuleChainMetaData;
@ -61,14 +60,29 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.testng.Assert.fail;
import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultDevicePrototype;
@Slf4j @Slf4j
public class MqttClientTest extends AbstractContainerTest { public class MqttClientTest extends AbstractContainerTest {
private Device device;
@BeforeMethod
public void setUp() throws Exception {
testRestClient.login("tenant@thingsboard.org", "tenant");
device = testRestClient.postDevice("", defaultDevicePrototype("http_"));
}
@AfterMethod
public void tearDown() {
testRestClient.deleteDeviceIfExists(device.getId());
}
@Test @Test
public void telemetryUpload() throws Exception { public void telemetryUpload() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
MqttClient mqttClient = getMqttClient(deviceCredentials, null); MqttClient mqttClient = getMqttClient(deviceCredentials, null);
@ -77,25 +91,19 @@ public class MqttClientTest extends AbstractContainerTest {
log.info("Received telemetry: {}", actualLatestTelemetry); log.info("Received telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(Sets.newHashSet("booleanKey", "stringKey", "doubleKey", "longKey"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("booleanKey", "stringKey", "doubleKey", "longKey"));
actualLatestTelemetry.getLatestValues().keySet());
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", Boolean.TRUE.toString())); assertThat(actualLatestTelemetry.getDataValuesByKey("booleanKey").get(1)).isEqualTo(Boolean.TRUE.toString());
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", "value1")); assertThat(actualLatestTelemetry.getDataValuesByKey("stringKey").get(1)).isEqualTo("value1");
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0))); assertThat(actualLatestTelemetry.getDataValuesByKey("doubleKey").get(1)).isEqualTo(Double.toString(42.0));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73))); assertThat(actualLatestTelemetry.getDataValuesByKey("longKey").get(1)).isEqualTo(Long.toString(73));
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
} }
@Test @Test
public void telemetryUploadWithTs() throws Exception { public void telemetryUploadWithTs() throws Exception {
long ts = 1451649600512L; long ts = 1451649600512L;
DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
restClient.login("tenant@thingsboard.org", "tenant");
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(device.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
MqttClient mqttClient = getMqttClient(deviceCredentials, null); MqttClient mqttClient = getMqttClient(deviceCredentials, null);
@ -104,22 +112,18 @@ public class MqttClientTest extends AbstractContainerTest {
log.info("Received telemetry: {}", actualLatestTelemetry); log.info("Received telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(getExpectedLatestValues(ts), actualLatestTelemetry.getLatestValues()); assertThat(getExpectedLatestValues(ts)).isEqualTo(actualLatestTelemetry.getLatestValues());
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", ts, Boolean.TRUE.toString())); assertThat(actualLatestTelemetry.getDataValuesByKey("booleanKey").get(1)).isEqualTo(Boolean.TRUE.toString());
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", ts, "value1")); assertThat(actualLatestTelemetry.getDataValuesByKey("stringKey").get(1)).isEqualTo("value1");
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", ts, Double.toString(42.0))); assertThat(actualLatestTelemetry.getDataValuesByKey("doubleKey").get(1)).isEqualTo(Double.toString(42.0));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", ts, Long.toString(73))); assertThat(actualLatestTelemetry.getDataValuesByKey("longKey").get(1)).isEqualTo(Long.toString(73));
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
} }
@Test @Test
public void publishAttributeUpdateToServer() throws Exception { public void publishAttributeUpdateToServer() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
WsClient wsClient = subscribeToWebSocket(device.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(device.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
@ -134,23 +138,18 @@ public class MqttClientTest extends AbstractContainerTest {
log.info("Received telemetry: {}", actualLatestTelemetry); log.info("Received telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(Sets.newHashSet("attr1", "attr2", "attr3", "attr4"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("attr1", "attr2", "attr3", "attr4"));
actualLatestTelemetry.getLatestValues().keySet());
Assert.assertTrue(verify(actualLatestTelemetry, "attr1", "value1"));
Assert.assertTrue(verify(actualLatestTelemetry, "attr2", Boolean.TRUE.toString()));
Assert.assertTrue(verify(actualLatestTelemetry, "attr3", Double.toString(42.0)));
Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73)));
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId()); assertThat(actualLatestTelemetry.getDataValuesByKey("attr1").get(1)).isEqualTo("value1");
assertThat(actualLatestTelemetry.getDataValuesByKey("attr2").get(1)).isEqualTo(Boolean.TRUE.toString());
assertThat(actualLatestTelemetry.getDataValuesByKey("attr3").get(1)).isEqualTo(Double.toString(42.0));
assertThat(actualLatestTelemetry.getDataValuesByKey("attr4").get(1)).isEqualTo(Long.toString(73));
} }
@Test @Test
public void requestAttributeValuesFromServer() throws Exception { public void requestAttributeValuesFromServer() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
WsClient wsClient = subscribeToWebSocket(device.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(device.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
@ -166,21 +165,16 @@ public class MqttClientTest extends AbstractContainerTest {
log.info("Received ws telemetry: {}", actualLatestTelemetry); log.info("Received ws telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(1, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(1);
Assert.assertEquals(Sets.newHashSet("clientAttr"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnly("clientAttr");
actualLatestTelemetry.getLatestValues().keySet()); assertThat(actualLatestTelemetry.getDataValuesByKey("clientAttr").get(1)).isEqualTo(clientAttributeValue);
Assert.assertTrue(verify(actualLatestTelemetry, "clientAttr", clientAttributeValue));
// Add a new shared attribute // Add a new shared attribute
JsonObject sharedAttributes = new JsonObject(); JsonObject sharedAttributes = new JsonObject();
String sharedAttributeValue = StringUtils.randomAlphanumeric(8); String sharedAttributeValue = StringUtils.randomAlphanumeric(8);
sharedAttributes.addProperty("sharedAttr", sharedAttributeValue); sharedAttributes.addProperty("sharedAttr", sharedAttributeValue);
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() JsonNode sharedAttribute = mapper.readTree(sharedAttributes.toString());
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE", testRestClient.postTelemetryAttribute(DataConstants.DEVICE, device.getId(), SHARED_SCOPE, sharedAttribute);
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
device.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
// Subscribe to attributes response // Subscribe to attributes response
mqttClient.on("v1/devices/me/attributes/response/+", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.on("v1/devices/me/attributes/response/+", listener, MqttQoS.AT_LEAST_ONCE).get();
@ -197,20 +191,16 @@ public class MqttClientTest extends AbstractContainerTest {
AttributesResponse attributes = mapper.readValue(Objects.requireNonNull(event).getMessage(), AttributesResponse.class); AttributesResponse attributes = mapper.readValue(Objects.requireNonNull(event).getMessage(), AttributesResponse.class);
log.info("Received telemetry: {}", attributes); log.info("Received telemetry: {}", attributes);
Assert.assertEquals(1, attributes.getClient().size()); assertThat(attributes.getClient()).hasSize(1);
Assert.assertEquals(clientAttributeValue, attributes.getClient().get("clientAttr")); assertThat(attributes.getClient().get("clientAttr")).isEqualTo(clientAttributeValue);
Assert.assertEquals(1, attributes.getShared().size());
Assert.assertEquals(sharedAttributeValue, attributes.getShared().get("sharedAttr"));
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId()); assertThat(attributes.getShared()).hasSize(1);
assertThat(attributes.getShared().get("sharedAttr")).isEqualTo(sharedAttributeValue);
} }
@Test @Test
public void subscribeToAttributeUpdatesFromServer() throws Exception { public void subscribeToAttributeUpdatesFromServer() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
MqttClient mqttClient = getMqttClient(deviceCredentials, listener); MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
@ -225,38 +215,28 @@ public class MqttClientTest extends AbstractContainerTest {
JsonObject sharedAttributes = new JsonObject(); JsonObject sharedAttributes = new JsonObject();
String sharedAttributeValue = StringUtils.randomAlphanumeric(8); String sharedAttributeValue = StringUtils.randomAlphanumeric(8);
sharedAttributes.addProperty(sharedAttributeName, sharedAttributeValue); sharedAttributes.addProperty(sharedAttributeName, sharedAttributeValue);
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() JsonNode sharedAttribute = mapper.readTree(sharedAttributes.toString());
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class, testRestClient.postTelemetryAttribute(DataConstants.DEVICE, device.getId(), SHARED_SCOPE, sharedAttribute);
device.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertEquals(sharedAttributeValue, assertThat(mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText())
mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText()); .isEqualTo(sharedAttributeValue);
// Update the shared attribute value // Update the shared attribute value
JsonObject updatedSharedAttributes = new JsonObject(); JsonObject updatedSharedAttributes = new JsonObject();
String updatedSharedAttributeValue = StringUtils.randomAlphanumeric(8); String updatedSharedAttributeValue = StringUtils.randomAlphanumeric(8);
updatedSharedAttributes.addProperty(sharedAttributeName, updatedSharedAttributeValue); updatedSharedAttributes.addProperty(sharedAttributeName, updatedSharedAttributeValue);
ResponseEntity updatedSharedAttributesResponse = restClient.getRestTemplate() testRestClient.postTelemetryAttribute(DEVICE, device.getId(), SHARED_SCOPE, mapper.readTree(updatedSharedAttributes.toString()));
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(updatedSharedAttributes.toString()), ResponseEntity.class,
device.getId());
Assert.assertTrue(updatedSharedAttributesResponse.getStatusCode().is2xxSuccessful());
event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertEquals(updatedSharedAttributeValue, assertThat(mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText())
mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get(sharedAttributeName).asText()); .isEqualTo(updatedSharedAttributeValue);
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
} }
@Test @Test
public void serverSideRpc() throws Exception { public void serverSideRpc() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
MqttClient mqttClient = getMqttClient(deviceCredentials, listener); MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
@ -270,21 +250,18 @@ public class MqttClientTest extends AbstractContainerTest {
serverRpcPayload.addProperty("method", "getValue"); serverRpcPayload.addProperty("method", "getValue");
serverRpcPayload.addProperty("params", true); serverRpcPayload.addProperty("params", true);
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getClass().getSimpleName()))); ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getClass().getSimpleName())));
ListenableFuture<ResponseEntity> future = service.submit(() -> { ListenableFuture<JsonNode> future = service.submit(() -> {
try { try {
return restClient.getRestTemplate() return testRestClient.postServerSideRpc(device.getId(), mapper.readTree(serverRpcPayload.toString()));
.postForEntity(HTTPS_URL + "/api/rpc/twoway/{deviceId}",
mapper.readTree(serverRpcPayload.toString()), String.class,
device.getId());
} catch (IOException e) { } catch (IOException e) {
return ResponseEntity.badRequest().build(); return null;
} }
}); });
// Wait for RPC call from the server and send the response // Wait for RPC call from the server and send the response
MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertEquals("{\"method\":\"getValue\",\"params\":true}", Objects.requireNonNull(requestFromServer).getMessage()); assertThat(Objects.requireNonNull(requestFromServer).getMessage()).isEqualTo("{\"method\":\"getValue\",\"params\":true}");
Integer requestId = Integer.valueOf(Objects.requireNonNull(requestFromServer).getTopic().substring("v1/devices/me/rpc/request/".length())); Integer requestId = Integer.valueOf(Objects.requireNonNull(requestFromServer).getTopic().substring("v1/devices/me/rpc/request/".length()));
JsonObject clientResponse = new JsonObject(); JsonObject clientResponse = new JsonObject();
@ -292,19 +269,14 @@ public class MqttClientTest extends AbstractContainerTest {
// Send a response to the server's RPC request // Send a response to the server's RPC request
mqttClient.publish("v1/devices/me/rpc/response/" + requestId, Unpooled.wrappedBuffer(clientResponse.toString().getBytes())).get(); mqttClient.publish("v1/devices/me/rpc/response/" + requestId, Unpooled.wrappedBuffer(clientResponse.toString().getBytes())).get();
ResponseEntity serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS); JsonNode serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS);
service.shutdownNow(); service.shutdownNow();
Assert.assertTrue(serverResponse.getStatusCode().is2xxSuccessful()); assertThat(serverResponse).isEqualTo(mapper.readTree(clientResponse.toString()));
Assert.assertEquals(clientResponse.toString(), serverResponse.getBody());
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
} }
@Test @Test
public void clientSideRpc() throws Exception { public void clientSideRpc() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
MqttClient mqttClient = getMqttClient(deviceCredentials, listener); MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
@ -328,46 +300,33 @@ public class MqttClientTest extends AbstractContainerTest {
TimeUnit.SECONDS.sleep(1 * timeoutMultiplier); TimeUnit.SECONDS.sleep(1 * timeoutMultiplier);
MqttEvent responseFromServer = listener.getEvents().poll(1 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent responseFromServer = listener.getEvents().poll(1 * timeoutMultiplier, TimeUnit.SECONDS);
Integer responseId = Integer.valueOf(Objects.requireNonNull(responseFromServer).getTopic().substring("v1/devices/me/rpc/response/".length())); Integer responseId = Integer.valueOf(Objects.requireNonNull(responseFromServer).getTopic().substring("v1/devices/me/rpc/response/".length()));
Assert.assertEquals(requestId, responseId); assertThat(responseId).isEqualTo(requestId);
Assert.assertEquals("requestReceived", mapper.readTree(responseFromServer.getMessage()).get("response").asText()); assertThat(mapper.readTree(responseFromServer.getMessage()).get("response").asText()).isEqualTo("requestReceived");
// Make the default rule chain a root again // Make the default rule chain a root again
ResponseEntity<RuleChain> rootRuleChainResponse = restClient.getRestTemplate() testRestClient.setRootRuleChain(defaultRuleChainId);
.postForEntity(HTTPS_URL + "/api/ruleChain/{ruleChainId}/root",
null,
RuleChain.class,
defaultRuleChainId);
Assert.assertTrue(rootRuleChainResponse.getStatusCode().is2xxSuccessful());
// Delete the created rule chain // Delete the created rule chain
restClient.getRestTemplate().delete(HTTPS_URL + "/api/ruleChain/{ruleChainId}", ruleChainId); testRestClient.deleteRuleChain(ruleChainId);
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + device.getId());
} }
@Test @Test
public void deviceDeletedClosingSession() throws Exception { public void deviceDeletedClosingSession() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); DeviceCredentials deviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(device.getId());
String deviceForDeletingTestName = "Device for deleting notification test";
Device device = createDevice(deviceForDeletingTestName);
DeviceCredentials deviceCredentials = restClient.getDeviceCredentialsByDeviceId(device.getId()).get();
MqttMessageListener listener = new MqttMessageListener(); MqttMessageListener listener = new MqttMessageListener();
MqttClient mqttClient = getMqttClient(deviceCredentials, listener); MqttClient mqttClient = getMqttClient(deviceCredentials, listener);
restClient.deleteDevice(device.getId()); testRestClient.deleteDeviceIfExists(device.getId());
TimeUnit.SECONDS.sleep(3 * timeoutMultiplier); TimeUnit.SECONDS.sleep(3 * timeoutMultiplier);
Assert.assertFalse(mqttClient.isConnected()); assertThat(mqttClient.isConnected()).isFalse();
} }
private RuleChainId createRootRuleChainForRpcResponse() throws Exception { private RuleChainId createRootRuleChainForRpcResponse() throws Exception {
RuleChain newRuleChain = new RuleChain(); RuleChain newRuleChain = new RuleChain();
newRuleChain.setName("testRuleChain"); newRuleChain.setName("testRuleChain");
ResponseEntity<RuleChain> ruleChainResponse = restClient.getRestTemplate()
.postForEntity(HTTPS_URL + "/api/ruleChain", RuleChain ruleChain = testRestClient.postRootRuleChain(newRuleChain);
newRuleChain,
RuleChain.class);
Assert.assertTrue(ruleChainResponse.getStatusCode().is2xxSuccessful());
RuleChain ruleChain = ruleChainResponse.getBody();
JsonNode configuration = mapper.readTree(this.getClass().getClassLoader().getResourceAsStream("RpcResponseRuleChainMetadata.json")); JsonNode configuration = mapper.readTree(this.getClass().getClassLoader().getResourceAsStream("RpcResponseRuleChainMetadata.json"));
RuleChainMetaData ruleChainMetaData = new RuleChainMetaData(); RuleChainMetaData ruleChainMetaData = new RuleChainMetaData();
@ -376,37 +335,22 @@ public class MqttClientTest extends AbstractContainerTest {
ruleChainMetaData.setNodes(Arrays.asList(mapper.treeToValue(configuration.get("nodes"), RuleNode[].class))); ruleChainMetaData.setNodes(Arrays.asList(mapper.treeToValue(configuration.get("nodes"), RuleNode[].class)));
ruleChainMetaData.setConnections(Arrays.asList(mapper.treeToValue(configuration.get("connections"), NodeConnectionInfo[].class))); ruleChainMetaData.setConnections(Arrays.asList(mapper.treeToValue(configuration.get("connections"), NodeConnectionInfo[].class)));
ResponseEntity<RuleChainMetaData> ruleChainMetadataResponse = restClient.getRestTemplate() testRestClient.postRuleChainMetadata(ruleChainMetaData);
.postForEntity(HTTPS_URL + "/api/ruleChain/metadata",
ruleChainMetaData,
RuleChainMetaData.class);
Assert.assertTrue(ruleChainMetadataResponse.getStatusCode().is2xxSuccessful());
// Set a new rule chain as root // Set a new rule chain as root
ResponseEntity<RuleChain> rootRuleChainResponse = restClient.getRestTemplate() testRestClient.setRootRuleChain(ruleChain.getId());
.postForEntity(HTTPS_URL + "/api/ruleChain/{ruleChainId}/root",
null,
RuleChain.class,
ruleChain.getId());
Assert.assertTrue(rootRuleChainResponse.getStatusCode().is2xxSuccessful());
return ruleChain.getId(); return ruleChain.getId();
} }
private RuleChainId getDefaultRuleChainId() { private RuleChainId getDefaultRuleChainId() {
ResponseEntity<PageData<RuleChain>> ruleChains = restClient.getRestTemplate().exchange( PageData<RuleChain> ruleChains = testRestClient.getRuleChains(new PageLink(40, 0));
HTTPS_URL + "/api/ruleChains?pageSize=40&page=0&textSearch=",
HttpMethod.GET, Optional<RuleChain> defaultRuleChain = ruleChains.getData()
null,
new ParameterizedTypeReference<PageData<RuleChain>>() {
});
Optional<RuleChain> defaultRuleChain = ruleChains.getBody().getData()
.stream() .stream()
.filter(RuleChain::isRoot) .filter(RuleChain::isRoot)
.findFirst(); .findFirst();
if (!defaultRuleChain.isPresent()) { if (!defaultRuleChain.isPresent()) {
Assert.fail("Root rule chain wasn't found"); fail("Root rule chain wasn't found");
} }
return defaultRuleChain.get().getId(); return defaultRuleChain.get().getId();
} }

220
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttGatewayClientTest.java

@ -16,7 +16,6 @@
package org.thingsboard.server.msa.connectivity; package org.thingsboard.server.msa.connectivity;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.collect.Sets;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService; import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.common.util.concurrent.MoreExecutors; import com.google.common.util.concurrent.MoreExecutors;
@ -28,15 +27,17 @@ import io.netty.buffer.Unpooled;
import io.netty.handler.codec.mqtt.MqttQoS; import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.junit.After; import org.springframework.http.HttpStatus;
import org.junit.Assert; import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
import org.junit.Before; import org.testng.annotations.AfterMethod;
import org.junit.Test; import org.testng.annotations.BeforeMethod;
import org.springframework.http.ResponseEntity; import org.testng.annotations.Test;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.common.util.ThingsBoardThreadFactory; import org.thingsboard.common.util.ThingsBoardThreadFactory;
import org.thingsboard.mqtt.MqttClient; import org.thingsboard.mqtt.MqttClient;
import org.thingsboard.mqtt.MqttClientConfig; import org.thingsboard.mqtt.MqttClientConfig;
import org.thingsboard.mqtt.MqttHandler; import org.thingsboard.mqtt.MqttHandler;
import org.thingsboard.server.common.data.DataConstants;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
@ -50,9 +51,9 @@ import org.thingsboard.server.msa.mapper.WsTelemetryResponse;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.Random; import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue; import java.util.concurrent.BlockingQueue;
@ -60,28 +61,34 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat;
import static org.thingsboard.server.common.data.DataConstants.DEVICE;
import static org.thingsboard.server.common.data.DataConstants.SHARED_SCOPE;
import static org.thingsboard.server.msa.prototypes.DevicePrototypes.defaultGatewayPrototype;
@Slf4j @Slf4j
public class MqttGatewayClientTest extends AbstractContainerTest { public class MqttGatewayClientTest extends AbstractContainerTest {
Device gatewayDevice; private Device gatewayDevice;
MqttClient mqttClient; private MqttClient mqttClient;
Device createdDevice; private Device createdDevice;
MqttMessageListener listener; private MqttMessageListener listener;
private JsonParser jsonParser = new JsonParser();
@Before @BeforeMethod
public void createGateway() throws Exception { public void createGateway() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant"); testRestClient.login("tenant@thingsboard.org", "tenant");
this.gatewayDevice = createGatewayDevice(); gatewayDevice = testRestClient.postDevice("", defaultGatewayPrototype());
Optional<DeviceCredentials> gatewayDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(gatewayDevice.getId()); DeviceCredentials gatewayDeviceCredentials = testRestClient.getDeviceCredentialsByDeviceId(gatewayDevice.getId());
Assert.assertTrue(gatewayDeviceCredentials.isPresent());
this.listener = new MqttMessageListener(); this.listener = new MqttMessageListener();
this.mqttClient = getMqttClient(gatewayDeviceCredentials.get(), listener); this.mqttClient = getMqttClient(gatewayDeviceCredentials, listener);
this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice); this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice);
} }
@After @AfterMethod
public void removeGateway() throws Exception { public void removeGateway() {
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.gatewayDevice.getId()); testRestClient.deleteDeviceIfExists(this.gatewayDevice.getId());
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.createdDevice.getId()); testRestClient.deleteDeviceIfExists(this.createdDevice.getId());
this.listener = null; this.listener = null;
this.mqttClient = null; this.mqttClient = null;
this.createdDevice = null; this.createdDevice = null;
@ -95,40 +102,38 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
log.info("Received telemetry: {}", actualLatestTelemetry); log.info("Received telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(Sets.newHashSet("booleanKey", "stringKey", "doubleKey", "longKey"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("booleanKey", "stringKey", "doubleKey", "longKey"));
actualLatestTelemetry.getLatestValues().keySet());
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", Boolean.TRUE.toString())); assertThat(actualLatestTelemetry.getDataValuesByKey("booleanKey").get(1)).isEqualTo(Boolean.TRUE.toString());
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", "value1")); assertThat(actualLatestTelemetry.getDataValuesByKey("stringKey").get(1)).isEqualTo("value1");
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", Double.toString(42.0))); assertThat(actualLatestTelemetry.getDataValuesByKey("doubleKey").get(1)).isEqualTo(Double.toString(42.0));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", Long.toString(73))); assertThat(actualLatestTelemetry.getDataValuesByKey("longKey").get(1)).isEqualTo(Long.toString(73));
} }
@Test @Test
public void telemetryUploadWithTs() throws Exception { public void telemetryUploadWithTs() throws Exception {
long ts = 1451649600512L; long ts = 1451649600512L;
restClient.login("tenant@thingsboard.org", "tenant");
WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "LATEST_TELEMETRY", CmdsType.TS_SUB_CMDS);
mqttClient.publish("v1/gateway/telemetry", Unpooled.wrappedBuffer(createGatewayPayload(createdDevice.getName(), ts).toString().getBytes())).get(); mqttClient.publish("v1/gateway/telemetry", Unpooled.wrappedBuffer(createGatewayPayload(createdDevice.getName(), ts).toString().getBytes())).get();
WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage(); WsTelemetryResponse actualLatestTelemetry = wsClient.getLastMessage();
log.info("Received telemetry: {}", actualLatestTelemetry); log.info("Received telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(getExpectedLatestValues(ts), actualLatestTelemetry.getLatestValues()); assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("booleanKey", "stringKey", "doubleKey", "longKey"));
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", ts, Boolean.TRUE.toString())); assertThat(actualLatestTelemetry.getDataValuesByKey("booleanKey").get(1)).isEqualTo(Boolean.TRUE.toString());
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", ts, "value1")); assertThat(actualLatestTelemetry.getDataValuesByKey("stringKey").get(1)).isEqualTo("value1");
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", ts, Double.toString(42.0))); assertThat(actualLatestTelemetry.getDataValuesByKey("doubleKey").get(1)).isEqualTo(Double.toString(42.0));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", ts, Long.toString(73))); assertThat(actualLatestTelemetry.getDataValuesByKey("longKey").get(1)).isEqualTo(Long.toString(73));
} }
@Test @Test
public void publishAttributeUpdateToServer() throws Exception { public void publishAttributeUpdateToServer() throws Exception {
Optional<DeviceCredentials> createdDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(createdDevice.getId()); testRestClient.getDeviceCredentialsByDeviceId(createdDevice.getId());
Assert.assertTrue(createdDeviceCredentials.isPresent());
WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS); WsClient wsClient = subscribeToWebSocket(createdDevice.getId(), "CLIENT_SCOPE", CmdsType.ATTR_SUB_CMDS);
JsonObject clientAttributes = new JsonObject(); JsonObject clientAttributes = new JsonObject();
clientAttributes.addProperty("attr1", "value1"); clientAttributes.addProperty("attr1", "value1");
@ -142,20 +147,18 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
log.info("Received attributes: {}", actualLatestTelemetry); log.info("Received attributes: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(4, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(4);
Assert.assertEquals(Sets.newHashSet("attr1", "attr2", "attr3", "attr4"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnlyOnceElementsOf(Arrays.asList("attr1", "attr2", "attr3", "attr4"));
actualLatestTelemetry.getLatestValues().keySet());
Assert.assertTrue(verify(actualLatestTelemetry, "attr1", "value1")); assertThat(actualLatestTelemetry.getDataValuesByKey("attr1").get(1)).isEqualTo("value1");
Assert.assertTrue(verify(actualLatestTelemetry, "attr2", Boolean.TRUE.toString())); assertThat(actualLatestTelemetry.getDataValuesByKey("attr2").get(1)).isEqualTo(Boolean.TRUE.toString());
Assert.assertTrue(verify(actualLatestTelemetry, "attr3", Double.toString(42.0))); assertThat(actualLatestTelemetry.getDataValuesByKey("attr3").get(1)).isEqualTo(Double.toString(42.0));
Assert.assertTrue(verify(actualLatestTelemetry, "attr4", Long.toString(73))); assertThat(actualLatestTelemetry.getDataValuesByKey("attr4").get(1)).isEqualTo(Long.toString(73));
} }
@Test @Test
public void responseDataOnAttributesRequestCheck() throws Exception { public void responseDataOnAttributesRequestCheck() throws Exception {
Optional<DeviceCredentials> createdDeviceCredentials = restClient.getDeviceCredentialsByDeviceId(createdDevice.getId()); testRestClient.getDeviceCredentialsByDeviceId(createdDevice.getId());
Assert.assertTrue(createdDeviceCredentials.isPresent());
JsonObject sharedAttributes = new JsonObject(); JsonObject sharedAttributes = new JsonObject();
sharedAttributes.addProperty("attr1", "value1"); sharedAttributes.addProperty("attr1", "value1");
sharedAttributes.addProperty("attr2", true); sharedAttributes.addProperty("attr2", true);
@ -163,11 +166,8 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
sharedAttributes.addProperty("attr4", 73); sharedAttributes.addProperty("attr4", 73);
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate()
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE", testRestClient.postTelemetryAttribute(DataConstants.DEVICE, createdDevice.getId(), SHARED_SCOPE, mapper.readTree(sharedAttributes.toString()));
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
createdDevice.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
var event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); var event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
JsonObject requestData = new JsonObject(); JsonObject requestData = new JsonObject();
@ -181,8 +181,8 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
JsonObject responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); JsonObject responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("value")); assertThat(responseData.has("value")).isTrue();
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("value").getAsString()); assertThat(responseData.get("value").getAsString()).isEqualTo(sharedAttributes.get("attr1").getAsString());
requestData = new JsonObject(); requestData = new JsonObject();
requestData.addProperty("id", 1); requestData.addProperty("id", 1);
@ -198,9 +198,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("values")); assertThat(responseData.has("values")).isTrue();
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString()); assertThat(responseData.get("values").getAsJsonObject().get("attr1").getAsString()).isEqualTo(sharedAttributes.get("attr1").getAsString());
Assert.assertEquals(sharedAttributes.get("attr2").getAsString(), responseData.get("values").getAsJsonObject().get("attr2").getAsString()); assertThat(responseData.get("values").getAsJsonObject().get("attr2").getAsString()).isEqualTo(sharedAttributes.get("attr2").getAsString());
requestData = new JsonObject(); requestData = new JsonObject();
requestData.addProperty("id", 1); requestData.addProperty("id", 1);
@ -216,9 +216,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject(); responseData = jsonParser.parse(Objects.requireNonNull(event).getMessage()).getAsJsonObject();
Assert.assertTrue(responseData.has("values")); assertThat(responseData.has("values")).isTrue();
Assert.assertEquals(sharedAttributes.get("attr1").getAsString(), responseData.get("values").getAsJsonObject().get("attr1").getAsString()); assertThat(responseData.get("values").getAsJsonObject().get("attr1").getAsString()).isEqualTo(sharedAttributes.get("attr1").getAsString());
Assert.assertEquals(1, responseData.get("values").getAsJsonObject().entrySet().size()); assertThat(responseData.get("values").getAsJsonObject().entrySet()).hasSize(1);
} }
@Test @Test
@ -237,11 +237,9 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
log.info("Received ws telemetry: {}", actualLatestTelemetry); log.info("Received ws telemetry: {}", actualLatestTelemetry);
wsClient.closeBlocking(); wsClient.closeBlocking();
Assert.assertEquals(1, actualLatestTelemetry.getData().size()); assertThat(actualLatestTelemetry.getData()).hasSize(1);
Assert.assertEquals(Sets.newHashSet("clientAttr"), assertThat(actualLatestTelemetry.getLatestValues().keySet()).containsOnly("clientAttr");
actualLatestTelemetry.getLatestValues().keySet()); assertThat(actualLatestTelemetry.getDataValuesByKey("clientAttr").get(1)).isEqualTo(clientAttributeValue);
Assert.assertTrue(verify(actualLatestTelemetry, "clientAttr", clientAttributeValue));
// Add a new shared attribute // Add a new shared attribute
JsonObject sharedAttributes = new JsonObject(); JsonObject sharedAttributes = new JsonObject();
@ -251,16 +249,12 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
// Subscribe for attribute update event // Subscribe for attribute update event
mqttClient.on("v1/gateway/attributes", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.on("v1/gateway/attributes", listener, MqttQoS.AT_LEAST_ONCE).get();
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() testRestClient.postTelemetryAttribute(DEVICE, createdDevice.getId(), SHARED_SCOPE, mapper.readTree(sharedAttributes.toString()));
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
createdDevice.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
MqttEvent sharedAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent sharedAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
// Catch attribute update event // Catch attribute update event
Assert.assertNotNull(sharedAttributeEvent); assertThat(sharedAttributeEvent).isNotNull();
Assert.assertEquals("v1/gateway/attributes", sharedAttributeEvent.getTopic()); assertThat(sharedAttributeEvent.getTopic()).isEqualTo("v1/gateway/attributes");
// Subscribe to attributes response // Subscribe to attributes response
mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get(); mqttClient.on("v1/gateway/attributes/response", listener, MqttQoS.AT_LEAST_ONCE).get();
@ -288,15 +282,11 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
gatewaySharedAttributeValue.addProperty("device", createdDevice.getName()); gatewaySharedAttributeValue.addProperty("device", createdDevice.getName());
gatewaySharedAttributeValue.add("data", sharedAttributes); gatewaySharedAttributeValue.add("data", sharedAttributes);
ResponseEntity sharedAttributesResponse = restClient.getRestTemplate() testRestClient.postTelemetryAttribute(DEVICE, createdDevice.getId(), SHARED_SCOPE, mapper.readTree(sharedAttributes.toString()));
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(sharedAttributes.toString()), ResponseEntity.class,
createdDevice.getId());
Assert.assertTrue(sharedAttributesResponse.getStatusCode().is2xxSuccessful());
MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertEquals(sharedAttributeValue, assertThat(mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText())
mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText()); .isEqualTo(sharedAttributeValue);
// Update the shared attribute value // Update the shared attribute value
JsonObject updatedSharedAttributes = new JsonObject(); JsonObject updatedSharedAttributes = new JsonObject();
@ -307,15 +297,10 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
gatewayUpdatedSharedAttributeValue.addProperty("device", createdDevice.getName()); gatewayUpdatedSharedAttributeValue.addProperty("device", createdDevice.getName());
gatewayUpdatedSharedAttributeValue.add("data", updatedSharedAttributes); gatewayUpdatedSharedAttributeValue.add("data", updatedSharedAttributes);
ResponseEntity updatedSharedAttributesResponse = restClient.getRestTemplate() testRestClient.postTelemetryAttribute(DEVICE, createdDevice.getId(), SHARED_SCOPE, mapper.readTree(updatedSharedAttributes.toString()));
.postForEntity(HTTPS_URL + "/api/plugins/telemetry/DEVICE/{deviceId}/SHARED_SCOPE",
mapper.readTree(updatedSharedAttributes.toString()), ResponseEntity.class,
createdDevice.getId());
Assert.assertTrue(updatedSharedAttributesResponse.getStatusCode().is2xxSuccessful());
event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); event = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertEquals(updatedSharedAttributeValue, assertThat(mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText())
mapper.readValue(Objects.requireNonNull(event).getMessage(), JsonNode.class).get("data").get(sharedAttributeName).asText()); .isEqualTo(updatedSharedAttributeValue);
} }
@Test @Test
@ -331,14 +316,11 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
serverRpcPayload.addProperty("method", "getValue"); serverRpcPayload.addProperty("method", "getValue");
serverRpcPayload.addProperty("params", true); serverRpcPayload.addProperty("params", true);
ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getClass().getSimpleName()))); ListeningExecutorService service = MoreExecutors.listeningDecorator(Executors.newSingleThreadExecutor(ThingsBoardThreadFactory.forName(getClass().getSimpleName())));
ListenableFuture<ResponseEntity> future = service.submit(() -> { ListenableFuture<JsonNode> future = service.submit(() -> {
try { try {
return restClient.getRestTemplate() return testRestClient.postServerSideRpc(createdDevice.getId(), mapper.readTree(serverRpcPayload.toString()));
.postForEntity(HTTPS_URL + "/api/rpc/twoway/{deviceId}",
mapper.readTree(serverRpcPayload.toString()), String.class,
createdDevice.getId());
} catch (IOException e) { } catch (IOException e) {
return ResponseEntity.badRequest().build(); return null;
} }
}); });
@ -346,19 +328,13 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent requestFromServer = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
service.shutdownNow(); service.shutdownNow();
Assert.assertNotNull(requestFromServer); assertThat(requestFromServer).isNotNull();
Assert.assertNotNull(requestFromServer.getMessage()); assertThat(requestFromServer.getMessage()).isNotNull();
JsonNode requestFromServerJson = JacksonUtil.toJsonNode(requestFromServer.getMessage());
JsonObject requestFromServerJson = new JsonParser().parse(requestFromServer.getMessage()).getAsJsonObject(); assertThat(requestFromServerJson.get("device").asText()).isEqualTo(createdDevice.getName());
assertThat(requestFromServerJson.get("data").get("method").asText()).isEqualTo("getValue");
Assert.assertEquals(createdDevice.getName(), requestFromServerJson.get("device").getAsString()); assertThat(requestFromServerJson.get("data").get("params").asText()).isEqualTo("true");
int requestId = requestFromServerJson.get("data").get("id").asInt();
JsonObject requestFromServerData = requestFromServerJson.get("data").getAsJsonObject();
Assert.assertEquals("getValue", requestFromServerData.get("method").getAsString());
Assert.assertTrue(requestFromServerData.get("params").getAsBoolean());
int requestId = requestFromServerData.get("id").getAsInt();
JsonObject clientResponse = new JsonObject(); JsonObject clientResponse = new JsonObject();
clientResponse.addProperty("response", "someResponse"); clientResponse.addProperty("response", "someResponse");
@ -369,16 +345,15 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
// Send a response to the server's RPC request // Send a response to the server's RPC request
mqttClient.publish(gatewayRpcTopic, Unpooled.wrappedBuffer(gatewayResponse.toString().getBytes())).get(); mqttClient.publish(gatewayRpcTopic, Unpooled.wrappedBuffer(gatewayResponse.toString().getBytes())).get();
ResponseEntity serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS); JsonNode serverResponse = future.get(5 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertTrue(serverResponse.getStatusCode().is2xxSuccessful());
Assert.assertEquals(clientResponse.toString(), serverResponse.getBody()); assertThat(serverResponse).isEqualTo(mapper.readTree(clientResponse.toString()));
} }
@Test @Test
public void deviceCreationAfterDeleted() throws Exception { public void deviceCreationAfterDeleted() throws Exception {
restClient.getRestTemplate().delete(HTTPS_URL + "/api/device/" + this.createdDevice.getId()); testRestClient.deleteDevice(this.createdDevice.getId());
Optional<Device> deletedDevice = restClient.getDeviceById(this.createdDevice.getId()); testRestClient.getDeviceById(this.createdDevice.getId(), HttpStatus.NOT_FOUND.value());
Assert.assertTrue(deletedDevice.isEmpty());
this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice); this.createdDevice = createDeviceThroughGateway(mqttClient, gatewayDevice);
} }
@ -397,13 +372,13 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
log.info(gatewayAttributesRequest.toString()); log.info(gatewayAttributesRequest.toString());
mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(gatewayAttributesRequest.toString().getBytes())).get(); mqttClient.publish("v1/gateway/attributes/request", Unpooled.wrappedBuffer(gatewayAttributesRequest.toString().getBytes())).get();
MqttEvent clientAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS); MqttEvent clientAttributeEvent = listener.getEvents().poll(10 * timeoutMultiplier, TimeUnit.SECONDS);
Assert.assertNotNull(clientAttributeEvent); assertThat(clientAttributeEvent).isNotNull();
JsonObject responseMessage = new JsonParser().parse(Objects.requireNonNull(clientAttributeEvent).getMessage()).getAsJsonObject(); JsonObject responseMessage = new JsonParser().parse(Objects.requireNonNull(clientAttributeEvent).getMessage()).getAsJsonObject();
Assert.assertEquals(messageId, responseMessage.get("id").getAsInt()); assertThat(responseMessage.get("id").getAsInt()).isEqualTo(messageId);
Assert.assertEquals(createdDevice.getName(), responseMessage.get("device").getAsString()); assertThat(responseMessage.get("device").getAsString()).isEqualTo(createdDevice.getName());
Assert.assertEquals(3, responseMessage.entrySet().size()); assertThat(responseMessage.entrySet()).hasSize(3);
Assert.assertEquals(expectedValue, responseMessage.get("value").getAsString()); assertThat(responseMessage.get("value").getAsString()).isEqualTo(expectedValue);
} }
private Device createDeviceThroughGateway(MqttClient mqttClient, Device gatewayDevice) throws Exception { private Device createDeviceThroughGateway(MqttClient mqttClient, Device gatewayDevice) throws Exception {
@ -411,24 +386,19 @@ public class MqttGatewayClientTest extends AbstractContainerTest {
TimeUnit.SECONDS.sleep(30); TimeUnit.SECONDS.sleep(30);
} }
String deviceName = "mqtt_device"; String deviceName = "mqtt_device" + RandomStringUtils.randomAlphabetic(5);
mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes()), MqttQoS.AT_LEAST_ONCE).get(); mqttClient.publish("v1/gateway/connect", Unpooled.wrappedBuffer(createGatewayConnectPayload(deviceName).toString().getBytes()), MqttQoS.AT_LEAST_ONCE).get();
if (timeoutMultiplier > 1) { if (timeoutMultiplier > 1) {
TimeUnit.SECONDS.sleep(30); TimeUnit.SECONDS.sleep(30);
} }
List<EntityRelation> relations = restClient.findByFrom(gatewayDevice.getId(), RelationTypeGroup.COMMON); List<EntityRelation> relations = testRestClient.findRelationByFrom(gatewayDevice.getId(), RelationTypeGroup.COMMON);
assertThat(relations).hasSize(1);
Assert.assertEquals(1, relations.size());
EntityId createdEntityId = relations.get(0).getTo(); EntityId createdEntityId = relations.get(0).getTo();
DeviceId createdDeviceId = new DeviceId(createdEntityId.getId()); DeviceId createdDeviceId = new DeviceId(createdEntityId.getId());
Optional<Device> createdDevice = restClient.getDeviceById(createdDeviceId); return testRestClient.getDeviceById(createdDeviceId);
Assert.assertTrue(createdDevice.isPresent());
return createdDevice.get();
} }
private MqttClient getMqttClient(DeviceCredentials deviceCredentials, MqttMessageListener listener) throws InterruptedException, ExecutionException { private MqttClient getMqttClient(DeviceCredentials deviceCredentials, MqttMessageListener listener) throws InterruptedException, ExecutionException {

40
msa/black-box-tests/src/test/java/org/thingsboard/server/msa/prototypes/DevicePrototypes.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2022 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.msa.prototypes;
import com.fasterxml.jackson.databind.JsonNode;
import org.testcontainers.shaded.org.apache.commons.lang3.RandomStringUtils;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
public class DevicePrototypes {
public static Device defaultDevicePrototype(String name){
Device device = new Device();
device.setName(name + RandomStringUtils.randomAlphanumeric(7));
device.setType("DEFAULT");
return device;
}
public static Device defaultGatewayPrototype() {
String isGateway = "{\"gateway\":true}";
JsonNode additionalInfo = JacksonUtil.toJsonNode(isGateway);
Device gatewayDeviceTemplate = new Device();
gatewayDeviceTemplate.setName("mqtt_gateway_" + RandomStringUtils.randomAlphanumeric(5));
gatewayDeviceTemplate.setType("gateway");
gatewayDeviceTemplate.setAdditionalInfo(additionalInfo);
return gatewayDeviceTemplate;
}
}

2
msa/black-box-tests/src/test/resources/config.properties

@ -0,0 +1,2 @@
tb.baseUrl=http://localhost:8080
tb.wsUrl=ws://localhost:8080

27
msa/black-box-tests/src/test/resources/testNG.xml

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="ISO-8859-1"?>
<!--
Copyright © 2016-2022 The Thingsboard Authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE suite SYSTEM "https://testng.org/testng-1.0.dtd">
<suite name="Black-box tests">
<test verbose="2" name="Connectivity tests" preserve-order="false">
<packages>
<package name="org.thingsboard.server.msa.connectivity" />
</packages>
</test>
</suite>

28
pom.xml

@ -134,6 +134,10 @@
<spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 --> <spring-test-dbunit.version>1.3.0</spring-test-dbunit.version> <!-- 2016 -->
<takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 --> <takari-cpsuite.version>1.2.7</takari-cpsuite.version> <!-- 2015 -->
<!-- BLACKBOX TEST SCOPE --> <!-- BLACKBOX TEST SCOPE -->
<testng.version>7.6.1</testng.version>
<assertj.version>3.23.1</assertj.version>
<rest-assured.version>5.2.0</rest-assured.version>
<hamcrest.version>1.3</hamcrest.version>
<testcontainers.version>1.17.3</testcontainers.version> <testcontainers.version>1.17.3</testcontainers.version>
<zeroturnaround.version>1.12</zeroturnaround.version> <zeroturnaround.version>1.12</zeroturnaround.version>
<opensmpp.version>3.0.0</opensmpp.version> <opensmpp.version>3.0.0</opensmpp.version>
@ -1634,6 +1638,30 @@
</exclusion> </exclusion>
</exclusions> </exclusions>
</dependency> </dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>${assertj.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.rest-assured</groupId>
<artifactId>rest-assured</artifactId>
<version>${rest-assured.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>${hamcrest.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.awaitility</groupId> <groupId>org.awaitility</groupId>
<artifactId>awaitility</artifactId> <artifactId>awaitility</artifactId>

Loading…
Cancel
Save