Browse Source

Integration-tests subproject

pull/1154/head
Viacheslav Kukhtyn 8 years ago
parent
commit
4b0554b274
  1. 18
      msa/integration-tests/README.md
  2. 98
      msa/integration-tests/pom.xml
  3. 112
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java
  4. 37
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java
  5. 57
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/WsClient.java
  6. 40
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/WsTelemetryResponse.java
  7. 57
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java
  8. 111
      msa/integration-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java
  9. 1
      msa/pom.xml

18
msa/integration-tests/README.md

@ -0,0 +1,18 @@
## Integration tests execution
To run the integration tests with using Docker, the local Docker images of Thingsboard's microservices should be built. <br />
- Build the local Docker images in the directory with the Thingsboard's main [pom.xml](./../../pom.xml):
mvn clean install -Ddockerfile.skip=false
- Verify that the new local images were built:
docker image ls
As result, in REPOSITORY column, next images should be present:
local-maven-build/tb-node
local-maven-build/tb-web-ui
local-maven-build/tb-web-ui
- Run the integration tests in the [msa/integration-tests](../integration-tests) directory:
mvn clean install -Dintegrationtests.skip=false

98
msa/integration-tests/pom.xml

@ -0,0 +1,98 @@
<!--
Copyright © 2016-2018 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.thingsboard</groupId>
<version>2.2.0-SNAPSHOT</version>
<artifactId>msa</artifactId>
</parent>
<groupId>org.thingsboard.msa</groupId>
<artifactId>integration-tests</artifactId>
<name>ThingsBoard Integration Tests</name>
<url>https://thingsboard.io</url>
<description>Project for ThingsBoard integration tests with using Docker</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<main.dir>${basedir}/../..</main.dir>
<integrationtests.skip>true</integrationtests.skip>
<testcontainers.version>1.9.1</testcontainers.version>
<java-websocket.version>1.3.9</java-websocket.version>
</properties>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>${testcontainers.version}</version>
</dependency>
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>${java-websocket.version}</version>
</dependency>
<dependency>
<groupId>io.takari.junit</groupId>
<artifactId>takari-cpsuite</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>netty-mqtt</artifactId>
</dependency>
<dependency>
<groupId>org.thingsboard</groupId>
<artifactId>tools</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*TestSuite.java</include>
</includes>
<skipTests>${integrationtests.skip}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
</project>

112
msa/integration-tests/src/test/java/org/thingsboard/server/msa/AbstractContainerTest.java

