subState = new HashMap<>(keys.size());
keys.forEach(key -> subState.put(key, startTs));
data.forEach(v -> subState.put(v.getKey(), v.getTs()));
+ Lock subLock = new ReentrantLock();
TbTimeseriesSubscription sub = TbTimeseriesSubscription.builder()
.serviceId(serviceId)
.sessionId(sessionId)
.subscriptionId(cmd.getCmdId())
.tenantId(sessionRef.getSecurityCtx().getTenantId())
.entityId(entityId)
- .updateConsumer(DefaultTelemetryWebSocketService.this::sendWsMsg)
+ .updateConsumer((sessionId, update) -> {
+ subLock.lock();
+ try {
+ sendWsMsg(sessionId, update);
+ } finally {
+ subLock.unlock();
+ }
+ })
.allKeys(false)
.keyStates(subState).build();
- oldSubService.addSubscription(sub);
+
+ subLock.lock();
+ try{
+ oldSubService.addSubscription(sub);
+ sendWsMsg(sessionRef, new TelemetrySubscriptionUpdate(cmd.getCmdId(), data));
+ } finally {
+ subLock.unlock();
+ }
}
@Override
@@ -793,7 +852,7 @@ public class DefaultTelemetryWebSocketService implements TelemetryWebSocketServi
private void sendWsMsg(TelemetryWebSocketSessionRef sessionRef, int cmdId, Object update) {
try {
- String msg = jsonMapper.writeValueAsString(update);
+ String msg = JacksonUtil.OBJECT_MAPPER.writeValueAsString(update);
executor.submit(() -> {
try {
msgEndpoint.send(sessionRef, cmdId, msg);
diff --git a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java
index ca3876464e..4961f901c5 100644
--- a/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java
+++ b/application/src/main/java/org/thingsboard/server/service/telemetry/sub/AlarmSubscriptionUpdate.java
@@ -17,15 +17,8 @@ package org.thingsboard.server.service.telemetry.sub;
import lombok.Getter;
import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.kv.TsKvEntry;
-import org.thingsboard.server.common.data.query.AlarmData;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.stream.Collectors;
+import org.thingsboard.server.common.data.alarm.AlarmAssigneeUpdate;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
public class AlarmSubscriptionUpdate {
@@ -36,15 +29,15 @@ public class AlarmSubscriptionUpdate {
@Getter
private String errorMsg;
@Getter
- private Alarm alarm;
+ private AlarmInfo alarm;
@Getter
private boolean alarmDeleted;
- public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm) {
+ public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm) {
this(subscriptionId, alarm, false);
}
- public AlarmSubscriptionUpdate(int subscriptionId, Alarm alarm, boolean alarmDeleted) {
+ public AlarmSubscriptionUpdate(int subscriptionId, AlarmInfo alarm, boolean alarmDeleted) {
super();
this.subscriptionId = subscriptionId;
this.alarm = alarm;
@@ -64,7 +57,7 @@ public class AlarmSubscriptionUpdate {
@Override
public String toString() {
- return "AlarmUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg + ", alarm="
- + alarm + "]";
+ return "AlarmUpdate [subscriptionId=" + subscriptionId + ", errorCode=" + errorCode + ", errorMsg=" + errorMsg +
+ ", alarm=" + alarm + "]";
}
-}
+}
\ No newline at end of file
diff --git a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java
index 6ea3208208..ea059fcb2d 100644
--- a/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java
+++ b/application/src/main/java/org/thingsboard/server/service/ttl/AlarmsCleanUpService.java
@@ -21,6 +21,7 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.thingsboard.server.common.data.alarm.Alarm;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.AlarmId;
import org.thingsboard.server.common.data.id.TenantId;
diff --git a/application/src/main/java/org/thingsboard/server/utils/EventDeduplicationExecutor.java b/application/src/main/java/org/thingsboard/server/utils/EventDeduplicationExecutor.java
deleted file mode 100644
index 7ce958ef2d..0000000000
--- a/application/src/main/java/org/thingsboard/server/utils/EventDeduplicationExecutor.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/**
- * Copyright © 2016-2023 The Thingsboard Authors
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.thingsboard.server.utils;
-
-import lombok.extern.slf4j.Slf4j;
-
-import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.function.Consumer;
-
-/**
- * This class deduplicate executions of the specified function.
- * Useful in cluster mode, when you get event about partition change multiple times.
- * Assuming that the function execution is expensive, we should execute it immediately when first time event occurs and
- * later, once the processing of first event is done, process last pending task.
- *
- * @param parameters of the function
- */
-@Slf4j
-public class EventDeduplicationExecutor
{
- private final String name;
- private final ExecutorService executor;
- private final Consumer
function;
- private P pendingTask;
- private boolean busy;
-
- public EventDeduplicationExecutor(String name, ExecutorService executor, Consumer
function) {
- this.name = name;
- this.executor = executor;
- this.function = function;
- }
-
- public void submit(P params) {
- log.info("[{}] Going to submit: {}", name, params);
- synchronized (EventDeduplicationExecutor.this) {
- if (!busy) {
- busy = true;
- pendingTask = null;
- try {
- log.info("[{}] Submitting task: {}", name, params);
- executor.submit(() -> {
- try {
- log.info("[{}] Executing task: {}", name, params);
- function.accept(params);
- } catch (Throwable e) {
- log.warn("[{}] Failed to process task with parameters: {}", name, params, e);
- throw e;
- } finally {
- unlockAndProcessIfAny();
- }
- });
- } catch (Throwable e) {
- log.warn("[{}] Failed to submit task with parameters: {}", name, params, e);
- unlockAndProcessIfAny();
- throw e;
- }
- } else {
- log.info("[{}] Task is already in progress. {} pending task: {}", name, pendingTask == null ? "adding" : "updating", params);
- pendingTask = params;
- }
- }
- }
-
- private void unlockAndProcessIfAny() {
- synchronized (EventDeduplicationExecutor.this) {
- busy = false;
- if (pendingTask != null) {
- submit(pendingTask);
- }
- }
- }
-}
diff --git a/application/src/main/resources/thingsboard.yml b/application/src/main/resources/thingsboard.yml
index 2fb92cdf4d..10c4c9edac 100644
--- a/application/src/main/resources/thingsboard.yml
+++ b/application/src/main/resources/thingsboard.yml
@@ -73,6 +73,8 @@ server:
min_timeout: "${MIN_SERVER_SIDE_RPC_TIMEOUT:5000}"
# Default value of the server side RPC timeout.
default_timeout: "${DEFAULT_SERVER_SIDE_RPC_TIMEOUT:10000}"
+ rate_limits:
+ reset_password_per_user: "${RESET_PASSWORD_PER_USER_RATE_LIMIT_CONFIGURATION:5:3600}"
# Application info
app:
@@ -457,6 +459,9 @@ cache:
versionControlTask:
timeToLiveInMinutes: "${CACHE_SPECS_VERSION_CONTROL_TASK_TTL:5}"
maxSize: "${CACHE_SPECS_VERSION_CONTROL_TASK_MAX_SIZE:100000}"
+ userSettings:
+ timeToLiveInMinutes: "${CACHE_SPECS_USER_SETTINGS_TTL:1440}"
+ maxSize: "${CACHE_SPECS_USER_SETTINGS_MAX_SIZE:100000}"
#Disable this because it is not required.
spring.data.redis.repositories.enabled: false
@@ -568,6 +573,7 @@ spring:
password: "${SPRING_DATASOURCE_PASSWORD:postgres}"
hikari:
maximumPoolSize: "${SPRING_DATASOURCE_MAXIMUM_POOL_SIZE:16}"
+ registerMbeans: "${SPRING_DATASOURCE_HIKARI_REGISTER_MBEANS:false}" # true - enable MBean to diagnose pools state via JMX
# Audit log parameters
audit-log:
@@ -1206,3 +1212,4 @@ management:
exposure:
# Expose metrics endpoint (use value 'prometheus' to enable prometheus metrics).
include: '${METRICS_ENDPOINTS_EXPOSE:info}'
+
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
index 06cee7b71d..32d1f65765 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractControllerTest.java
@@ -52,7 +52,7 @@ public abstract class AbstractControllerTest extends AbstractNotifyEntityTest {
@LocalServerPort
protected int wsPort;
- private TbTestWebSocketClient wsClient; // lazy
+ private volatile TbTestWebSocketClient wsClient; // lazy
public TbTestWebSocketClient getWsClient() {
if (wsClient == null) {
diff --git a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
index 9de2738a8e..8c9121cc13 100644
--- a/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/AbstractWebTest.java
@@ -29,14 +29,19 @@ import lombok.extern.slf4j.Slf4j;
import org.hamcrest.Matcher;
import org.hibernate.exception.ConstraintViolationException;
import org.junit.After;
+import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestRule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.mock.mockito.SpyBean;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
@@ -52,6 +57,9 @@ import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilde
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.context.WebApplicationContext;
+import org.thingsboard.common.util.JacksonUtil;
+import org.thingsboard.rule.engine.api.MailService;
+import org.thingsboard.rule.engine.api.util.TbNodeUtils;
import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType;
@@ -69,6 +77,7 @@ import org.thingsboard.server.common.data.device.profile.MqttTopics;
import org.thingsboard.server.common.data.device.profile.ProtoTransportPayloadConfiguration;
import org.thingsboard.server.common.data.device.profile.TransportPayloadTypeConfiguration;
import org.thingsboard.server.common.data.edge.Edge;
+import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.HasId;
@@ -83,7 +92,6 @@ import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.config.ThingsboardSecurityConfiguration;
import org.thingsboard.server.dao.Dao;
import org.thingsboard.server.dao.tenant.TenantProfileService;
-import org.thingsboard.server.service.mail.TestMailService;
import org.thingsboard.server.service.security.auth.jwt.RefreshTokenRequest;
import org.thingsboard.server.service.security.auth.rest.LoginRequest;
@@ -98,11 +106,13 @@ import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.springframework.security.test.web.servlet.setup.SecurityMockMvcConfigurers.springSecurity;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.asyncDispatch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.request;
@@ -143,6 +153,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected MockMvc mockMvc;
+ protected String currentActivateToken;
+ protected String currentResetPasswordToken;
+
protected String token;
protected String refreshToken;
protected String username;
@@ -154,6 +167,7 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
protected TenantId differentTenantId;
protected CustomerId differentCustomerId;
protected UserId customerUserId;
+ protected UserId differentCustomerUserId;
@SuppressWarnings("rawtypes")
private HttpMessageConverter mappingJackson2HttpMessageConverter;
@@ -167,6 +181,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
@Autowired
private TenantProfileService tenantProfileService;
+ @SpyBean
+ protected MailService mailService;
+
@Rule
public TestRule watcher = new TestWatcher() {
protected void starting(Description description) {
@@ -197,7 +214,9 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
@Before
public void setupWebTest() throws Exception {
- log.info("Executing web test setup");
+ log.debug("Executing web test setup");
+
+ setupMailServiceMock();
if (this.mockMvc == null) {
this.mockMvc = webAppContextSetup(webApplicationContext)
@@ -237,12 +256,33 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
resetTokens();
- log.info("Executed web test setup");
+ log.debug("Executed web test setup");
+ }
+
+ private void setupMailServiceMock() throws ThingsboardException {
+ Mockito.doNothing().when(mailService).sendAccountActivatedEmail(anyString(), anyString());
+ Mockito.doAnswer(new Answer() {
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ String activationLink = (String) args[0];
+ currentActivateToken = activationLink.split("=")[1];
+ return null;
+ }
+ }).when(mailService).sendActivationEmail(anyString(), anyString());
+
+ Mockito.doAnswer(new Answer() {
+ public Void answer(InvocationOnMock invocation) {
+ Object[] args = invocation.getArguments();
+ String passwordResetLink = (String) args[0];
+ currentResetPasswordToken = passwordResetLink.split("=")[1];
+ return null;
+ }
+ }).when(mailService).sendResetPasswordEmailAsync(anyString(), anyString());
}
@After
public void teardownWebTest() throws Exception {
- log.info("Executing web test teardown");
+ log.debug("Executing web test teardown");
loginSysAdmin();
doDelete("/api/tenant/" + tenantId.getId().toString())
@@ -324,7 +364,8 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
differentCustomerUser.setCustomerId(savedDifferentCustomer.getId());
differentCustomerUser.setEmail(DIFFERENT_CUSTOMER_USER_EMAIL);
- createUserAndLogin(differentCustomerUser, DIFFERENT_CUSTOMER_USER_PASSWORD);
+ differentCustomerUser = createUserAndLogin(differentCustomerUser, DIFFERENT_CUSTOMER_USER_PASSWORD);
+ differentCustomerUserId = differentCustomerUser.getId();
}
}
@@ -367,11 +408,11 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
}
private JsonNode getActivateRequest(String password) throws Exception {
- doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
+ doGet("/api/noauth/activate?activateToken={activateToken}", this.currentActivateToken)
.andExpect(status().isSeeOther())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
+ .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + this.currentActivateToken));
return new ObjectMapper().createObjectNode()
- .put("activateToken", TestMailService.currentActivateToken)
+ .put("activateToken", this.currentActivateToken)
.put("password", password);
}
@@ -613,6 +654,22 @@ public abstract class AbstractWebTest extends AbstractInMemoryStorageTest {
return readResponse(doPostAsync(urlTemplate, content, DEFAULT_TIMEOUT, params).andExpect(resultMatcher), responseClass);
}
+ protected T doPut(String urlTemplate, T content, Class responseClass, String... params) {
+ try {
+ return readResponse(doPut(urlTemplate, content, params).andExpect(status().isOk()), responseClass);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ protected ResultActions doPut(String urlTemplate, T content, String... params) throws Exception {
+ MockHttpServletRequestBuilder postRequest = put(urlTemplate, params);
+ setJwtToken(postRequest);
+ String json = json(content);
+ postRequest.contentType(contentType).content(json);
+ return mockMvc.perform(postRequest);
+ }
+
protected T doDelete(String urlTemplate, Class responseClass, String... params) throws Exception {
return readResponse(doDelete(urlTemplate, params).andExpect(status().isOk()), responseClass);
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java
index 021171f1af..e6c27aa4a8 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAdminControllerTest.java
@@ -21,12 +21,9 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.junit.Test;
import org.mockito.Mockito;
-import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.common.util.JacksonUtil;
-import org.thingsboard.rule.engine.api.MailService;
import org.thingsboard.server.common.data.AdminSettings;
import org.thingsboard.server.common.data.security.model.JwtSettings;
-import org.thingsboard.server.service.mail.DefaultMailService;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
@@ -35,6 +32,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -43,12 +42,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
public abstract class BaseAdminControllerTest extends AbstractControllerTest {
final JwtSettings defaultJwtSettings = new JwtSettings(9000, 604800, "thingsboard.io", "thingsboardDefaultSigningKey");
- @Autowired
- MailService mailService;
-
- @Autowired
- DefaultMailService defaultMailService;
-
@Test
public void testFindAdminSettingsByKey() throws Exception {
loginSysAdmin();
@@ -118,10 +111,12 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest {
@Test
public void testSendTestMail() throws Exception {
+ Mockito.doNothing().when(mailService).sendTestMail(any(), anyString());
loginSysAdmin();
AdminSettings adminSettings = doGet("/api/admin/settings/mail", AdminSettings.class);
doPost("/api/admin/settings/testMail", adminSettings)
.andExpect(status().isOk());
+ Mockito.verify(mailService).sendTestMail(Mockito.any(), Mockito.anyString());
}
@Test
@@ -137,15 +132,8 @@ public abstract class BaseAdminControllerTest extends AbstractControllerTest {
adminSettings.setJsonValue(objectNode);
- Mockito.doAnswer((invocations) -> {
- var jsonConfig = (JsonNode) invocations.getArgument(0);
- var email = (String) invocations.getArgument(1);
-
- defaultMailService.sendTestMail(jsonConfig, email);
- return null;
- }).when(mailService).sendTestMail(Mockito.any(), Mockito.anyString());
doPost("/api/admin/settings/testMail", adminSettings).andExpect(status().is5xxServerError());
- Mockito.doNothing().when(mailService).sendTestMail(Mockito.any(), Mockito.any());
+ Mockito.verify(mailService).sendTestMail(Mockito.any(), Mockito.anyString());
}
void resetJwtSettingsToDefault() throws Exception {
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
index 7c8b3c4db8..8566d9d6a4 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmCommentControllerTest.java
@@ -79,7 +79,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
.tenantId(tenantId)
.customerId(customerId)
.originator(customerDevice.getId())
- .status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("test alarm type")
.build();
@@ -316,7 +315,6 @@ public abstract class BaseAlarmCommentControllerTest extends AbstractControllerT
Alarm alarm = Alarm.builder()
.originator(device.getId())
- .status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("Test")
.build();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java
index 21896efc66..777b97d940 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAlarmControllerTest.java
@@ -21,6 +21,7 @@ import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -28,6 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.web.servlet.ResultActions;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.EntityType;
@@ -123,7 +125,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Assert.assertNotNull(updatedAlarm);
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
- testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.UPDATED);
}
@@ -139,8 +142,57 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Assert.assertNotNull(updatedAlarm);
Assert.assertEquals(AlarmSeverity.MAJOR, updatedAlarm.getSeverity());
- testNotifyEntityAllOneTime(updatedAlarm, updatedAlarm.getId(), updatedAlarm.getOriginator(),
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.UPDATED);
+
+ alarm = updatedAlarm;
+ alarm.setAcknowledged(true);
+ alarm.setAckTs(System.currentTimeMillis() - 1000);
+ updatedAlarm = doPost("/api/alarm", alarm, Alarm.class);
+ Assert.assertNotNull(updatedAlarm);
+ Assert.assertTrue(updatedAlarm.isAcknowledged());
+ Assert.assertEquals(alarm.getAckTs(), updatedAlarm.getAckTs());
+
+ foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ACK);
+
+ alarm = updatedAlarm;
+ alarm.setCleared(true);
+ alarm.setClearTs(System.currentTimeMillis() - 1000);
+ updatedAlarm = doPost("/api/alarm", alarm, Alarm.class);
+ Assert.assertNotNull(updatedAlarm);
+ Assert.assertTrue(updatedAlarm.isCleared());
+ Assert.assertEquals(alarm.getClearTs(), updatedAlarm.getClearTs());
+
+ foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_CLEAR);
+
+ alarm = updatedAlarm;
+ alarm.setAssigneeId(tenantAdminUserId);
+ alarm.setAssignTs(System.currentTimeMillis() - 1000);
+ updatedAlarm = doPost("/api/alarm", alarm, Alarm.class);
+ Assert.assertNotNull(updatedAlarm);
+ Assert.assertEquals(tenantAdminUserId, updatedAlarm.getAssigneeId());
+ Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs());
+
+ foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN);
+
+ alarm = updatedAlarm;
+ alarm.setAssigneeId(null);
+ alarm.setAssignTs(System.currentTimeMillis() - 1000);
+ updatedAlarm = doPost("/api/alarm", alarm, Alarm.class);
+ Assert.assertNotNull(updatedAlarm);
+ Assert.assertNull(updatedAlarm.getAssigneeId());
+ Assert.assertEquals(alarm.getAssignTs(), updatedAlarm.getAssignTs());
+
+ foundAlarm = doGet("/api/alarm/info/" + updatedAlarm.getId(), AlarmInfo.class);
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_UNASSIGN);
}
@Test
@@ -186,7 +238,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
- testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
+ testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(),
tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.DELETED);
}
@@ -199,7 +251,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
doDelete("/api/alarm/" + alarm.getId()).andExpect(status().isOk());
- testNotifyEntityOneTimeMsgToEdgeServiceNever(alarm, alarm.getId(), alarm.getOriginator(),
+ testNotifyEntityOneTimeMsgToEdgeServiceNever(new Alarm(alarm), alarm.getId(), alarm.getOriginator(),
tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.DELETED);
}
@@ -244,7 +296,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk());
- Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class);
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm);
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
@@ -260,7 +312,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/alarm/" + alarm.getId() + "/clear").andExpect(status().isOk());
- Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class);
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm);
Assert.assertEquals(AlarmStatus.CLEARED_UNACK, foundAlarm.getStatus());
@@ -277,7 +329,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
doPost("/api/alarm/" + alarm.getId() + "/ack").andExpect(status().isOk());
- Alarm foundAlarm = doGet("/api/alarm/" + alarm.getId(), Alarm.class);
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
Assert.assertNotNull(foundAlarm);
Assert.assertEquals(AlarmStatus.ACTIVE_ACK, foundAlarm.getStatus());
@@ -347,6 +399,129 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
.andExpect(statusReason(containsString(msgErrorPermission)));
}
+ @Test
+ public void testAssignAlarm() throws Exception {
+ loginTenantAdmin();
+ Alarm alarm = createAlarm(TEST_ALARM_TYPE);
+ Mockito.reset(tbClusterService, auditLogService);
+ long beforeAssignmentTs = System.currentTimeMillis();
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk());
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN);
+ }
+
+ @Test
+ public void testAssignAlarmViaDifferentTenant() throws Exception {
+ loginTenantAdmin();
+ Alarm alarm = createAlarm(TEST_ALARM_TYPE);
+
+ loginDifferentTenant();
+
+ Mockito.reset(tbClusterService, auditLogService);
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isForbidden());
+ }
+
+ @Test
+ public void testReassignAlarm() throws Exception {
+ loginTenantAdmin();
+ Alarm alarm = createAlarm(TEST_ALARM_TYPE);
+ Mockito.reset(tbClusterService, auditLogService);
+ long beforeAssignmentTs = System.currentTimeMillis();
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk());
+
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN);
+
+ logout();
+
+ loginCustomerUser();
+ Mockito.reset(tbClusterService, auditLogService);
+ beforeAssignmentTs = System.currentTimeMillis();
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + customerUserId.getId()).andExpect(status().isOk());
+
+ foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(customerUserId, foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_ASSIGN);
+ }
+
+ @Test
+ public void testUnassignAlarm() throws Exception {
+ loginTenantAdmin();
+ Alarm alarm = createAlarm(TEST_ALARM_TYPE);
+ Mockito.reset(tbClusterService, auditLogService);
+ long beforeAssignmentTs = System.currentTimeMillis();
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk());
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN);
+
+ beforeAssignmentTs = System.currentTimeMillis();
+
+ doDelete("/api/alarm/" + alarm.getId() + "/assign").andExpect(status().isOk());
+ foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertNull(foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_UNASSIGN);
+ }
+
+ @Test
+ public void testUnassignTenantAlarmViaCustomer() throws Exception {
+ loginTenantAdmin();
+ Alarm alarm = createAlarm(TEST_ALARM_TYPE);
+ Mockito.reset(tbClusterService, auditLogService);
+ long beforeAssignmentTs = System.currentTimeMillis();
+
+ doPost("/api/alarm/" + alarm.getId() + "/assign/" + tenantAdminUserId.getId()).andExpect(status().isOk());
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(tenantAdminUserId, foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, tenantAdminUserId, TENANT_ADMIN_EMAIL, ActionType.ALARM_ASSIGN);
+
+ logout();
+ loginCustomerUser();
+
+ Mockito.reset(tbClusterService, auditLogService);
+ beforeAssignmentTs = System.currentTimeMillis();
+
+ doDelete("/api/alarm/" + alarm.getId() + "/assign").andExpect(status().isOk());
+ foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertNull(foundAlarm.getAssigneeId());
+ Assert.assertTrue(foundAlarm.getAssignTs() > beforeAssignmentTs && foundAlarm.getAssignTs() < System.currentTimeMillis());
+
+ testNotifyEntityAllOneTime(foundAlarm, foundAlarm.getId(), foundAlarm.getOriginator(),
+ tenantId, customerId, customerUserId, CUSTOMER_USER_EMAIL, ActionType.ALARM_UNASSIGN);
+ }
+
@Test
public void testFindAlarmsViaCustomerUser() throws Exception {
loginCustomerUser();
@@ -363,7 +538,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
var response = doGetTyped(
"/api/alarm/" + EntityType.DEVICE + "/"
+ customerDevice.getUuidId() + "?page=0&pageSize=" + size,
- new TypeReference>() {}
+ new TypeReference>() {
+ }
);
var foundAlarmInfos = response.getData();
Assert.assertNotNull("Found pageData is null", foundAlarmInfos);
@@ -411,7 +587,6 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Alarm alarm = Alarm.builder()
.originator(device.getId())
- .status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type("Test")
.build();
@@ -430,7 +605,8 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
this.token = tokens.get("token").asText();
PageData pageData = doGetTyped(
- "/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=1", new TypeReference>() {}
+ "/api/alarm/DEVICE/" + device.getUuidId() + "?page=0&pageSize=1", new TypeReference>() {
+ }
);
Assert.assertNotNull("Found pageData is null", pageData);
@@ -441,7 +617,6 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
Assert.assertTrue("Created alarm doesn't match the found one!", equals);
}
-
@Test
public void testDeleteAlarmWithDeleteRelationsOk() throws Exception {
loginCustomerUser();
@@ -449,6 +624,7 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
testEntityDaoWithRelationsOk(customerDevice.getId(), alarmId, "/api/alarm/" + alarmId);
}
+ @Ignore
@Test
public void testDeleteAlarmExceptionWithRelationsTransactional() throws Exception {
loginCustomerUser();
@@ -456,12 +632,11 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
testEntityDaoWithRelationsTransactionalException(alarmDao, customerDevice.getId(), alarmId, "/api/alarm/" + alarmId);
}
- private Alarm createAlarm(String type) throws Exception {
+ private AlarmInfo createAlarm(String type) throws Exception {
Alarm alarm = Alarm.builder()
.tenantId(tenantId)
.customerId(customerId)
.originator(customerDevice.getId())
- .status(AlarmStatus.ACTIVE_UNACK)
.severity(AlarmSeverity.CRITICAL)
.type(type)
.build();
@@ -469,6 +644,10 @@ public abstract class BaseAlarmControllerTest extends AbstractControllerTest {
alarm = doPost("/api/alarm", alarm, Alarm.class);
Assert.assertNotNull(alarm);
- return alarm;
+ AlarmInfo foundAlarm = doGet("/api/alarm/info/" + alarm.getId(), AlarmInfo.class);
+ Assert.assertNotNull(foundAlarm);
+ Assert.assertEquals(alarm, new Alarm(foundAlarm));
+
+ return foundAlarm;
}
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
index 2893f98217..545cc0522d 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseAssetControllerTest.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -980,6 +981,7 @@ public abstract class BaseAssetControllerTest extends AbstractControllerTest {
testEntityDaoWithRelationsOk(savedTenant.getId(), assetId, "/api/asset/" + assetId);
}
+ @Ignore
@Test
public void testDeleteAssetExceptionWithRelationsTransactional() throws Exception {
AssetId assetId = createAsset("Asset for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
index c745465684..53e18c6673 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseCustomerControllerTest.java
@@ -23,6 +23,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -428,6 +429,7 @@ public abstract class BaseCustomerControllerTest extends AbstractControllerTest
testEntityDaoWithRelationsOk(savedTenant.getId(), customerId, "/api/customer/" + customerId);
}
+ @Ignore
@Test
public void testDeleteCustomerExceptionWithRelationsTransactional() throws Exception {
CustomerId customerId = createCustomer("Customer for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
index e5faad32ae..6bb747cbfe 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceControllerTest.java
@@ -24,6 +24,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -1324,6 +1325,7 @@ public abstract class BaseDeviceControllerTest extends AbstractControllerTest {
testEntityDaoWithRelationsOk(savedTenant.getId(), deviceId, "/api/device/" + deviceId);
}
+ @Ignore
@Test
public void testDeleteDeviceExceptionWithRelationsTransactional() throws Exception {
DeviceId deviceId = createDevice("Device for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
index ece621d908..1546e0814e 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseDeviceProfileControllerTest.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -990,6 +991,7 @@ public abstract class BaseDeviceProfileControllerTest extends AbstractController
testEntityDaoWithRelationsOk(savedTenant.getId(), deviceProfileId, "/api/deviceProfile/" + deviceProfileId);
}
+ @Ignore
@Test
public void testDeleteDeviceProfileExceptionWithRelationsTransactional() throws Exception {
DeviceProfileId deviceProfileId = savedDeviceProfile("DeviceProfile for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
index e1ac1061c9..731b5570cc 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEdgeControllerTest.java
@@ -24,6 +24,7 @@ import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -931,6 +932,7 @@ public abstract class BaseEdgeControllerTest extends AbstractControllerTest {
testEntityDaoWithRelationsOk(savedTenant.getId(), edgeId, "/api/edge/" + edgeId);
}
+ @Ignore
@Test
public void testDeleteEdgeExceptionWithRelationsTransactional() throws Exception {
EdgeId edgeId = savedEdge("Edge for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
index 7a9d8f69a4..d29f572758 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseEntityViewControllerTest.java
@@ -30,6 +30,7 @@ import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -845,6 +846,7 @@ public abstract class BaseEntityViewControllerTest extends AbstractControllerTes
testEntityDaoWithRelationsOk(tenantId, entityViewId, "/api/entityView/" + entityViewId);
}
+ @Ignore
@Test
public void testDeleteEntityViewExceptionWithRelationsTransactional() throws Exception {
EntityViewId entityViewId = getNewSavedEntityView("EntityView for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
index 70e7cf7a6f..ba52cea1ec 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseRuleChainControllerTest.java
@@ -19,6 +19,7 @@ import com.fasterxml.jackson.core.type.TypeReference;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -253,6 +254,7 @@ public abstract class BaseRuleChainControllerTest extends AbstractControllerTest
testEntityDaoWithRelationsOk(savedTenant.getId(), ruleChainId, "/api/ruleChain/" + ruleChainId);
}
+ @Ignore
@Test
public void testDeleteRuleChainExceptionWithRelationsTransactional() throws Exception {
RuleChainId ruleChainId = createRuleChain("RuleChain for Test WithRelations Transactional Exception").getId();
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
index c7934d13aa..7149f52630 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseUserControllerTest.java
@@ -20,6 +20,7 @@ import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.After;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import org.mockito.AdditionalAnswers;
import org.mockito.Mockito;
@@ -33,6 +34,7 @@ import org.thingsboard.server.common.data.Customer;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.Tenant;
import org.thingsboard.server.common.data.User;
+import org.thingsboard.server.common.data.UserEmailInfo;
import org.thingsboard.server.common.data.audit.ActionType;
import org.thingsboard.server.common.data.id.CustomerId;
import org.thingsboard.server.common.data.id.TenantId;
@@ -42,15 +44,17 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.exception.DataValidationException;
import org.thingsboard.server.dao.user.UserDao;
-import org.thingsboard.server.service.mail.TestMailService;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@@ -60,6 +64,7 @@ import static org.thingsboard.server.dao.model.ModelConstants.SYSTEM_TENANT;
public abstract class BaseUserControllerTest extends AbstractControllerTest {
private IdComparator idComparator = new IdComparator<>();
+ private IdComparator userDataIdComparator = new IdComparator<>();
private CustomerId customerNUULId = (CustomerId) createEntityId_NULL_UUID(new Customer());
@@ -83,21 +88,15 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testSaveUser() throws Exception {
loginSysAdmin();
- String email = "tenant2@thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
-
+ User user = createTenantAdminUser();
+ String email = user.getEmail();
Mockito.reset(tbClusterService, auditLogService);
User savedUser = doPost("/api/user", user, User.class);
Assert.assertNotNull(savedUser);
Assert.assertNotNull(savedUser.getId());
Assert.assertTrue(savedUser.getCreatedTime() > 0);
- Assert.assertEquals(user.getEmail(), savedUser.getEmail());
+ Assert.assertEquals(email, savedUser.getEmail());
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
Assert.assertEquals(foundUser, savedUser);
@@ -108,12 +107,12 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
resetTokens();
- doGet("/api/noauth/activate?activateToken={activateToken}", TestMailService.currentActivateToken)
+ doGet("/api/noauth/activate?activateToken={activateToken}", this.currentActivateToken)
.andExpect(status().isSeeOther())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + TestMailService.currentActivateToken));
+ .andExpect(header().string(HttpHeaders.LOCATION, "/login/createPassword?activateToken=" + this.currentActivateToken));
JsonNode activateRequest = new ObjectMapper().createObjectNode()
- .put("activateToken", TestMailService.currentActivateToken)
+ .put("activateToken", this.currentActivateToken)
.put("password", "testPassword");
JsonNode tokenInfo = readResponse(doPost("/api/noauth/activate", activateRequest).andExpect(status().isOk()), JsonNode.class);
@@ -152,13 +151,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
- String email = "tenant2@thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName(StringUtils.randomAlphabetic(300));
- user.setLastName("Downs");
+ User user = createTenantAdminUser(StringUtils.randomAlphabetic(300), "Brown");
String msgError = msgErrorFieldLength("first name");
doPost("/api/user", user)
.andExpect(status().isBadRequest())
@@ -185,12 +178,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testUpdateUserFromDifferentTenant() throws Exception {
loginSysAdmin();
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(tenantId);
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
+ User tenantAdmin = createTenantAdminUser();
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1");
loginDifferentTenant();
@@ -210,14 +198,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testResetPassword() throws Exception {
loginSysAdmin();
- String email = "tenant2@thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
-
+ User user = createTenantAdminUser();
+ String email = user.getEmail();
User savedUser = createUserAndLogin(user, "testPassword1");
resetTokens();
@@ -227,17 +209,19 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
doPost("/api/noauth/resetPasswordByEmail", resetPasswordByEmailRequest)
.andExpect(status().isOk());
Thread.sleep(1000);
- doGet("/api/noauth/resetPassword?resetToken={resetToken}", TestMailService.currentResetPasswordToken)
+ doGet("/api/noauth/resetPassword?resetToken={resetToken}", this.currentResetPasswordToken)
.andExpect(status().isSeeOther())
- .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + TestMailService.currentResetPasswordToken));
+ .andExpect(header().string(HttpHeaders.LOCATION, "/login/resetPassword?resetToken=" + this.currentResetPasswordToken));
JsonNode resetPasswordRequest = new ObjectMapper().createObjectNode()
- .put("resetToken", TestMailService.currentResetPasswordToken)
+ .put("resetToken", this.currentResetPasswordToken)
.put("password", "testPassword2");
+ Mockito.doNothing().when(mailService).sendPasswordWasResetEmail(anyString(), anyString());
JsonNode tokenInfo = readResponse(
doPost("/api/noauth/resetPassword", resetPasswordRequest)
.andExpect(status().isOk()), JsonNode.class);
+ Mockito.verify(mailService).sendPasswordWasResetEmail(anyString(), anyString());
validateAndSetJwtToken(tokenInfo, email);
doGet("/api/auth/user")
@@ -262,13 +246,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testFindUserById() throws Exception {
loginSysAdmin();
- String email = "tenant2@thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
+ User user = createTenantAdminUser();
User savedUser = doPost("/api/user", user, User.class);
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
@@ -282,15 +260,12 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
- String email = TENANT_ADMIN_EMAIL;
User user = new User();
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
+ user.setEmail(TENANT_ADMIN_EMAIL);
- String msgError = "User with email '" + email + "' already present in database";
+ String msgError = "User with email '" + TENANT_ADMIN_EMAIL + "' already present in database";
doPost("/api/user", user)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(msgError)));
@@ -307,12 +282,8 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
Mockito.reset(tbClusterService, auditLogService);
String email = "tenant_thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
+ User user = createTenantAdminUser();
user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
String msgError = "Invalid email address format '" + email + "'";
doPost("/api/user", user)
@@ -373,13 +344,7 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testDeleteUser() throws Exception {
loginSysAdmin();
- String email = "tenant2@thingsboard.org";
- User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
- user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
+ User user = createTenantAdminUser();
User savedUser = doPost("/api/user", user, User.class);
User foundUser = doGet("/api/user/" + savedUser.getId().getId().toString(), User.class);
@@ -561,20 +526,10 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testFindCustomerUsers() throws Exception {
loginSysAdmin();
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(tenantId);
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
+ User tenantAdmin = createTenantAdminUser();
createUserAndLogin(tenantAdmin, "testPassword1");
- Customer customer = new Customer();
- customer.setTitle("My customer");
- Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
-
- CustomerId customerId = savedCustomer.getId();
+ CustomerId customerId = postCustomer();
List customerUsers = new ArrayList<>();
for (int i = 0; i < 56; i++) {
@@ -611,47 +566,22 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
public void testFindCustomerUsersByEmail() throws Exception {
loginSysAdmin();
- User tenantAdmin = new User();
- tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
- tenantAdmin.setTenantId(tenantId);
- tenantAdmin.setEmail("tenant2@thingsboard.org");
- tenantAdmin.setFirstName("Joe");
- tenantAdmin.setLastName("Downs");
-
+ User tenantAdmin = createTenantAdminUser();
createUserAndLogin(tenantAdmin, "testPassword1");
- Customer customer = new Customer();
- customer.setTitle("My customer");
- Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
-
- CustomerId customerId = savedCustomer.getId();
+ CustomerId customerId = postCustomer();
String email1 = "testEmail1";
- List customerUsersEmail1 = new ArrayList<>();
-
- for (int i = 0; i < 74; i++) {
- User user = new User();
- user.setAuthority(Authority.CUSTOMER_USER);
- user.setCustomerId(customerId);
- String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
- String email = email1 + suffix + "@thingsboard.org";
- email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
- user.setEmail(email);
- customerUsersEmail1.add(doPost("/api/user", user, User.class));
- }
-
String email2 = "testEmail2";
- List customerUsersEmail2 = new ArrayList<>();
-
- for (int i = 0; i < 92; i++) {
- User user = new User();
- user.setAuthority(Authority.CUSTOMER_USER);
- user.setCustomerId(customerId);
- String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
- String email = email2 + suffix + "@thingsboard.org";
- email = i % 2 == 0 ? email.toLowerCase() : email.toUpperCase();
- user.setEmail(email);
- customerUsersEmail2.add(doPost("/api/user", user, User.class));
+ List customerUsersEmail1 = new ArrayList<>();
+ List customerUsersEmail2= new ArrayList<>();
+ for (int i = 0; i < 45; i++) {
+ User customerUser = createCustomerUser( customerId);
+ customerUser.setEmail(email1 + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org");
+ customerUsersEmail1.add(doPost("/api/user", customerUser, User.class));
+
+ customerUser.setEmail(email2 + StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10)) + "@thingsboard.org");
+ customerUsersEmail2.add(doPost("/api/user", customerUser, User.class));
}
List loadedCustomerUsersEmail1 = new ArrayList<>();
@@ -717,16 +647,20 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
.andExpect(status().isOk());
}
-
@Test
public void testDeleteUserWithDeleteRelationsOk() throws Exception {
- UserId userId = createUser().getId();
+ loginSysAdmin();
+ User tenantAdminUser = createTenantAdminUser();
+ UserId userId = doPost("/api/user", tenantAdminUser, User.class).getId();
testEntityDaoWithRelationsOk(tenantId, userId, "/api/user/" + userId);
}
+ @Ignore
@Test
public void testDeleteUserExceptionWithRelationsTransactional() throws Exception {
- UserId userId = createUser().getId();
+ loginSysAdmin();
+ User tenantAdminUser = createTenantAdminUser("Joe", "Downs");
+ UserId userId = doPost("/api/user", tenantAdminUser, User.class).getId();
testEntityDaoWithRelationsTransactionalException(userDao, tenantId, userId, "/api/user/" + userId);
}
@@ -741,15 +675,313 @@ public abstract class BaseUserControllerTest extends AbstractControllerTest {
assertThat(getErrorMessage(result)).containsIgnoringCase("invalid sort property");
}
- private User createUser() throws Exception {
+ @Test
+ public void testSaveUserSettings() throws Exception {
+ loginCustomerUser();
+
+ JsonNode userSettings = mapper.readTree("{\"A\":5, \"B\":10, \"E\":18}");
+ JsonNode savedSettings = doPost("/api/user/settings", userSettings, JsonNode.class);
+ Assert.assertEquals(userSettings, savedSettings);
+
+ JsonNode retrievedSettings = doGet("/api/user/settings", JsonNode.class);
+ Assert.assertEquals(retrievedSettings, userSettings);
+ }
+
+ @Test
+ public void testShouldNotSaveJsonWithRestrictedSymbols() throws Exception {
+ loginCustomerUser();
+
+ JsonNode userSettings = mapper.readTree("{\"A.B\":5, \"E\":18}");
+ doPost("/api/user/settings", userSettings).andExpect(status().isBadRequest());
+
+ userSettings = mapper.readTree("{\"A,B\":5, \"E\":18}");
+ doPost("/api/user/settings", userSettings).andExpect(status().isBadRequest());
+ }
+
+ @Test
+ public void testUpdateUserSettings() throws Exception {
+ loginCustomerUser();
+
+ JsonNode userSettings = mapper.readTree("{\"A\":5, \"B\":{\"C\":true, \"D\":\"stringValue\"}}");
+ JsonNode savedSettings = doPost("/api/user/settings", userSettings, JsonNode.class);
+ Assert.assertEquals(userSettings, savedSettings);
+
+ JsonNode newSettings = mapper.readTree("{\"A\":10}");
+ doPut("/api/user/settings", newSettings);
+ JsonNode updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ JsonNode expectedSettings = mapper.readTree("{\"A\":10, \"B\":{\"C\":true, \"D\":\"stringValue\"}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ JsonNode patchedSettings = mapper.readTree("{\"A\":11, \"B\":{\"C\":false, \"D\":\"stringValue2\"}}");
+ doPut("/api/user/settings", patchedSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":11, \"B\":{\"C\":false, \"D\":\"stringValue2\"}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ patchedSettings = mapper.readTree("{\"B.D\": \"stringValue3\"}");
+ doPut("/api/user/settings", patchedSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":11, \"B\":{\"C\":false, \"D\": \"stringValue3\"}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ patchedSettings = mapper.readTree("{\"B.D\": {\"E\": 76, \"F\": 92}}");
+ doPut("/api/user/settings", patchedSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":11, \"B\":{\"C\":false, \"D\": {\"E\":76, \"F\": 92}}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ patchedSettings = mapper.readTree("{\"B.D.E\": 100}");
+ doPut("/api/user/settings", patchedSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":11, \"B\":{\"C\":false, \"D\": {\"E\":100, \"F\": 92}}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+ }
+
+ @Test
+ public void testShouldCreatePathIfNotExists() throws Exception {
+ loginCustomerUser();
+
+ JsonNode userSettings = mapper.readTree("{\"A\":5}");
+ JsonNode savedSettings = doPost("/api/user/settings", userSettings, JsonNode.class);
+ Assert.assertEquals(userSettings, savedSettings);
+
+ JsonNode newSettings = mapper.readTree("{\"B\":{\"C\": 10}}");
+ doPut("/api/user/settings", newSettings);
+ JsonNode updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ JsonNode expectedSettings = mapper.readTree("{\"A\":5, \"B\":{\"C\": 10}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ newSettings = mapper.readTree("{\"B.K\":true}");
+ doPut("/api/user/settings", newSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":5, \"B\":{\"C\": 10, \"K\": true}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ newSettings = mapper.readTree("{\"B\":{}}");
+ doPut("/api/user/settings", newSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":5, \"B\":{}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ newSettings = mapper.readTree("{\"F.G\":\"string\"}");
+ doPut("/api/user/settings", newSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":5, \"B\":{}, \"F\":{\"G\": \"string\"}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+
+ newSettings = mapper.readTree("{\"F\":{\"G\":\"string2\"}}");
+ doPut("/api/user/settings", newSettings);
+ updatedSettings = doGet("/api/user/settings", JsonNode.class);
+ expectedSettings = mapper.readTree("{\"A\":5, \"B\":{}, \"F\":{\"G\": \"string2\"}}");
+ Assert.assertEquals(expectedSettings, updatedSettings);
+ }
+
+ @Test
+ public void testDeleteUserSettings() throws Exception {
+ loginCustomerUser();
+
+ JsonNode userSettings = mapper.readTree("{\"A\":10, \"B\":10, \"C\":{\"D\": 16}}");
+ JsonNode savedSettings = doPost("/api/user/settings", userSettings, JsonNode.class);
+ Assert.assertEquals(userSettings, savedSettings);
+
+ doDelete("/api/user/settings/C.D,B");
+
+ JsonNode retrievedSettings = doGet("/api/user/settings", JsonNode.class);
+ JsonNode expectedSettings = mapper.readTree("{\"A\":10, \"C\":{}}");
+ Assert.assertEquals(expectedSettings, retrievedSettings);
+ }
+
+ @Test
+ public void checkCustomerUserDoNotSeeTenantUsersOtherTenantUsersOtherCustomerUsers() throws Exception {
+ loginSysAdmin();
+ String searchText = "Joe";
+
+ loginDifferentTenant();
+ CustomerId customerId1 = postCustomer();
+ doPost("/api/user", createCustomerUser(searchText, "Ress", customerId1), User.class);
+
+ loginSysAdmin();
+ User tenantAdmin = createTenantAdminUser(searchText, "Brown");
+ createUserAndLogin(tenantAdmin, "testPassword1");
+
+ CustomerId customerId2 = postCustomer();
+ User user = createCustomerUser(searchText, "Downs", customerId2);
+ doPost("/api/user", user, User.class);
+
+ CustomerId customerId3 = postCustomer();
+ User user2 = createCustomerUser(customerId3);
+ createUserAndLogin(user2, "testPassword2");
+
+ PageLink pageLink = new PageLink(10, 0, searchText);
+ List usersInfo = getUsersInfo(pageLink);
+
+ Assert.assertEquals(usersInfo.size(), 0);
+
+ //clear users
+ loginDifferentTenant();
+ doDelete("/api/customer/" + customerId1.getId().toString())
+ .andExpect(status().isOk());
+ loginUser(tenantAdmin.getEmail(), "testPassword1");
+ doDelete("/api/customer/" + customerId2.getId().toString())
+ .andExpect(status().isOk());
+ doDelete("/api/customer/" + customerId3.getId().toString())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void shouldFindCustomerUsersBySearchText() throws Exception {
+ loginSysAdmin();
+ User tenantAdmin = createTenantAdminUser();
+ createUserAndLogin(tenantAdmin, "testPassword1");
+
+ String searchText = "Philip";
+
+ CustomerId customerId = postCustomer();
+ CustomerId customerId2 = postCustomer();
+
+ List customerUsersContainingWord = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ String suffix = StringUtils.randomAlphabetic((int) (5 + Math.random() * 10));
+
+ customerUsersContainingWord.add(doPost("/api/user", createCustomerUser(searchText + i, "Last" + i, customerId), User.class));
+ customerUsersContainingWord.add(doPost("/api/user", createCustomerUser(null, null, searchText + suffix + "@thingsboard.org", customerId), User.class));
+ doPost("/api/user", createCustomerUser(null, null, customerId), User.class);
+
+ suffix = StringUtils.randomAlphabetic((int) (5 + Math.random() * 10));
+ doPost("/api/user", createCustomerUser(searchText + i, "Last" + i, customerId2), User.class);
+ doPost("/api/user", createCustomerUser(null, null, searchText + suffix + "@thingsboard.org", customerId2), User.class);
+ }
+
+ createUserAndLogin(createCustomerUser(customerId), "testPassword2");
+
+ // find users by search text
+ PageLink pageLink = new PageLink(10, 0, searchText);
+ List usersInfo = getUsersInfo(pageLink);
+
+ List expectedUserInfos = customerUsersContainingWord.stream().map(customerUser -> new UserEmailInfo(customerUser.getId(),
+ customerUser.getEmail(), customerUser.getFirstName() == null ? "" : customerUser.getFirstName(),
+ customerUser.getLastName() == null ? "" : customerUser.getLastName()))
+ .sorted(userDataIdComparator).collect(Collectors.toList());
+ usersInfo.sort(userDataIdComparator);
+
+ Assert.assertEquals(expectedUserInfos, usersInfo);
+
+ // find user by full name
+ pageLink = new PageLink(10, 0, searchText + "5");
+ usersInfo = getUsersInfo(pageLink);
+ Assert.assertEquals(1, usersInfo.size());
+
+ //clear users
+ loginUser(tenantAdmin.getEmail(), "testPassword1");
+ doDelete("/api/customer/" + customerId.getId().toString())
+ .andExpect(status().isOk());
+ doDelete("/api/customer/" + customerId2.getId().toString())
+ .andExpect(status().isOk());
+ }
+
+ @Test
+ public void shouldFindTenantUsersBySearchText() throws Exception {
loginSysAdmin();
- String email = "tenant2@thingsboard.org";
+
+ User tenantAdmin = createTenantAdminUser();
+ createUserAndLogin(tenantAdmin, "testPassword1");
+ CustomerId customerId = postCustomer();
+ CustomerId customerId2 = postCustomer();
+
+ String searchText = "Brown";
+
+ List usersContainingWord = new ArrayList<>();
+ for (int i = 0; i < 10; i++) {
+ String suffix = StringUtils.randomAlphabetic((int) (5 + Math.random() * 10));
+ usersContainingWord.add(doPost("/api/user", createCustomerUser("First" + i, searchText + i, customerId), User.class));
+ usersContainingWord.add(doPost("/api/user", createCustomerUser(null, null, searchText + suffix + "@thingsboard.org", customerId), User.class));
+ doPost("/api/user", createCustomerUser(null, null, customerId), User.class);
+
+ suffix = StringUtils.randomAlphabetic((int) (5 + Math.random() * 10));
+ usersContainingWord.add(doPost("/api/user", createCustomerUser("First" + i, searchText + i, customerId2), User.class));
+ usersContainingWord.add(doPost("/api/user", createCustomerUser(null, null, searchText + suffix + "@thingsboard.org", customerId2), User.class));
+ }
+
+ loginDifferentTenant();
+ CustomerId customerId3 = postCustomer();
+ doPost("/api/user", createCustomerUser("Jane", searchText, customerId3), User.class);
+
+ // find users by search text
+ loginUser(tenantAdmin.getEmail(), "testPassword1");
+ PageLink pageLink = new PageLink(10, 0, searchText);
+ List usersInfo = getUsersInfo(pageLink);
+
+ List expectedUserInfos = usersContainingWord.stream().map(customerUser -> new UserEmailInfo(customerUser.getId(),
+ customerUser.getEmail(), customerUser.getFirstName() == null ? "" : customerUser.getFirstName(),
+ customerUser.getLastName() == null ? "" : customerUser.getLastName()))
+ .sorted(userDataIdComparator).collect(Collectors.toList());
+ usersInfo.sort(userDataIdComparator);
+
+ Assert.assertEquals(expectedUserInfos, usersInfo);
+
+ // find user by full last name
+ pageLink = new PageLink(10, 0, searchText + "3");
+ usersInfo = getUsersInfo(pageLink);
+ Assert.assertEquals(2, usersInfo.size());
+
+ //clear users
+ doDelete("/api/customer/" + customerId.getId().toString())
+ .andExpect(status().isOk());
+ doDelete("/api/customer/" + customerId2.getId().toString())
+ .andExpect(status().isOk());
+ }
+
+ private CustomerId postCustomer() {
+ Customer customer = new Customer();
+ customer.setTitle(StringUtils.randomAlphabetic(9));
+ Customer savedCustomer = doPost("/api/customer", customer, Customer.class);
+ return savedCustomer.getId();
+ }
+
+ private static User createCustomerUser(CustomerId customerId) {
+ return createCustomerUser(null, null, customerId);
+ }
+ private static User createCustomerUser(String firstName, String lastName, CustomerId customerId) {
+ String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+ return createCustomerUser(firstName, lastName, "testMail" + suffix + "@thingsboard.org", customerId);
+ }
+
+ private static User createCustomerUser(String firstName, String lastName, String email, CustomerId customerId) {
User user = new User();
- user.setAuthority(Authority.TENANT_ADMIN);
- user.setTenantId(tenantId);
+ user.setAuthority(Authority.CUSTOMER_USER);
+ user.setFirstName(firstName);
+ user.setLastName(lastName);
+ user.setCustomerId(customerId);
user.setEmail(email);
- user.setFirstName("Joe");
- user.setLastName("Downs");
- return doPost("/api/user", user, User.class);
+ return user;
+ }
+
+ private User createTenantAdminUser() {
+ return createTenantAdminUser(null, null);
}
+ private User createTenantAdminUser(String firstName, String lastName) {
+ String suffix = StringUtils.randomAlphanumeric((int) (5 + Math.random() * 10));
+
+ User tenantAdmin = new User();
+ tenantAdmin.setAuthority(Authority.TENANT_ADMIN);
+ tenantAdmin.setTenantId(tenantId);
+ tenantAdmin.setEmail("testEmail" + suffix + "@thingsbord.org");
+ tenantAdmin.setFirstName(firstName);
+ tenantAdmin.setLastName(lastName);
+ return tenantAdmin;
+ }
+
+ private List getUsersInfo(PageLink pageLink) throws Exception {
+ List loadedCustomerUsers = new ArrayList<>();
+ PageData pageData = null;
+ do {
+ pageData = doGetTypedWithPageLink("/api/users/info?", new TypeReference<>() {}, pageLink);
+ loadedCustomerUsers.addAll(pageData.getData());
+ if (pageData.hasNext()) {
+ pageLink = pageLink.nextPageLink();
+ }
+ } while (pageData.hasNext());
+ return loadedCustomerUsers;
+ }
+
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
index 09fb5c505d..ce354749f3 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseWebsocketApiTest.java
@@ -548,7 +548,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
SingleEntityFilter entityFilter = new SingleEntityFilter();
entityFilter.setSingleEntity(tenantId);
- assertThatNoException().isThrownBy(() -> {
+ assertThatNoException().as("subscribeForAttributes").isThrownBy(() -> {
JsonNode update = getWsClient().subscribeForAttributes(tenantId, TbAttributeSubscriptionScope.SERVER_SCOPE.name(), List.of("attr"));
assertThat(update.get("errorMsg").isNull()).isTrue();
assertThat(update.get("errorCode").asInt()).isEqualTo(SubscriptionErrorCode.NO_ERROR.getCode());
@@ -560,7 +560,7 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
new BaseAttributeKvEntry(System.currentTimeMillis(), new StringDataEntry("attr", expectedAttrValue))
));
JsonNode update = JacksonUtil.toJsonNode(getWsClient().waitForUpdate());
- assertThat(update).isNotNull();
+ assertThat(update).as("waitForUpdate").isNotNull();
assertThat(update.get("data").get("attr").get(0).get(1).asText()).isEqualTo(expectedAttrValue);
}
@@ -569,15 +569,17 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
tsService.saveAndNotify(device.getTenantId(), null, device.getId(), tsData, 0, new FutureCallback() {
@Override
public void onSuccess(@Nullable Void result) {
+ log.debug("sendTelemetry callback onSuccess");
latch.countDown();
}
@Override
public void onFailure(Throwable t) {
+ log.error("Failed to send telemetry", t);
latch.countDown();
}
});
- latch.await(3, TimeUnit.SECONDS);
+ assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendTelemetry callback");
}
private void sendAttributes(Device device, TbAttributeSubscriptionScope scope, List attrData) throws InterruptedException {
@@ -589,14 +591,16 @@ public abstract class BaseWebsocketApiTest extends AbstractControllerTest {
tsService.saveAndNotify(tenantId, entityId, scope.name(), attrData, new FutureCallback() {
@Override
public void onSuccess(@Nullable Void result) {
+ log.debug("sendAttributes callback onSuccess");
latch.countDown();
}
@Override
public void onFailure(Throwable t) {
+ log.error("Failed to sendAttributes", t);
latch.countDown();
}
});
- latch.await(3, TimeUnit.SECONDS);
+ assertThat(latch.await(TIMEOUT, TimeUnit.SECONDS)).as("await sendAttributes callback").isTrue();
}
}
diff --git a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
index 56df8513fe..828d279e93 100644
--- a/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
+++ b/application/src/test/java/org/thingsboard/server/controller/BaseWidgetsBundleControllerTest.java
@@ -192,7 +192,7 @@ public abstract class BaseWidgetsBundleControllerTest extends AbstractController
WidgetsBundle savedWidgetsBundle = doPost("/api/widgetsBundle", widgetsBundle, WidgetsBundle.class);
savedWidgetsBundle.setAlias("new_alias");
- Mockito.reset(tbClusterService);
+ Mockito.clearInvocations(tbClusterService);
doPost("/api/widgetsBundle", savedWidgetsBundle)
.andExpect(status().isBadRequest())
diff --git a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
index db26e9f6df..1f9ac4ea69 100644
--- a/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
+++ b/application/src/test/java/org/thingsboard/server/controller/TbTestWebSocketClient.java
@@ -47,6 +47,7 @@ import java.util.concurrent.TimeUnit;
@Slf4j
public class TbTestWebSocketClient extends WebSocketClient {
+ private static final long TIMEOUT = TimeUnit.SECONDS.toMillis(30);
private volatile String lastMsg;
private volatile CountDownLatch reply;
private volatile CountDownLatch update;
@@ -87,12 +88,14 @@ public class TbTestWebSocketClient extends WebSocketClient {
}
public void registerWaitForUpdate(int count) {
+ log.debug("registerWaitForUpdate [{}]", count);
lastMsg = null;
update = new CountDownLatch(count);
}
@Override
public void send(String text) throws NotYetConnectedException {
+ log.debug("send [{}]", text);
reply = new CountDownLatch(1);
super.send(text);
}
@@ -110,21 +113,31 @@ public class TbTestWebSocketClient extends WebSocketClient {
}
public String waitForUpdate() {
- return waitForUpdate(TimeUnit.SECONDS.toMillis(3));
+ return waitForUpdate(TIMEOUT);
}
public String waitForUpdate(long ms) {
+ log.debug("waitForUpdate [{}]", ms);
try {
- update.await(ms, TimeUnit.MILLISECONDS);
+ if (!update.await(ms, TimeUnit.MILLISECONDS)) {
+ log.warn("Failed to await update (waiting time [{}]ms elapsed)", ms, new RuntimeException("stacktrace"));
+ }
} catch (InterruptedException e) {
- log.warn("Failed to await reply", e);
+ log.warn("Failed to await update", e);
}
return lastMsg;
}
public String waitForReply() {
+ return waitForReply(TIMEOUT);
+ }
+
+ public String waitForReply(long ms) {
+ log.debug("waitForReply [{}]", ms);
try {
- reply.await(3, TimeUnit.SECONDS);
+ if (!reply.await(ms, TimeUnit.MILLISECONDS)) {
+ log.warn("Failed to await reply (waiting time [{}]ms elapsed)", ms, new RuntimeException("stacktrace"));
+ }
} catch (InterruptedException e) {
log.warn("Failed to await reply", e);
}
diff --git a/application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java
index 5014280b07..1cf2cd44bb 100644
--- a/application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java
+++ b/application/src/test/java/org/thingsboard/server/edge/BaseAlarmEdgeTest.java
@@ -76,7 +76,6 @@ abstract public class BaseAlarmEdgeTest extends AbstractEdgeTest {
Device device = findDeviceByName("Edge Device 1");
Alarm alarm = new Alarm();
alarm.setOriginator(device.getId());
- alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
alarm.setType("alarm");
alarm.setSeverity(AlarmSeverity.CRITICAL);
edgeImitator.expectMessageAmount(1);
diff --git a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java
index f9916d27a0..93aeb17de6 100644
--- a/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/entitiy/alarm/DefaultTbAlarmServiceTest.java
@@ -27,9 +27,10 @@ import org.springframework.test.context.junit4.SpringRunner;
import org.thingsboard.server.cluster.TbClusterService;
import org.thingsboard.server.common.data.User;
import org.thingsboard.server.common.data.alarm.Alarm;
-import org.thingsboard.server.common.data.alarm.AlarmStatus;
+import org.thingsboard.server.common.data.alarm.AlarmInfo;
import org.thingsboard.server.common.data.exception.ThingsboardException;
import org.thingsboard.server.common.data.id.UserId;
+import org.thingsboard.server.dao.alarm.AlarmApiCallResult;
import org.thingsboard.server.dao.alarm.AlarmCommentService;
import org.thingsboard.server.dao.alarm.AlarmService;
import org.thingsboard.server.dao.customer.CustomerService;
@@ -81,36 +82,41 @@ public class DefaultTbAlarmServiceTest {
@Test
public void testSave() throws ThingsboardException {
- var alarm = new Alarm();
- when(alarmSubscriptionService.createOrUpdateAlarm(alarm)).thenReturn(alarm);
+ var alarm = new AlarmInfo();
+ when(alarmSubscriptionService.createAlarm(any())).thenReturn(AlarmApiCallResult.builder()
+ .successful(true)
+ .modified(true)
+ .alarm(alarm)
+ .build());
service.save(alarm, new User());
verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any());
- verify(alarmSubscriptionService, times(1)).createOrUpdateAlarm(eq(alarm));
+ verify(alarmSubscriptionService, times(1)).createAlarm(any());
}
@Test
- public void testAck() {
+ public void testAck() throws ThingsboardException {
var alarm = new Alarm();
- alarm.setStatus(AlarmStatus.ACTIVE_UNACK);
- when(alarmSubscriptionService.ackAlarm(any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
+ when(alarmSubscriptionService.acknowledgeAlarm(any(), any(), anyLong()))
+ .thenReturn(AlarmApiCallResult.builder().successful(true).modified(true).build());
service.ack(alarm, new User(new UserId(UUID.randomUUID())));
verify(alarmCommentService, times(1)).createOrUpdateAlarmComment(any(), any());
verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any());
- verify(alarmSubscriptionService, times(1)).ackAlarm(any(), any(), anyLong());
+ verify(alarmSubscriptionService, times(1)).acknowledgeAlarm(any(), any(), anyLong());
}
@Test
- public void testClear() {
+ public void testClear() throws ThingsboardException {
var alarm = new Alarm();
- alarm.setStatus(AlarmStatus.ACTIVE_ACK);
- when(alarmSubscriptionService.clearAlarm(any(), any(), any(), anyLong())).thenReturn(Futures.immediateFuture(true));
+ alarm.setAcknowledged(true);
+ when(alarmSubscriptionService.clearAlarm(any(), any(), anyLong(), any()))
+ .thenReturn(AlarmApiCallResult.builder().successful(true).cleared(true).build());
service.clear(alarm, new User(new UserId(UUID.randomUUID())));
verify(alarmCommentService, times(1)).createOrUpdateAlarmComment(any(), any());
verify(notificationEntityService, times(1)).notifyCreateOrUpdateAlarm(any(), any(), any());
- verify(alarmSubscriptionService, times(1)).clearAlarm(any(), any(), any(), anyLong());
+ verify(alarmSubscriptionService, times(1)).clearAlarm(any(), any(), anyLong(), any());
}
@Test
diff --git a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java b/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
deleted file mode 100644
index 7c4c45c951..0000000000
--- a/application/src/test/java/org/thingsboard/server/service/mail/TestMailService.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Copyright © 2016-2023 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.service.mail;
-
-import org.mockito.Mockito;
-import org.mockito.invocation.InvocationOnMock;
-import org.mockito.stubbing.Answer;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-import org.springframework.context.annotation.Profile;
-import org.thingsboard.rule.engine.api.MailService;
-import org.thingsboard.server.common.data.exception.ThingsboardException;
-
-@Profile("test")
-@Configuration
-public class TestMailService {
-
- public static String currentActivateToken;
- public static String currentResetPasswordToken;
-
- @Bean
- @Primary
- public MailService mailService() throws ThingsboardException {
- MailService mailService = Mockito.mock(MailService.class);
- Mockito.doAnswer(new Answer() {
- public Void answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- String activationLink = (String) args[0];
- currentActivateToken = activationLink.split("=")[1];
- return null;
- }
- }).when(mailService).sendActivationEmail(Mockito.anyString(), Mockito.anyString());
- Mockito.doAnswer(new Answer() {
- public Void answer(InvocationOnMock invocation) {
- Object[] args = invocation.getArguments();
- String passwordResetLink = (String) args[0];
- currentResetPasswordToken = passwordResetLink.split("=")[1];
- return null;
- }
- }).when(mailService).sendResetPasswordEmailAsync(Mockito.anyString(), Mockito.anyString());
- return mailService;
- }
-
-}
diff --git a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java
index 08cd031a52..4373a4c108 100644
--- a/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java
+++ b/application/src/test/java/org/thingsboard/server/service/script/NashornJsInvokeServiceTest.java
@@ -36,6 +36,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
@DaoSqlTest
@TestPropertySource(properties = {
+ "js.evaluator=local",
"js.max_script_body_size=50",
"js.max_total_args_size=50",
"js.max_result_size=50",
diff --git a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
index cf4de21cc2..89a4b707bf 100644
--- a/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/AbstractTransportIntegrationTest.java
@@ -28,7 +28,7 @@ import java.util.concurrent.atomic.AtomicInteger;
@Slf4j
public abstract class AbstractTransportIntegrationTest extends AbstractControllerTest {
- protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 10;
+ protected static final int DEFAULT_WAIT_TIMEOUT_SECONDS = 30;
protected static final String MQTT_URL = "tcp://localhost:1883";
protected static final String COAP_BASE_URL = "coap://localhost:5683/api/v1/";
diff --git a/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java b/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java
index 41cc4c2b76..ab2f6b9bca 100644
--- a/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java
+++ b/application/src/test/java/org/thingsboard/server/transport/TransportNoSqlTestSuite.java
@@ -15,30 +15,14 @@
*/
package org.thingsboard.server.transport;
-import org.cassandraunit.dataset.cql.ClassPathCQLDataSet;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
import org.junit.extensions.cpsuite.ClasspathSuite;
import org.junit.runner.RunWith;
-import org.thingsboard.server.dao.CustomCassandraCQLUnit;
-import org.thingsboard.server.queue.memory.InMemoryStorage;
-
-import java.util.Arrays;
+import org.thingsboard.server.dao.AbstractNoSqlContainer;
@RunWith(ClasspathSuite.class)
@ClasspathSuite.ClassnameFilters({
"org.thingsboard.server.transport.*.telemetry.timeseries.nosql.*Test",
})
-public class TransportNoSqlTestSuite {
-
- @ClassRule
- public static CustomCassandraCQLUnit cassandraUnit =
- new CustomCassandraCQLUnit(
- Arrays.asList(
- new ClassPathCQLDataSet("cassandra/schema-keyspace.cql", false, false),
- new ClassPathCQLDataSet("cassandra/schema-ts.cql", false, false),
- new ClassPathCQLDataSet("cassandra/schema-ts-latest.cql", false, false)
- ),
- "cassandra-test.yaml", 30000l);
+public class TransportNoSqlTestSuite extends AbstractNoSqlContainer {
}
diff --git a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
index d90af7b6ba..1266ef2c29 100644
--- a/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/coap/AbstractCoapIntegrationTest.java
@@ -45,6 +45,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@TestPropertySource(properties = {
+ "coap.enabled=true",
+ "service.integrations.supported=ALL",
"transport.coap.enabled=true",
})
@Slf4j
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
index 0f1815b46b..6cb6e49e79 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/AbstractMqttIntegrationTest.java
@@ -46,8 +46,8 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@TestPropertySource(properties = {
+ "service.integrations.supported=ALL",
"transport.mqtt.enabled=true",
- "js.evaluator=mock",
})
@Slf4j
public abstract class AbstractMqttIntegrationTest extends AbstractTransportIntegrationTest {
diff --git a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java
index 2c0278f689..7feafead95 100644
--- a/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java
+++ b/application/src/test/java/org/thingsboard/server/transport/mqtt/mqttv3/attributes/AbstractMqttAttributesIntegrationTest.java
@@ -15,6 +15,7 @@
*/
package org.thingsboard.server.transport.mqtt.mqttv3.attributes;
+import com.fasterxml.jackson.core.type.TypeReference;
import com.github.os72.protobuf.dynamic.DynamicSchema;
import com.google.protobuf.Descriptors;
import com.google.protobuf.DynamicMessage;
@@ -22,6 +23,7 @@ import com.google.protobuf.InvalidProtocolBufferException;
import com.squareup.wire.schema.internal.parser.ProtoFileElement;
import io.netty.handler.codec.mqtt.MqttQoS;
import lombok.extern.slf4j.Slf4j;
+import org.awaitility.Awaitility;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DynamicProtoUtils;
@@ -45,6 +47,7 @@ import org.thingsboard.server.transport.mqtt.mqttv3.MqttTestClient;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@@ -125,14 +128,16 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
- onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onUpdateCallback").isTrue();
validateUpdateAttributesJsonResponse(onUpdateCallback, SHARED_ATTRIBUTES_PAYLOAD);
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
- onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onDeleteCallback").isTrue();
validateUpdateAttributesJsonResponse(onDeleteCallback, SHARED_ATTRIBUTES_DELETED_RESPONSE);
client.disconnect();
}
@@ -145,13 +150,15 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
client.subscribeAndWait(attrSubTopic, MqttQoS.AT_MOST_ONCE);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
- onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onUpdateCallback").isTrue();
validateUpdateAttributesProtoResponse(onUpdateCallback);
MqttTestCallback onDeleteCallback = new MqttTestCallback();
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
- onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onDeleteCallback").isTrue();
validateDeleteAttributesProtoResponse(onDeleteCallback);
client.disconnect();
}
@@ -162,7 +169,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
}
protected void validateUpdateAttributesProtoResponse(MqttTestCallback callback) throws InvalidProtocolBufferException {
- assertNotNull(callback.getPayloadBytes());
+ assertThat(callback.getPayloadBytes()).as("callback payload non-null").isNotNull();
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List tsKvProtoList = getTsKvProtoList("shared");
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList);
@@ -178,7 +185,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
}
protected void validateDeleteAttributesProtoResponse(MqttTestCallback callback) throws InvalidProtocolBufferException {
- assertNotNull(callback.getPayloadBytes());
+ assertThat(callback.getPayloadBytes()).as("callback payload non-null").isNotNull();
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
@@ -209,7 +216,8 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
client.subscribeAndWait(GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE);
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", SHARED_ATTRIBUTES_PAYLOAD, String.class, status().isOk());
- onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onUpdateCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onUpdateCallback").isTrue();
validateJsonGatewayUpdateAttributesResponse(onUpdateCallback, deviceName, SHARED_ATTRIBUTES_PAYLOAD);
@@ -217,7 +225,8 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
client.setCallback(onDeleteCallback);
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=sharedJson", String.class);
- onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
+ assertThat(onDeleteCallback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await onDeleteCallback").isTrue();
validateJsonGatewayUpdateAttributesResponse(onDeleteCallback, deviceName, SHARED_ATTRIBUTES_DELETED_RESPONSE);
client.disconnect();
@@ -246,7 +255,7 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
}
protected void validateJsonGatewayUpdateAttributesResponse(MqttTestCallback callback, String deviceName, String expectResultData) {
- assertNotNull(callback.getPayloadBytes());
+ assertThat(callback.getPayloadBytes()).as("callback payload non-null").isNotNull();
assertEquals(JacksonUtil.toJsonNode(getGatewayAttributesResponseJson(deviceName, expectResultData)), JacksonUtil.fromBytes(callback.getPayloadBytes()));
}
@@ -260,8 +269,9 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
}
protected void validateProtoGatewayUpdateAttributesResponse(MqttTestCallback callback, String deviceName) throws InvalidProtocolBufferException, InterruptedException {
- callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- assertNotNull(callback.getPayloadBytes());
+ assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await callback").isTrue();
+ assertThat(callback.getPayloadBytes()).as("callback payload non-null").isNotNull();
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
List tsKvProtoList = getTsKvProtoList("shared");
@@ -285,8 +295,9 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
}
protected void validateProtoGatewayDeleteAttributesResponse(MqttTestCallback callback, String deviceName) throws InvalidProtocolBufferException, InterruptedException {
- callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- assertNotNull(callback.getPayloadBytes());
+ assertThat(callback.getSubscribeLatch().await(DEFAULT_WAIT_TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ .as("await callback").isTrue();
+ assertThat(callback.getPayloadBytes()).as("callback payload non-null").isNotNull();
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder();
attributeUpdateNotificationMsgBuilder.addSharedDeleted("sharedJson");
TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build();
@@ -391,9 +402,19 @@ public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqtt
100);
assertNotNull(device);
+ String clientKeysStr = "clientStr,clientBool,clientDbl,clientLong,clientJson";
+
+ String attributeValuesUrl = "/api/plugins/telemetry/DEVICE/" + device.getId() + "/values/attributes/CLIENT_SCOPE?keys=" + clientKeysStr;
+
+ Awaitility.await()
+ .atMost(10, TimeUnit.SECONDS)
+ .until(() -> {
+ List