@ -0,0 +1,112 @@
/**
* Copyright © 2016-2018 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.ObjectMapper;
import com.google.common.collect.ImmutableMap;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.*;
import org.thingsboard.client.tools.RestClient;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.id.DeviceId;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@Slf4j
public abstract class AbstractContainerTest {
protected static String httpUrl;
protected static String wsUrl;
protected static RestClient restClient;
protected ObjectMapper mapper = new ObjectMapper();
@BeforeClass
public static void before() {
httpUrl = "http://localhost:" + ContainerTestSuite.composeContainer.getServicePort("tb-web-ui1", ContainerTestSuite.EXPOSED_PORT);
wsUrl = "ws://localhost:" + ContainerTestSuite.composeContainer.getServicePort("tb-web-ui1", ContainerTestSuite.EXPOSED_PORT);
restClient = new RestClient(httpUrl);
}
protected Device createDevice(String name) {
return restClient.createDevice(name + RandomStringUtils.randomAlphanumeric(7), "DEFAULT");
}
protected WsClient subscribeToTelemetryWebSocket(DeviceId deviceId) throws URISyntaxException, InterruptedException {
WsClient mWs = new WsClient(new URI(wsUrl + "/api/ws/plugins/telemetry?token=" + restClient.getToken()));
mWs.connectBlocking(1, TimeUnit.SECONDS);
JsonObject tsSubCmd = new JsonObject();
tsSubCmd.addProperty("entityType", EntityType.DEVICE.name());
tsSubCmd.addProperty("entityId", deviceId.toString());
tsSubCmd.addProperty("scope", "LATEST_TELEMETRY");
tsSubCmd.addProperty("cmdId", new Random().nextInt(100));
tsSubCmd.addProperty("unsubscribe", false);
JsonArray wsTsSubCmds = new JsonArray();
wsTsSubCmds.add(tsSubCmd);
JsonObject wsRequest = new JsonObject();
wsRequest.add("tsSubCmds", wsTsSubCmds);
wsRequest.add("historyCmds", new JsonArray());
wsRequest.add("attrSubCmds", new JsonArray());
mWs.send(wsRequest.toString());
return mWs;
}
protected Map<String, Long> getExpectedLatestValues(long ts) {
return ImmutableMap.<String, Long>builder()
.put("booleanKey", ts)
.put("stringKey", ts)
.put("doubleKey", ts)
.put("longKey", ts)
.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 createPayload(long ts) {
JsonObject values = createPayload();
JsonObject payload = new JsonObject();
payload.addProperty("ts", ts);
payload.add("values", values);
return payload;
}
protected JsonObject createPayload() {
JsonObject values = new JsonObject();
values.addProperty("stringKey", "value1");
values.addProperty("booleanKey", true);
values.addProperty("doubleKey", 42.0);
values.addProperty("longKey", 73L);
return values;
}
}

37
msa/integration-tests/src/test/java/org/thingsboard/server/msa/ContainerTestSuite.java

@ -0,0 +1,37 @@
/**
* Copyright © 2016-2018 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 org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
import org.testcontainers.containers.DockerComposeContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import java.io.File;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.msa.*"})
public class ContainerTestSuite {
static final int EXPOSED_PORT = 8080;
@ClassRule
public static DockerComposeContainer composeContainer = new DockerComposeContainer(new File("./../docker/docker-compose.yml"))
.withPull(false)
.withLocalCompose(true)
.withTailChildContainers(true)
.withExposedService("tb-web-ui1", EXPOSED_PORT, Wait.forHttp("/login"));
}

57
msa/integration-tests/src/test/java/org/thingsboard/server/msa/WsClient.java

@ -0,0 +1,57 @@
/**
* Copyright © 2016-2018 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 org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import java.net.URI;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class WsClient extends WebSocketClient {
private final BlockingQueue<String> events;
private String message;
public WsClient(URI serverUri) {
super(serverUri);
events = new ArrayBlockingQueue<>(100);
}
@Override
public void onOpen(ServerHandshake serverHandshake) {
}
@Override
public void onMessage(String message) {
events.add(message);
this.message = message;
}
@Override
public void onClose(int code, String reason, boolean remote) {
events.clear();
}
@Override
public void onError(Exception ex) {
ex.printStackTrace();
}
public String getLastMessage() {
return this.message;
}
}

40
msa/integration-tests/src/test/java/org/thingsboard/server/msa/WsTelemetryResponse.java

@ -0,0 +1,40 @@
/**
* Copyright © 2016-2018 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.Data;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Data
public class WsTelemetryResponse implements Serializable {
private int subscriptionId;
private int errorCode;
private String errorMsg;
private Map<String, List<List<Object>>> data;
private Map<String, Object> latestValues;
public List<Object> getDataValuesByKey(String key) {
return data.entrySet().stream()
.filter(e -> e.getKey().equals(key))
.flatMap(e -> e.getValue().stream().flatMap(Collection::stream))
.collect(Collectors.toList());
}
}

57
msa/integration-tests/src/test/java/org/thingsboard/server/msa/connectivity/HttpClientTest.java

@ -0,0 +1,57 @@
/**
* Copyright © 2016-2018 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.connectivity;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.http.ResponseEntity;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.WsTelemetryResponse;
import java.util.concurrent.TimeUnit;
public class HttpClientTest extends AbstractContainerTest {
@Test
public void telemetryUpdate() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant");
Device device = createDevice("http_");
DeviceCredentials deviceCredentials = restClient.getCredentials(device.getId());
WsClient mWs = subscribeToTelemetryWebSocket(device.getId());
ResponseEntity deviceTelemetryResponse = restClient.getRestTemplate()
.postForEntity(httpUrl + "/api/v1/{credentialsId}/telemetry",
mapper.readTree(createPayload().toString()),
ResponseEntity.class,
deviceCredentials.getCredentialsId());
Assert.assertTrue(deviceTelemetryResponse.getStatusCode().is2xxSuccessful());
TimeUnit.SECONDS.sleep(1);
WsTelemetryResponse actualLatestTelemetry = mapper.readValue(mWs.getLastMessage(), WsTelemetryResponse.class);
Assert.assertEquals(getExpectedLatestValues(123456789L).keySet(), 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.getRestTemplate().delete(httpUrl + "/api/device/" + device.getId());
}
}

111
msa/integration-tests/src/test/java/org/thingsboard/server/msa/connectivity/MqttClientTest.java

@ -0,0 +1,111 @@
/**
* Copyright © 2016-2018 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.connectivity;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import lombok.Data;
import org.junit.*;
import org.thingsboard.mqtt.MqttClient;
import org.thingsboard.mqtt.MqttClientConfig;
import org.thingsboard.mqtt.MqttHandler;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.security.DeviceCredentials;
import org.thingsboard.server.msa.AbstractContainerTest;
import org.thingsboard.server.msa.WsClient;
import org.thingsboard.server.msa.WsTelemetryResponse;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.*;
public class MqttClientTest extends AbstractContainerTest {
@Test
public void telemetryUpload() throws Exception {
restClient.login("tenant@thingsboard.org", "tenant");
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getCredentials(device.getId());
WsClient mWs = subscribeToTelemetryWebSocket(device.getId());
MqttClient mqttClient = getMqttClient(deviceCredentials);
mqttClient.publish("v1/devices/me/telemetry", Unpooled.wrappedBuffer(createPayload().toString().getBytes()));
TimeUnit.SECONDS.sleep(1);
WsTelemetryResponse actualLatestTelemetry = mapper.readValue(mWs.getLastMessage(), WsTelemetryResponse.class);
Assert.assertEquals(getExpectedLatestValues(123456789L).keySet(), 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.getRestTemplate().delete(httpUrl + "/api/device/" + device.getId());
}
@Test
public void telemetryUploadWithTs() throws Exception {
long ts = 1451649600512L;
restClient.login("tenant@thingsboard.org", "tenant");
Device device = createDevice("mqtt_");
DeviceCredentials deviceCredentials = restClient.getCredentials(device.getId());
WsClient mWs = subscribeToTelemetryWebSocket(device.getId());
MqttClient mqttClient = getMqttClient(deviceCredentials);
mqttClient.publish("v1/devices/me/telemetry", Unpooled.wrappedBuffer(createPayload(ts).toString().getBytes()));
TimeUnit.SECONDS.sleep(1);
WsTelemetryResponse actualLatestTelemetry = mapper.readValue(mWs.getLastMessage(), WsTelemetryResponse.class);
Assert.assertEquals(getExpectedLatestValues(ts), actualLatestTelemetry.getLatestValues());
Assert.assertTrue(verify(actualLatestTelemetry, "booleanKey", ts, Boolean.TRUE.toString()));
Assert.assertTrue(verify(actualLatestTelemetry, "stringKey", ts, "value1"));
Assert.assertTrue(verify(actualLatestTelemetry, "doubleKey", ts, Double.toString(42.0)));
Assert.assertTrue(verify(actualLatestTelemetry, "longKey", ts, Long.toString(73)));
restClient.getRestTemplate().delete(httpUrl + "/api/device/" + device.getId());
}
private MqttClient getMqttClient(DeviceCredentials deviceCredentials) throws InterruptedException {
MqttMessageListener queue = new MqttMessageListener();
MqttClientConfig clientConfig = new MqttClientConfig();
clientConfig.setClientId("MQTT client from test");
clientConfig.setUsername(deviceCredentials.getCredentialsId());
MqttClient mqttClient = MqttClient.create(clientConfig, queue);
mqttClient.connect("localhost", 1883).sync();
return mqttClient;
}
@Data
private class MqttMessageListener implements MqttHandler {
private final BlockingQueue<MqttEvent> events;
private MqttMessageListener() {
events = new ArrayBlockingQueue<>(100);
}
@Override
public void onMessage(String topic, ByteBuf message) {
events.add(new MqttEvent(topic, message.toString(StandardCharsets.UTF_8)));
}
}
@Data
private class MqttEvent {
private final String topic;
private final String message;
}
}

1
msa/pom.xml

@ -40,6 +40,7 @@
<module>js-executor</module>
<module>web-ui</module>
<module>tb-node</module>
<module>integration-tests</module>
</modules>
<build>

Loading…
Cancel
Save