Browse Source

Tests for new ThingsboardClient

pull/15264/head
Viacheslav Klimov 3 months ago
parent
commit
63f9c05f04
Failed to extract signature
  1. 5
      application/pom.xml
  2. 168
      application/src/test/java/org/thingsboard/server/client/AIModelJavaClientTest.java
  3. 127
      application/src/test/java/org/thingsboard/server/client/AbstractJavaClientTest.java
  4. 100
      application/src/test/java/org/thingsboard/server/client/AdminJavaClientTest.java
  5. 106
      application/src/test/java/org/thingsboard/server/client/AlarmCommentJavaClientTest.java
  6. 163
      application/src/test/java/org/thingsboard/server/client/AlarmJavaClientTest.java
  7. 84
      application/src/test/java/org/thingsboard/server/client/AssetJavaClientTest.java
  8. 135
      application/src/test/java/org/thingsboard/server/client/AssetProfileJavaClientTest.java
  9. 286
      application/src/test/java/org/thingsboard/server/client/CalculatedFieldJavaClientTest.java
  10. 113
      application/src/test/java/org/thingsboard/server/client/CustomerJavaClientTest.java
  11. 123
      application/src/test/java/org/thingsboard/server/client/DashboardJavaClientTest.java
  12. 53
      application/src/test/java/org/thingsboard/server/client/DeviceConnectivityJavaClientTest.java
  13. 114
      application/src/test/java/org/thingsboard/server/client/DeviceJavaClientTest.java
  14. 157
      application/src/test/java/org/thingsboard/server/client/DeviceProfileJavaClientTest.java
  15. 105
      application/src/test/java/org/thingsboard/server/client/DomainJavaClientTest.java
  16. 141
      application/src/test/java/org/thingsboard/server/client/EdgeJavaClientTest.java
  17. 289
      application/src/test/java/org/thingsboard/server/client/EntityQueryJavaClientTest.java
  18. 182
      application/src/test/java/org/thingsboard/server/client/EntityRelationJavaClientTest.java
  19. 267
      application/src/test/java/org/thingsboard/server/client/EntityViewJavaClientTest.java
  20. 156
      application/src/test/java/org/thingsboard/server/client/MobileAppJavaClientTest.java
  21. 278
      application/src/test/java/org/thingsboard/server/client/NotificationJavaClientTest.java
  22. 138
      application/src/test/java/org/thingsboard/server/client/Oauth2JavaClientTest.java
  23. 261
      application/src/test/java/org/thingsboard/server/client/OtaPackageJavaClientTest.java
  24. 72
      application/src/test/java/org/thingsboard/server/client/RpcV1JavaClientTest.java
  25. 133
      application/src/test/java/org/thingsboard/server/client/RpcV2JavaClientTest.java
  26. 163
      application/src/test/java/org/thingsboard/server/client/RuleChainJavaClientTest.java
  27. 151
      application/src/test/java/org/thingsboard/server/client/TbImageJavaClientTest.java
  28. 128
      application/src/test/java/org/thingsboard/server/client/TbResourceJavaClientTest.java
  29. 150
      application/src/test/java/org/thingsboard/server/client/TelemetryJavaClientTest.java
  30. 119
      application/src/test/java/org/thingsboard/server/client/TenantJavaClientTest.java
  31. 179
      application/src/test/java/org/thingsboard/server/client/TenantProfileJavaClientTest.java
  32. 78
      application/src/test/java/org/thingsboard/server/client/TwoFactorAuthJavaClientTest.java
  33. 135
      application/src/test/java/org/thingsboard/server/client/UserJavaClientTest.java
  34. 155
      application/src/test/java/org/thingsboard/server/client/WidgetTypeJavaClientTest.java
  35. 7
      pom.xml

5
application/pom.xml

@ -290,6 +290,11 @@
<artifactId>rest-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thingsboard.client</groupId>
<artifactId>thingsboard-ce-client</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>

168
application/src/test/java/org/thingsboard/server/client/AIModelJavaClientTest.java

@ -0,0 +1,168 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AiModel;
import org.thingsboard.client.model.OpenAiChatModelConfig;
import org.thingsboard.client.model.OpenAiProviderConfig;
import org.thingsboard.client.model.PageDataAiModel;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class AIModelJavaClientTest extends AbstractJavaClientTest {
private static final String AI_PREFIX = "AiTest_";
@Test
public void testSaveAndGetAiModel() throws Exception {
long ts = System.currentTimeMillis();
String name = AI_PREFIX + "save_" + ts;
AiModel model = buildAiModel(name, "gpt-4o", 0.7);
AiModel saved = client.saveAiModel(model);
assertNotNull(saved);
assertNotNull(saved.getId());
assertEquals(name, saved.getName());
assertNotNull(saved.getConfiguration());
// get by id
AiModel fetched = client.getAiModelById(saved.getId().getId());
assertNotNull(fetched);
assertEquals(name, fetched.getName());
assertEquals(saved.getId().getId(), fetched.getId().getId());
}
@Test
public void testGetAiModelById() throws Exception {
long ts = System.currentTimeMillis();
AiModel saved = createAiModel("getbyid_" + ts);
AiModel fetched = client.getAiModelById(saved.getId().getId());
assertNotNull(fetched);
assertEquals(saved.getName(), fetched.getName());
assertEquals(saved.getId().getId(), fetched.getId().getId());
}
@Test
public void testUpdateAiModel() throws Exception {
long ts = System.currentTimeMillis();
AiModel saved = createAiModel("update_" + ts);
saved.setName(AI_PREFIX + "updated_" + ts);
OpenAiChatModelConfig updatedConfig = new OpenAiChatModelConfig();
updatedConfig.setModelId("gpt-4o-mini");
updatedConfig.setTemperature(0.3);
updatedConfig.setMaxOutputTokens(2048);
updatedConfig.setMaxRetries(50);
OpenAiProviderConfig providerConfig = new OpenAiProviderConfig();
providerConfig.setApiKey("test-api-key");
providerConfig.setBaseUrl("https://api.openai.com/v1");
updatedConfig.setProviderConfig(providerConfig);
updatedConfig.setProvider("OPENAI");
saved.setConfiguration(updatedConfig);
AiModel updated = client.saveAiModel(saved);
assertNotNull(updated);
assertEquals(saved.getId().getId(), updated.getId().getId());
assertEquals(AI_PREFIX + "updated_" + ts, updated.getName());
}
@Test
public void testDeleteAiModel() throws Exception {
long ts = System.currentTimeMillis();
AiModel saved = createAiModel("delete_" + ts);
UUID modelId = saved.getId().getId();
client.getAiModelById(modelId);
Boolean deleted = client.deleteAiModelById(modelId);
assertTrue(deleted);
assertReturns404(() -> client.getAiModelById(modelId));
}
@Test
public void testGetAiModels() throws Exception {
long ts = System.currentTimeMillis();
for (int i = 0; i < 3; i++) {
createAiModel("list_" + ts + "_" + i);
}
PageDataAiModel page = client.getAiModels(100, 0, AI_PREFIX + "list_" + ts, null, null);
assertNotNull(page);
assertEquals(3, page.getTotalElements().intValue());
for (AiModel m : page.getData()) {
assertTrue(m.getName().startsWith(AI_PREFIX + "list_" + ts));
}
}
@Test
public void testGetAiModelById_notFound() {
UUID nonExistentId = UUID.randomUUID();
assertReturns404(() -> client.getAiModelById(nonExistentId));
}
@Test
public void testGetAiModelsPagination() throws Exception {
long ts = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
createAiModel("paged_" + ts + "_" + i);
}
PageDataAiModel page1 = client.getAiModels(2, 0, AI_PREFIX + "paged_" + ts, null, null);
assertNotNull(page1);
assertEquals(5, page1.getTotalElements().intValue());
assertEquals(3, page1.getTotalPages().intValue());
assertEquals(2, page1.getData().size());
assertTrue(page1.getHasNext());
PageDataAiModel lastPage = client.getAiModels(2, 2, AI_PREFIX + "paged_" + ts, null, null);
assertEquals(1, lastPage.getData().size());
assertFalse(lastPage.getHasNext());
}
private AiModel buildAiModel(String name, String modelId, double temperature) {
OpenAiChatModelConfig config = new OpenAiChatModelConfig();
config.setModelId(modelId);
config.setTemperature(temperature);
config.setMaxRetries(50);
OpenAiProviderConfig openAiProviderConfig = new OpenAiProviderConfig();
openAiProviderConfig.setApiKey("test-api-key");
openAiProviderConfig.setBaseUrl("https://api.openai.com/v1");
config.setProviderConfig(openAiProviderConfig);
config.setProvider("OPENAI");
AiModel model = new AiModel();
model.setName(name);
model.setConfiguration(config);
return model;
}
private AiModel createAiModel(String suffix) throws Exception {
return client.saveAiModel(buildAiModel(AI_PREFIX + suffix, "gpt-4o", 0.7));
}
}

127
application/src/test/java/org/thingsboard/server/client/AbstractJavaClientTest.java

@ -0,0 +1,127 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.junit.After;
import org.junit.Before;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.ThingsboardClient;
import org.thingsboard.client.model.ActivateUserRequest;
import org.thingsboard.client.model.Authority;
import org.thingsboard.client.model.JwtPair;
import org.thingsboard.client.model.User;
import org.thingsboard.client.model.UserId;
import org.thingsboard.server.common.data.util.ThrowingRunnable;
import org.thingsboard.server.controller.AbstractControllerTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@Slf4j
public abstract class AbstractJavaClientTest extends AbstractControllerTest {
protected static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
protected static final ObjectMapper MAPPER = new ObjectMapper();
protected static final String TEST_PREFIX = "JavaClientTestDevice_";
protected static final String TEST_PREFIX_2 = "JavaClientTestDevice2_";
protected static final String CUSTOMER_USERNAME = "javaClientCustomer@thingsboard.org";
protected static final String TENANT_ADMIN_USERNAME = "javaClientTenant@thingsboard.org";
protected static final String TEST_PASSWORD = "password123";
protected ThingsboardClient client;
// FQN for Tenant/Customer to avoid collision with AbstractWebTest fields
protected org.thingsboard.client.model.Tenant savedClientTenant;
protected User clientTenantAdmin;
protected org.thingsboard.client.model.Customer savedClientCustomer;
protected User savedClientCustomerUser;
@Before
public void setUpJavaClient() throws Exception {
client = ThingsboardClient.builder()
.url("http://localhost:" + wsPort)
.build();
client.login("sysadmin@thingsboard.org", "sysadmin");
org.thingsboard.client.model.Tenant tenant = new org.thingsboard.client.model.Tenant();
tenant.setTitle("Java client test tenant");
savedClientTenant = client.saveTenant(tenant);
clientTenantAdmin = new User();
clientTenantAdmin.setAuthority(Authority.TENANT_ADMIN);
clientTenantAdmin.setTenantId(savedClientTenant.getId());
clientTenantAdmin.setEmail(TENANT_ADMIN_USERNAME);
clientTenantAdmin = client.saveUser(clientTenantAdmin, "false");
activateUserAndAuthorize(clientTenantAdmin);
org.thingsboard.client.model.Customer customer = new org.thingsboard.client.model.Customer();
customer.setTitle("Java client test customer");
customer.setTenantId(savedClientTenant.getId());
savedClientCustomer = client.saveCustomer(customer, null, null, null);
User customerUser = new User();
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(savedClientTenant.getId());
customerUser.setCustomerId(savedClientCustomer.getId());
customerUser.setEmail(CUSTOMER_USERNAME);
savedClientCustomerUser = client.saveUser(customerUser, "false");
activateUser(savedClientCustomerUser.getId(), "password123", false);
}
@After
public void tearDownJavaClient() {
client.login("sysadmin@thingsboard.org", "sysadmin");
client.deleteTenant(savedClientTenant.getId().getId().toString());
}
protected String getBaseUrl() {
return "http://localhost:" + wsPort;
}
protected void activateUserAndAuthorize(User user) throws ApiException {
JwtPair jwtPair = activateUser(user.getId(), TEST_PASSWORD, false);
client.setToken(jwtPair.getToken());
}
protected JwtPair activateUser(UserId userId, String password, boolean sendActivationMail) throws ApiException {
ActivateUserRequest activateRequest = new ActivateUserRequest();
activateRequest.setActivateToken(getActivateToken(userId));
activateRequest.setPassword(password);
return client.activateUser(activateRequest, sendActivationMail);
}
protected String getActivateToken(UserId userId) throws ApiException {
String activateTokenRegex = "/api/noauth/activate?activateToken=";
String activationLink = client.getActivationLink(userId.getId().toString());
return activationLink.substring(activationLink.lastIndexOf(activateTokenRegex) + activateTokenRegex.length());
}
protected void assertReturns404(ThrowingRunnable operation) {
try {
operation.run();
fail("Expected ApiException with 404 status code");
} catch (ApiException exception) {
assertEquals("Expected 404 status code but got " + exception.getCode(),
404, exception.getCode());
} catch (Exception e) {
fail("Expected ApiException but got " + e.getClass().getName() + ": " + e.getMessage());
}
}
}

100
application/src/test/java/org/thingsboard/server/client/AdminJavaClientTest.java

@ -0,0 +1,100 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Test;
import org.thingsboard.client.model.AdminSettings;
import org.thingsboard.client.model.FeaturesInfo;
import org.thingsboard.client.model.JwtSettings;
import org.thingsboard.client.model.SecuritySettings;
import org.thingsboard.client.model.SystemInfo;
import org.thingsboard.client.model.UpdateMessage;
import org.thingsboard.server.dao.service.DaoSqlTest;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class AdminJavaClientTest extends AbstractJavaClientTest {
@Test
public void testAdminSettingsLifecycle() throws Exception {
// authenticate as sysadmin for admin settings management
client.login("sysadmin@thingsboard.org", "sysadmin");
// get mail settings
AdminSettings mailSettings = client.getAdminSettings("mail");
assertNotNull(mailSettings);
assertNotNull(mailSettings.getKey());
assertEquals("mail", mailSettings.getKey());
assertNotNull(mailSettings.getJsonValue());
// get general settings
AdminSettings generalSettings = client.getAdminSettings("general");
assertNotNull(generalSettings);
assertEquals("general", generalSettings.getKey());
assertNotNull(generalSettings.getJsonValue());
assertNotNull(generalSettings.getJsonValue().get("baseUrl").asText());
// update general settings and restore
((ObjectNode) generalSettings.getJsonValue()).put("prohibitDifferentUrl", true);
AdminSettings updatedGeneralSettings = client.saveAdminSettings(generalSettings);
assertTrue(updatedGeneralSettings.getJsonValue().get("prohibitDifferentUrl").asBoolean());
// get security settings
SecuritySettings securitySettings = client.getSecuritySettings();
assertNotNull(securitySettings);
assertNotNull(securitySettings.getPasswordPolicy());
Integer originalMaxAttempts = securitySettings.getMaxFailedLoginAttempts();
// update security settings
securitySettings.setMaxFailedLoginAttempts(10);
SecuritySettings updatedSecurity = client.saveSecuritySettings(securitySettings);
assertNotNull(updatedSecurity);
assertEquals(10, updatedSecurity.getMaxFailedLoginAttempts().intValue());
// restore original security settings
updatedSecurity.setMaxFailedLoginAttempts(originalMaxAttempts);
client.saveSecuritySettings(updatedSecurity);
// get JWT settings
JwtSettings jwtSettings = client.getJwtSettings();
assertNotNull(jwtSettings);
assertNotNull(jwtSettings.getTokenExpirationTime());
assertNotNull(jwtSettings.getRefreshTokenExpTime());
assertEquals("thingsboard.io", jwtSettings.getTokenIssuer());
assertNotNull(jwtSettings.getTokenSigningKey());
// get system info
SystemInfo systemInfo = client.getSystemInfo();
assertNotNull(systemInfo);
// get features info
FeaturesInfo featuresInfo = client.getFeaturesInfo();
assertNotNull(featuresInfo);
assertFalse(featuresInfo.getSmsEnabled());
assertTrue(featuresInfo.getOauthEnabled());
// check updates
UpdateMessage updateMessage = client.checkUpdates();
assertNotNull(updateMessage);
assertNotNull(updateMessage.getCurrentVersion());
}
}

106
application/src/test/java/org/thingsboard/server/client/AlarmCommentJavaClientTest.java

@ -0,0 +1,106 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.junit.Test;
import org.thingsboard.client.model.Alarm;
import org.thingsboard.client.model.AlarmComment;
import org.thingsboard.client.model.AlarmCommentInfo;
import org.thingsboard.client.model.AlarmSeverity;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.PageDataAlarmCommentInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class AlarmCommentJavaClientTest extends AbstractJavaClientTest {
@Test
public void testAlarmComments() throws Exception {
long timestamp = System.currentTimeMillis();
// Create device for alarm
Device device = new Device();
device.setName("Device_For_Comments_" + timestamp);
device.setType("default");
Device createdDevice = client.saveDevice(device, null, null, null, null);
// Create alarm
Alarm alarm = new Alarm();
alarm.setType("Temperature Alarm");
alarm.setSeverity(AlarmSeverity.CRITICAL);
alarm.setOriginator(createdDevice.getId());
Alarm createdAlarm = client.saveAlarm(alarm);
String alarmId = createdAlarm.getId().getId().toString();
List<AlarmComment> createdComments = new ArrayList<>();
// Create multiple comments
for (int i = 0; i < 5; i++) {
AlarmComment alarmComment = new AlarmComment();
String message = "Test comment #" + i + " at " + timestamp;
ObjectNode comment = OBJECT_MAPPER.createObjectNode().put("message", message);
alarmComment.setComment(comment);
AlarmComment commentInfo = client.saveAlarmComment(alarmId, alarmComment);
assertNotNull(commentInfo);
assertNotNull(commentInfo.getId());
JsonNode commentValue = commentInfo.getComment();
assertEquals(message, commentValue.get("message").asText());
assertNotNull(commentInfo.getCreatedTime());
createdComments.add(commentInfo);
}
// Get all comments for the alarm
PageDataAlarmCommentInfo allComments = client.getAlarmComments(alarmId, 100, 0, null, null);
assertEquals("Expected 5 comments", 5, allComments.getData().size());
// Update a comment
AlarmComment commentToUpdate = createdComments.get(2);
JsonNode comment = commentToUpdate.getComment();
((ObjectNode) comment).put("message", "New comment");
commentToUpdate.setComment(comment);
AlarmComment updatedComment = client.saveAlarmComment(alarmId, commentToUpdate);
assertEquals("New comment", updatedComment.getComment().get("message").asText());
// Delete a comment
UUID commentToDeleteId = createdComments.get(0).getId().getId();
client.deleteAlarmComment(alarmId, commentToDeleteId.toString());
// Verify comment was updated to "deleted"
PageDataAlarmCommentInfo commentsAfterDelete = client.getAlarmComments(alarmId, 100, 0, null, null);
List<AlarmCommentInfo> data = commentsAfterDelete.getData();
AlarmCommentInfo deletedComment = data.stream()
.filter(alarmCommentInfo -> alarmCommentInfo.getId().getId().equals(commentToDeleteId))
.findFirst()
.get();
assertEquals("User " + clientTenantAdmin.getEmail() + " deleted his comment", deletedComment.getComment().get("text").asText());
}
}

163
application/src/test/java/org/thingsboard/server/client/AlarmJavaClientTest.java

@ -0,0 +1,163 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Alarm;
import org.thingsboard.client.model.AlarmInfo;
import org.thingsboard.client.model.AlarmSeverity;
import org.thingsboard.client.model.AlarmStatus;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.EntitySubtype;
import org.thingsboard.client.model.EntityType;
import org.thingsboard.client.model.PageDataAlarmInfo;
import org.thingsboard.client.model.PageDataEntitySubtype;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class AlarmJavaClientTest extends AbstractJavaClientTest {
@Test
public void testAlarmLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Alarm> createdAlarms = new ArrayList<>();
// First, create devices to attach alarms to
Device device1 = new Device();
device1.setName("Device_For_Alarm_" + timestamp + "_1");
device1.setType("default");
Device createdDevice1 = client.saveDevice(device1, null, null, null, null);
Device device2 = new Device();
device2.setName("Device_For_Alarm_" + timestamp + "_2");
device2.setType("thermostat");
Device createdDevice2 = client.saveDevice(device2, null, null, null, null);
// Create 2 alarms (1 for each device)
for (int i = 0; i < 2; i++) {
Alarm alarm = new Alarm();
alarm.setType(((i % 2 == 0) ? "Temperature Alarm" : "Connection Alarm"));
alarm.setSeverity(((i % 2 == 0) ? AlarmSeverity.CRITICAL : AlarmSeverity.WARNING));
alarm.setOriginator((i % 2 == 0) ? createdDevice1.getId() : createdDevice2.getId());
Alarm createdAlarm = client.saveAlarm(alarm);
assertNotNull(createdAlarm);
assertNotNull(createdAlarm.getId());
assertEquals(alarm.getType(), createdAlarm.getType());
assertEquals(alarm.getSeverity(), createdAlarm.getSeverity());
createdAlarms.add(createdAlarm);
}
// Get all alarms
PageDataAlarmInfo allAlarms = client.getAllAlarms(100, 0, null, null, null, null, null, null, null, null, null);
assertNotNull(allAlarms);
assertNotNull(allAlarms.getData());
int initialSize = allAlarms.getData().size();
assertEquals("Expected at least 2 alarms, but got " + initialSize, 2, initialSize);
// Get alarms by entity (device1)
PageDataAlarmInfo device1Alarms = client.getAlarmsV2(EntityType.DEVICE.toString(), createdDevice1.getId().getId().toString(), 100, 0, null, null, null, null, null, null, null, null, null);
assertNotNull(device1Alarms);
assertEquals("Expected 1 alarms for device1", 1, device1Alarms.getData().size());
// Get alarm by id
Alarm searchAlarm = createdAlarms.get(0);
Alarm fetchedAlarm = client.getAlarmById(searchAlarm.getId().getId().toString());
assertEquals(searchAlarm.getType(), fetchedAlarm.getType());
assertEquals(searchAlarm.getSeverity(), fetchedAlarm.getSeverity());
// Get alarm info
AlarmInfo alarmInfo = client.getAlarmInfoById(searchAlarm.getId().getId().toString());
assertNotNull(alarmInfo);
assertEquals(searchAlarm.getId().getId(), alarmInfo.getId().getId());
// Acknowledge alarm
client.ackAlarm(searchAlarm.getId().getId().toString());
// Verify alarm is acknowledged
Alarm ackedAlarm = client.getAlarmById(searchAlarm.getId().getId().toString());
assertEquals(AlarmStatus.ACTIVE_ACK, ackedAlarm.getStatus());
// Clear alarm
client.clearAlarm(searchAlarm.getId().getId().toString());
// Verify alarm is cleared
Alarm clearedAlarm = client.getAlarmById(searchAlarm.getId().getId().toString());
assertEquals(AlarmStatus.CLEARED_ACK, clearedAlarm.getStatus());
// Get highest severity alarm for device
AlarmSeverity highestSeverity = client.getHighestAlarmSeverity(EntityType.DEVICE.toString(), createdDevice1.getId().getId().toString(), null, null, null);
assertNotNull(highestSeverity);
assertEquals(AlarmSeverity.CRITICAL, highestSeverity);
// Assign alarm to customer
client.assignAlarm(createdAlarms.get(0).getId().getId().toString(), clientTenantAdmin.getId().getId().toString());
// Verify assignment
Alarm assignedAlarm = client.getAlarmById(createdAlarms.get(0).getId().getId().toString());
assertEquals(clientTenantAdmin.getId().getId(), assignedAlarm.getAssigneeId().getId());
// Unassign alarm
client.unassignAlarm(createdAlarms.get(0).getId().getId().toString());
// Verify unassignment
Alarm unassignedAlarm = client.getAlarmById(createdAlarms.get(0).getId().getId().toString());
assertNull(unassignedAlarm.getAssigneeId());
// Get alarm types
PageDataEntitySubtype pageDataEntitySubtype = client.getAlarmTypes(100, 0, null, null);
assertEquals(2, pageDataEntitySubtype.getData().size());
List<String> alarmTypes = pageDataEntitySubtype.getData().stream()
.map(EntitySubtype::getType)
.collect(Collectors.toList());
assertTrue(alarmTypes.containsAll(List.of("Temperature Alarm", "Connection Alarm")));
// Get alarms V2 (alternative endpoint)
PageDataAlarmInfo alarmsV2 = client.getAlarmsV2(EntityType.DEVICE.toString(), createdDevice1.getId().getId().toString(), 100, 0, null, null, null, null, null, null, null, null, null);
assertNotNull(alarmsV2);
assertEquals(1, alarmsV2.getData().size());
// Get all alarms V2
PageDataAlarmInfo allAlarmsV2 = client.getAllAlarmsV2(100, 0, null, null, null, null, null, null, null, null, null);
assertEquals(2, allAlarmsV2.getData().size());
// Delete alarm
UUID alarmToDeleteId = createdAlarms.get(0).getId().getId();
client.deleteAlarm(alarmToDeleteId.toString());
// Verify the alarm is deleted (should return 404)
assertReturns404(() ->
client.getAlarmById(alarmToDeleteId.toString())
);
// Verify count after deletion
PageDataAlarmInfo alarmsAfterDelete = client.getAllAlarms(100, 0, null, null, null, null, null, null, null, null, null);
assertEquals(initialSize - 1, alarmsAfterDelete.getData().size());
}
}

84
application/src/test/java/org/thingsboard/server/client/AssetJavaClientTest.java

@ -0,0 +1,84 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Asset;
import org.thingsboard.client.model.PageDataAsset;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class AssetJavaClientTest extends AbstractJavaClientTest {
@Test
public void testAssetLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Asset> createdAssets = new ArrayList<>();
// create 20 assets
for (int i = 0; i < 20; i++) {
Asset asset = new Asset();
String assetName = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i;
asset.setName(assetName);
asset.setLabel("Test Asset " + i);
asset.setType(((i % 2 == 0) ? "default" : "building"));
Asset createdAsset = client.saveAsset(asset, null, null, null);
assertNotNull(createdAsset);
assertNotNull(createdAsset.getId());
assertEquals(assetName, createdAsset.getName());
createdAssets.add(createdAsset);
}
// find all, check count
PageDataAsset allAssets = client.getTenantAssets(100, 0, null, null, null, null);
assertNotNull(allAssets);
assertNotNull(allAssets.getData());
int initialSize = allAssets.getData().size();
assertEquals("Expected at least 20 assets, but got " + allAssets.getData().size(), 20, initialSize);
//find all with search text, check count
PageDataAsset allAssetsBySearchText = client.getTenantAssets(100, 0, null, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 test assets", 10, allAssetsBySearchText.getData().size());
// find by id
Asset searchAsset = createdAssets.get(10);
Asset asset = client.getAssetById(searchAsset.getId().getId().toString());
assertEquals(searchAsset.getName(), asset.getName());
// delete asset
UUID assetToDeleteId = createdAssets.get(0).getId().getId();
client.deleteAsset(assetToDeleteId.toString());
// Verify the asset is deleted
PageDataAsset assetsAfterDelete = client.getTenantAssets(100, 0, null, null, null, null);
assertEquals(initialSize - 1, assetsAfterDelete.getData().size());
assertReturns404(() ->
client.getAssetById(assetToDeleteId.toString())
);
}
}

135
application/src/test/java/org/thingsboard/server/client/AssetProfileJavaClientTest.java

@ -0,0 +1,135 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AssetProfile;
import org.thingsboard.client.model.AssetProfileInfo;
import org.thingsboard.client.model.EntityInfo;
import org.thingsboard.client.model.PageDataAssetProfile;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class AssetProfileJavaClientTest extends AbstractJavaClientTest {
@Test
public void testAssetProfileLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<AssetProfile> createdProfiles = new ArrayList<>();
// Get initial count (there should be a default profile)
PageDataAssetProfile initialProfiles = client.getAssetProfiles(100, 0, null, null, null);
assertNotNull(initialProfiles);
int initialSize = initialProfiles.getData().size();
assertTrue("Expected at least 1 default asset profile", initialSize == 1);
// Get default asset profile info
AssetProfileInfo defaultProfileInfo = client.getDefaultAssetProfileInfo();
assertNotNull(defaultProfileInfo);
assertEquals(defaultProfileInfo.getName(), "default");
// Create multiple asset profiles
for (int i = 0; i < 5; i++) {
AssetProfile profile = new AssetProfile();
profile.setName("Test Asset Profile " + timestamp + "_" + i);
profile.setDescription("Test description " + i);
AssetProfile created = client.saveAssetProfile(profile);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(profile.getName(), created.getName());
assertEquals(profile.getDescription(), created.getDescription());
assertFalse(created.getDefault());
createdProfiles.add(created);
}
// Find all, check count
PageDataAssetProfile allProfiles = client.getAssetProfiles(100, 0, null, null, null);
assertNotNull(allProfiles);
assertEquals(initialSize + 5, allProfiles.getData().size());
// Find all with text search
PageDataAssetProfile filteredProfiles = client.getAssetProfiles(100, 0, "Test Asset Profile " + timestamp, null, null);
assertEquals(5, filteredProfiles.getData().size());
// Get by id
AssetProfile searchProfile = createdProfiles.get(2);
AssetProfile fetchedProfile = client.getAssetProfileById(searchProfile.getId().getId().toString(), false);
assertEquals(searchProfile.getName(), fetchedProfile.getName());
assertEquals(searchProfile.getDescription(), fetchedProfile.getDescription());
// Update asset profile
fetchedProfile.setDescription("Updated description");
AssetProfile updatedProfile = client.saveAssetProfile(fetchedProfile);
assertEquals("Updated description", updatedProfile.getDescription());
assertEquals(fetchedProfile.getName(), updatedProfile.getName());
// Get asset profile info by id
AssetProfileInfo profileInfo = client.getAssetProfileInfoById(searchProfile.getId().getId().toString());
assertNotNull(profileInfo);
assertEquals(searchProfile.getName(), profileInfo.getName());
// Get asset profile infos (paginated)
PageDataAssetProfile profileInfos = client.getAssetProfiles(100, 0, null, null, null);
assertNotNull(profileInfos);
assertEquals(initialSize + 5, profileInfos.getData().size());
// Set a profile as default
AssetProfile profileToSetDefault = createdProfiles.get(1);
AssetProfile newDefault = client.setDefaultAssetProfile(profileToSetDefault.getId().getId().toString());
assertNotNull(newDefault);
assertTrue(newDefault.getDefault());
// Verify default profile info now points to the new default
AssetProfileInfo newDefaultInfo = client.getDefaultAssetProfileInfo();
assertEquals(profileToSetDefault.getName(), newDefaultInfo.getName());
// Get asset profile names
List<EntityInfo> profileNames = client.getAssetProfileNames(false);
assertNotNull(profileNames);
assertEquals(createdProfiles.size() + 1, profileNames.size());
// Delete asset profile (cannot delete the default one, so delete a non-default one)
UUID profileToDeleteId = createdProfiles.get(0).getId().getId();
client.deleteAssetProfile(profileToDeleteId.toString());
// Verify the profile is deleted
assertReturns404(() ->
client.getAssetProfileById(profileToDeleteId.toString(), false));
// Verify count after deletion
PageDataAssetProfile profilesAfterDelete = client.getAssetProfiles(100, 0, null, null, null);
assertEquals(initialSize + 4, profilesAfterDelete.getData().size());
// Restore original default profile
AssetProfile originalDefault = initialProfiles.getData().stream()
.filter(AssetProfile::getDefault)
.findFirst()
.orElseThrow();
client.setDefaultAssetProfile(originalDefault.getId().getId().toString());
}
}

286
application/src/test/java/org/thingsboard/server/client/CalculatedFieldJavaClientTest.java

@ -0,0 +1,286 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AlarmCalculatedFieldConfiguration;
import org.thingsboard.client.model.AlarmConditionValueAlarmRuleSchedule;
import org.thingsboard.client.model.AlarmRuleDefinition;
import org.thingsboard.client.model.AlarmRuleSimpleCondition;
import org.thingsboard.client.model.AlarmRuleSpecificTimeSchedule;
import org.thingsboard.client.model.AlarmSeverity;
import org.thingsboard.client.model.Argument;
import org.thingsboard.client.model.ArgumentType;
import org.thingsboard.client.model.CalculatedField;
import org.thingsboard.client.model.CalculatedFieldType;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.EntityType;
import org.thingsboard.client.model.PageDataCalculatedField;
import org.thingsboard.client.model.ReferencedEntityKey;
import org.thingsboard.client.model.SimpleCalculatedFieldConfiguration;
import org.thingsboard.client.model.TbelAlarmConditionExpression;
import org.thingsboard.client.model.TimeSeriesOutput;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class CalculatedFieldJavaClientTest extends AbstractJavaClientTest {
@Test
public void testCalculatedFieldLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<CalculatedField> createdFields = new ArrayList<>();
// create devices to attach calculated fields to
Device device1 = new Device();
device1.setName("CalcFieldDevice1_" + timestamp);
device1.setType("default");
Device createdDevice1 = client.saveDevice(device1, null, null, null, null);
Device device2 = new Device();
device2.setName("CalcFieldDevice2_" + timestamp);
device2.setType("default");
Device createdDevice2 = client.saveDevice(device2, null, null, null, null);
// create calculated fields on device1
for (int i = 0; i < 5; i++) {
CalculatedField cf = new CalculatedField();
cf.setName(TEST_PREFIX + "CalcField_" + timestamp + "_" + i);
cf.setType(CalculatedFieldType.SIMPLE);
cf.setEntityId(createdDevice1.getId());
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
Argument arg = new Argument();
ReferencedEntityKey refKey = new ReferencedEntityKey();
refKey.setKey("temperature");
refKey.setType(ArgumentType.TS_LATEST);
arg.setRefEntityKey(refKey);
config.putArgumentsItem("temp", arg);
config.setExpression("temp * " + (i + 1));
TimeSeriesOutput output = new TimeSeriesOutput();
output.setName("scaledTemp_" + i);
config.setOutput(output);
cf.setConfiguration(config);
CalculatedField created = client.saveCalculatedField(cf);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(cf.getName(), created.getName());
assertEquals(CalculatedFieldType.SIMPLE, created.getType());
createdFields.add(created);
}
// create calculated fields on device2
for (int i = 0; i < 3; i++) {
CalculatedField cf = new CalculatedField();
cf.setName(TEST_PREFIX + "CalcField2_" + timestamp + "_" + i);
cf.setType(CalculatedFieldType.SIMPLE);
cf.setEntityId(createdDevice2.getId());
SimpleCalculatedFieldConfiguration config = new SimpleCalculatedFieldConfiguration();
Argument arg = new Argument();
ReferencedEntityKey refKey = new ReferencedEntityKey();
refKey.setKey("humidity");
refKey.setType(ArgumentType.TS_LATEST);
arg.setRefEntityKey(refKey);
config.putArgumentsItem("hum", arg);
config.setExpression("hum + " + i);
TimeSeriesOutput output = new TimeSeriesOutput();
output.setName("adjustedHumidity_" + i);
config.setOutput(output);
cf.setConfiguration(config);
CalculatedField created = client.saveCalculatedField(cf);
assertNotNull(created);
createdFields.add(created);
}
// get calculated fields by entity id for device1
PageDataCalculatedField device1Fields = client.getCalculatedFieldsByEntityId(
EntityType.DEVICE.toString(), createdDevice1.getId().getId().toString(),
100, 0, CalculatedFieldType.SIMPLE, null, null, null);
assertNotNull(device1Fields);
assertEquals(5, device1Fields.getData().size());
// get calculated fields by entity id for device2
PageDataCalculatedField device2Fields = client.getCalculatedFieldsByEntityId(
EntityType.DEVICE.toString(), createdDevice2.getId().getId().toString(),
100, 0, CalculatedFieldType.SIMPLE, null, null, null);
assertEquals(3, device2Fields.getData().size());
// get by id
CalculatedField searchField = createdFields.get(2);
CalculatedField fetchedField = client.getCalculatedFieldById(searchField.getId().getId().toString());
assertEquals(searchField.getName(), fetchedField.getName());
assertEquals(searchField.getType(), fetchedField.getType());
assertNotNull(fetchedField.getConfiguration());
SimpleCalculatedFieldConfiguration fetchedConfig =
(SimpleCalculatedFieldConfiguration) fetchedField.getConfiguration();
assertEquals("temp * 3", fetchedConfig.getExpression());
// update calculated field
fetchedField.setName(fetchedField.getName() + "_updated");
fetchedConfig.setExpression("temp * 100");
CalculatedField updatedField = client.saveCalculatedField(fetchedField);
assertEquals(fetchedField.getName(), updatedField.getName());
SimpleCalculatedFieldConfiguration updatedConfig =
(SimpleCalculatedFieldConfiguration) updatedField.getConfiguration();
assertEquals("temp * 100", updatedConfig.getExpression());
// delete calculated field
UUID fieldToDeleteId = createdFields.get(0).getId().getId();
client.deleteCalculatedField(fieldToDeleteId.toString());
// verify deletion
assertReturns404(() ->
client.getCalculatedFieldById(fieldToDeleteId.toString())
);
PageDataCalculatedField device1FieldsAfterDelete = client.getCalculatedFieldsByEntityId(
EntityType.DEVICE.toString(), createdDevice1.getId().getId().toString(),
100, 0, null, null, null, null);
assertEquals(4, device1FieldsAfterDelete.getData().size());
}
@Test
public void testAlarmCalculatedFieldLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// create a device to attach the alarm calculated field to
Device device = new Device();
device.setName("AlarmCalcFieldDevice_" + timestamp);
device.setType("default");
Device createdDevice = client.saveDevice(device, null, null, null, null);
// build the alarm calculated field configuration
AlarmCalculatedFieldConfiguration config = new AlarmCalculatedFieldConfiguration();
// argument: temperature time-series
Argument tempArg = new Argument();
ReferencedEntityKey refKey = new ReferencedEntityKey();
refKey.setKey("temperature");
refKey.setType(ArgumentType.TS_LATEST);
tempArg.setRefEntityKey(refKey);
config.putArgumentsItem("temp", tempArg);
// create rule: HIGH_TEMPERATURE when temp > 50 (TBEL expression)
TbelAlarmConditionExpression createExpression = new TbelAlarmConditionExpression();
createExpression.setExpression("return temp > 50;");
AlarmRuleSimpleCondition createCondition = new AlarmRuleSimpleCondition();
createCondition.setExpression(createExpression);
AlarmRuleSpecificTimeSchedule specificTimeSchedule = new AlarmRuleSpecificTimeSchedule().addDaysOfWeekItem(3);
AlarmConditionValueAlarmRuleSchedule schedule = new AlarmConditionValueAlarmRuleSchedule().staticValue(specificTimeSchedule);
createCondition.setSchedule(schedule);
AlarmRuleDefinition createRule = new AlarmRuleDefinition();
createRule.setCondition(createCondition);
createRule.setAlarmDetails("Temperature is too high: ${temp}");
config.setCreateRules(Map.of(
AlarmSeverity.CRITICAL.name(), createRule
));
// clear rule: when temp drops below 30
TbelAlarmConditionExpression clearExpression = new TbelAlarmConditionExpression();
clearExpression.setExpression("return temp < 30;");
AlarmRuleSimpleCondition clearCondition = new AlarmRuleSimpleCondition();
clearCondition.setExpression(clearExpression);
AlarmRuleDefinition clearRule = new AlarmRuleDefinition();
clearRule.setCondition(clearCondition);
config.setClearRule(clearRule);
config.setPropagate(true);
config.setPropagateToOwner(false);
// create calculated field
CalculatedField cf = new CalculatedField();
cf.setName(TEST_PREFIX + "AlarmCalcField_" + timestamp);
cf.setType(CalculatedFieldType.ALARM);
cf.setEntityId(createdDevice.getId());
cf.setConfiguration(config);
CalculatedField created = client.saveCalculatedField(cf);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(cf.getName(), created.getName());
assertEquals(CalculatedFieldType.ALARM, created.getType());
AlarmCalculatedFieldConfiguration configuration = (AlarmCalculatedFieldConfiguration) created.getConfiguration();
AlarmConditionValueAlarmRuleSchedule createdSchedule = configuration.getCreateRules().get(AlarmSeverity.CRITICAL.name()).getCondition().getSchedule();
AlarmRuleSpecificTimeSchedule staticSchedule = (AlarmRuleSpecificTimeSchedule) createdSchedule.getStaticValue();
assertEquals(Set.of(3), staticSchedule.getDaysOfWeek());
// get by id and verify configuration
CalculatedField fetched = client.getCalculatedFieldById(created.getId().getId().toString());
assertNotNull(fetched);
assertEquals(created.getName(), fetched.getName());
assertEquals(CalculatedFieldType.ALARM, fetched.getType());
assertNotNull(fetched.getConfiguration());
AlarmCalculatedFieldConfiguration fetchedConfig =
(AlarmCalculatedFieldConfiguration) fetched.getConfiguration();
assertNotNull(fetchedConfig.getCreateRules());
assertEquals(1, fetchedConfig.getCreateRules().size());
assertTrue(fetchedConfig.getCreateRules().containsKey("CRITICAL"));
assertNotNull(fetchedConfig.getClearRule());
assertEquals(Boolean.TRUE, fetchedConfig.getPropagate());
// update: add a second create rule for CRITICAL_TEMPERATURE
TbelAlarmConditionExpression criticalExpression = new TbelAlarmConditionExpression();
criticalExpression.setExpression("return temp > 80;");
AlarmRuleSimpleCondition criticalCondition = new AlarmRuleSimpleCondition();
criticalCondition.setExpression(criticalExpression);
AlarmRuleDefinition criticalRule = new AlarmRuleDefinition();
criticalRule.setCondition(criticalCondition);
fetchedConfig.putCreateRulesItem(AlarmSeverity.INDETERMINATE.name(), criticalRule);
fetched.setConfiguration(fetchedConfig);
CalculatedField updated = client.saveCalculatedField(fetched);
AlarmCalculatedFieldConfiguration updatedConfig =
(AlarmCalculatedFieldConfiguration) updated.getConfiguration();
assertEquals(2, updatedConfig.getCreateRules().size());
assertTrue(updatedConfig.getCreateRules().containsKey("INDETERMINATE"));
// filter by entity and ALARM type
PageDataCalculatedField deviceFields = client.getCalculatedFieldsByEntityId(
EntityType.DEVICE.toString(), createdDevice.getId().getId().toString(),
100, 0, CalculatedFieldType.ALARM, null, null, null);
assertNotNull(deviceFields);
assertEquals(1, deviceFields.getData().size());
// delete and verify
UUID fieldId = created.getId().getId();
client.deleteCalculatedField(fieldId.toString());
assertReturns404(() -> client.getCalculatedFieldById(fieldId.toString()));
}
}

113
application/src/test/java/org/thingsboard/server/client/CustomerJavaClientTest.java

@ -0,0 +1,113 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Customer;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.PageDataCustomer;
import org.thingsboard.client.model.PageDataDevice;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class CustomerJavaClientTest extends AbstractJavaClientTest {
@Test
public void testCustomerLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Customer> createdCustomers = new ArrayList<>();
// create 20 customers
for (int i = 0; i < 20; i++) {
Customer customer = new Customer();
String customerTitle = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i;
customer.setTitle(customerTitle);
customer.setEmail("customer_" + timestamp + "_" + i + "@test.com");
Customer createdCustomer = client.saveCustomer(customer, null, null, null);
assertNotNull(createdCustomer);
assertNotNull(createdCustomer.getId());
assertEquals(customerTitle, createdCustomer.getTitle());
createdCustomers.add(createdCustomer);
}
// find all, check count (includes savedClientCustomer from AbstractJavaClientTest setup)
PageDataCustomer allCustomers = client.getCustomers(100, 0, null, null, null);
assertNotNull(allCustomers);
assertNotNull(allCustomers.getData());
int initialSize = allCustomers.getData().size();
assertEquals("Expected 21 customers (20 created + 1 from setup), but got " + initialSize, 21, initialSize);
// find all with search text, check count
PageDataCustomer filteredCustomers = client.getCustomers(100, 0, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 customers matching prefix", 10, filteredCustomers.getData().size());
// find by id
Customer searchCustomer = createdCustomers.get(10);
Customer fetchedCustomer = client.getCustomerById(searchCustomer.getId().getId().toString());
assertEquals(searchCustomer.getTitle(), fetchedCustomer.getTitle());
// find by title
Customer fetchedByTitle = client.getTenantCustomer(searchCustomer.getTitle());
assertEquals(searchCustomer.getId().getId(), fetchedByTitle.getId().getId());
// update customer
fetchedCustomer.setCity("New York");
fetchedCustomer.setCountry("US");
Customer updatedCustomer = client.saveCustomer(fetchedCustomer, null, null, null);
assertEquals("New York", updatedCustomer.getCity());
assertEquals("US", updatedCustomer.getCountry());
// assign device to customer and verify
Device device = new Device();
device.setName("CustomerTestDevice_" + timestamp);
device.setType("default");
Device createdDevice = client.saveDevice(device, null, null, null, null);
String customerId = createdCustomers.get(0).getId().getId().toString();
client.assignDeviceToCustomer(customerId, createdDevice.getId().getId().toString());
PageDataDevice customerDevices = client.getCustomerDevices(customerId, 100, 0, null, null, null, null);
assertEquals(1, customerDevices.getData().size());
assertEquals(createdDevice.getName(), customerDevices.getData().get(0).getName());
// unassign device from customer
client.unassignDeviceFromCustomer(createdDevice.getId().getId().toString());
PageDataDevice devicesAfterUnassign = client.getCustomerDevices(customerId, 100, 0, null, null, null, null);
assertEquals(0, devicesAfterUnassign.getData().size());
// delete customer
UUID customerToDeleteId = createdCustomers.get(0).getId().getId();
client.deleteCustomer(customerToDeleteId.toString());
// verify deletion
PageDataCustomer customersAfterDelete = client.getCustomers(100, 0, null, null, null);
assertEquals(initialSize - 1, customersAfterDelete.getData().size());
assertReturns404(() ->
client.getCustomerById(customerToDeleteId.toString())
);
}
}

123
application/src/test/java/org/thingsboard/server/client/DashboardJavaClientTest.java

@ -0,0 +1,123 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Dashboard;
import org.thingsboard.client.model.DashboardInfo;
import org.thingsboard.client.model.PageDataDashboardInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class DashboardJavaClientTest extends AbstractJavaClientTest {
@Test
public void testDashboardLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// create 20 dashboards
for (int i = 0; i < 20; i++) {
Dashboard dashboard = new Dashboard();
String dashboardTitle = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i;
dashboard.setTitle(dashboardTitle);
client.saveDashboard(dashboard, null);
}
// find all, check count
PageDataDashboardInfo allDashboards = client.getTenantDashboards(100, 0, null, null, null, null);
assertNotNull(allDashboards);
assertNotNull(allDashboards.getData());
int initialSize = allDashboards.getData().size();
assertEquals("Expected 20 dashboards, but got " + initialSize, 20, initialSize);
List<DashboardInfo> createdDashboards = allDashboards.getData();
// find all with search text, check count
PageDataDashboardInfo filteredDashboards = client.getTenantDashboards(100, 0, null, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 dashboards matching prefix", 10, filteredDashboards.getData().size());
// find by id
DashboardInfo searchDashboard = createdDashboards.get(10);
DashboardInfo fetchedDashboard = client.getDashboardInfoById(searchDashboard.getId().getId().toString());
assertEquals(searchDashboard.getTitle(), fetchedDashboard.getTitle());
// update dashboard
Dashboard dashboardToUpdate = new Dashboard();
dashboardToUpdate.setId(fetchedDashboard.getId());
dashboardToUpdate.setTitle(fetchedDashboard.getTitle() + "_updated");
dashboardToUpdate.setVersion(fetchedDashboard.getVersion());
client.saveDashboard(dashboardToUpdate, null);
DashboardInfo updatedDashboard = client.getDashboardInfoById(fetchedDashboard.getId().getId().toString());
assertEquals(fetchedDashboard.getTitle() + "_updated", updatedDashboard.getTitle());
// assign dashboard to customer and verify
String customerId = savedClientCustomer.getId().getId().toString();
String dashboardId = createdDashboards.get(0).getId().getId().toString();
client.assignDashboardToCustomer(customerId, dashboardId);
PageDataDashboardInfo customerDashboards = client.getCustomerDashboards(customerId, 100, 0, null, null, null, null);
assertEquals(1, customerDashboards.getData().size());
assertEquals(createdDashboards.get(0).getTitle(), customerDashboards.getData().get(0).getTitle());
// unassign dashboard from customer
client.unassignDashboardFromCustomer(customerId, dashboardId);
PageDataDashboardInfo dashboardsAfterUnassign = client.getCustomerDashboards(customerId, 100, 0, null, null, null, null);
assertEquals(0, dashboardsAfterUnassign.getData().size());
// make dashboard public and verify
client.assignDashboardToPublicCustomer(dashboardId);
DashboardInfo publicDashboard = client.getDashboardInfoById(dashboardId);
assertNotNull(publicDashboard.getAssignedCustomers());
assertTrue(publicDashboard.getAssignedCustomers().size() > 0);
// remove public access
client.unassignDashboardFromPublicCustomer(dashboardId);
// delete dashboard
UUID dashboardToDeleteId = createdDashboards.get(0).getId().getId();
client.deleteDashboard(dashboardToDeleteId.toString());
// verify deletion
PageDataDashboardInfo dashboardsAfterDelete = client.getTenantDashboards(100, 0, null, null, null, null);
assertEquals(initialSize - 1, dashboardsAfterDelete.getData().size());
assertReturns404(() ->
client.getDashboardInfoById(dashboardToDeleteId.toString())
);
}
@Test
public void testGetServerTime() throws Exception {
Long serverTime = client.getServerTime();
assertNotNull(serverTime);
}
@Test
public void testGetMaxDatapointsLimit() throws Exception {
Long maxDatapointsLimit = client.getMaxDatapointsLimit();
assertNotNull(maxDatapointsLimit);
}
}

53
application/src/test/java/org/thingsboard/server/client/DeviceConnectivityJavaClientTest.java

@ -0,0 +1,53 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.thingsboard.client.model.Device;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
@DaoSqlTest
public class DeviceConnectivityJavaClientTest extends AbstractJavaClientTest {
@Test
public void testGetDevicePublishTelemetryCommands() throws Exception {
Device device = new Device();
device.setName(TEST_PREFIX + System.currentTimeMillis());
device.setType("default");
Device savedDevice = client.saveDevice(device, null, null, null, null);
String token = client.getDeviceCredentialsByDeviceId(savedDevice.getId().getId().toString()).getCredentialsId();
String deviceId = savedDevice.getId().getId().toString();
JsonNode commands = client.getDevicePublishTelemetryCommands(deviceId);
assertEquals("curl -v -X POST http://localhost:8080/api/v1/" + token + "/telemetry --header Content-Type:application/json --data \"{temperature:25}\"", commands.get("http").get("http").asText());
assertEquals("mosquitto_pub -d -q 1 -h localhost -p 1883 -t v1/devices/me/telemetry -u \"" + token + "\" -m \"{temperature:25}\"", commands.get("mqtt").get("mqtt").asText());
assertEquals("coap-client -v 6 -m POST -t \"application/json\" -e \"{temperature:25}\" coap://localhost:5683/api/v1/" + token + "/telemetry", commands.get("coap").get("coap").asText());
}
@Test
public void testGetDevicePublishTelemetryCommands_nonExistentDevice() {
String nonExistentId = UUID.randomUUID().toString();
assertReturns404(() -> client.getDevicePublishTelemetryCommands(nonExistentId));
}
}

114
application/src/test/java/org/thingsboard/server/client/DeviceJavaClientTest.java

@ -0,0 +1,114 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.DeviceCredentials;
import org.thingsboard.client.model.DeviceCredentialsType;
import org.thingsboard.client.model.PageDataDevice;
import org.thingsboard.client.model.SaveDeviceWithCredentialsRequest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class DeviceJavaClientTest extends AbstractJavaClientTest {
@Test
public void testDeviceLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Device> createdDevices = new ArrayList<>();
// create 20 devices
for (int i = 0; i < 20; i++) {
Device device = new Device();
String deviceName = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i;
device.setName(deviceName);
device.setLabel("Test Device " + i);
device.setType(((i % 2 == 0) ? "default" : "thermostat"));
Device createdDevice = client.saveDevice(device, null, null, null, null);
assertNotNull(createdDevice);
assertNotNull(createdDevice.getId());
assertEquals(deviceName, createdDevice.getName());
createdDevices.add(createdDevice);
}
// find all, check count
PageDataDevice allDevices = client.getTenantDevices(100, 0, null, null, null, null);
assertNotNull(allDevices);
assertNotNull(allDevices.getData());
int initialSize = allDevices.getData().size();
assertEquals("Expected at least 20 devices, but got " + allDevices.getData().size(), 20, initialSize);
//find all with search text, check count
PageDataDevice allDevicesBySearchText = client.getTenantDevices(10, 0, null, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 test devices", 10, allDevicesBySearchText.getData().size());
// find by id
Device searchDevice = createdDevices.get(10);
Device device = client.getDeviceById(searchDevice.getId().getId().toString());
assertEquals(searchDevice.getName(), device.getName());
// create device with credentials
Device deviceWithCreds = new Device();
deviceWithCreds.setName("device-with-creds");
DeviceCredentials creds = new DeviceCredentials();
creds.setCredentialsType(DeviceCredentialsType.ACCESS_TOKEN);
creds.setCredentialsId("TEST_ACCESS_TOKEN");
SaveDeviceWithCredentialsRequest request = new SaveDeviceWithCredentialsRequest();
request.setDevice(deviceWithCreds);
request.setCredentials(creds);
Device savedDeviceWithCreds = client.saveDeviceWithCredentials(request, null, null, null);
assertEquals("device-with-creds", savedDeviceWithCreds.getName());
// find credentials by device id
DeviceCredentials fetchedCreds = client.getDeviceCredentialsByDeviceId(savedDeviceWithCreds.getId().getId().toString());
assertEquals(creds.getCredentialsId(), fetchedCreds.getCredentialsId());
// delete device
UUID deviceToDeleteId = createdDevices.get(0).getId().getId();
client.deleteDevice(deviceToDeleteId.toString());
// Verify the device is deleted
PageDataDevice devicesAfterDelete = client.getTenantDevices(100, 0, null, null, null, null);
assertEquals(initialSize, devicesAfterDelete.getData().size());
assertReturns404(() ->
client.getDeviceById(deviceToDeleteId.toString()));
// assign device to customer
client.assignDeviceToCustomer(savedClientCustomer.getId().getId().toString(), savedDeviceWithCreds.getId().getId().toString());
// check customer devices
PageDataDevice pageDataDevice = client.getCustomerDevices(savedClientCustomer.getId().getId().toString(), 100, 0, null, null, null, null);
List<Device> data = pageDataDevice.getData();
assertEquals(1, data.size());
assertEquals(savedDeviceWithCreds.getName(), data.get(0).getName());
}
}

157
application/src/test/java/org/thingsboard/server/client/DeviceProfileJavaClientTest.java

@ -0,0 +1,157 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.DefaultDeviceProfileConfiguration;
import org.thingsboard.client.model.DefaultDeviceProfileTransportConfiguration;
import org.thingsboard.client.model.DeviceProfile;
import org.thingsboard.client.model.DeviceProfileData;
import org.thingsboard.client.model.DeviceProfileInfo;
import org.thingsboard.client.model.DeviceProfileType;
import org.thingsboard.client.model.DeviceTransportType;
import org.thingsboard.client.model.EntityInfo;
import org.thingsboard.client.model.PageDataDeviceProfile;
import org.thingsboard.client.model.PageDataDeviceProfileInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class DeviceProfileJavaClientTest extends AbstractJavaClientTest {
@Test
public void testDeviceProfileLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<DeviceProfile> createdProfiles = new ArrayList<>();
// Get initial count (there should be a default profile)
PageDataDeviceProfile initialProfiles = client.getDeviceProfiles(100, 0, null, null, null);
assertNotNull(initialProfiles);
int initialSize = initialProfiles.getData().size();
assertTrue("Expected at least 1 default device profile", initialSize >= 1);
// Get default device profile info
DeviceProfileInfo defaultProfileInfo = client.getDefaultDeviceProfileInfo();
assertNotNull(defaultProfileInfo);
assertNotNull(defaultProfileInfo.getName());
// Create multiple device profiles
for (int i = 0; i < 5; i++) {
DeviceProfile deviceProfile = new DeviceProfile();
deviceProfile.setName("Test Device Profile " + timestamp + "_" + i);
deviceProfile.setDescription("Test description " + i);
deviceProfile.setType(DeviceProfileType.DEFAULT);
deviceProfile.setTransportType(DeviceTransportType.DEFAULT);
DeviceProfileData deviceProfileData = new DeviceProfileData();
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration();
configuration.setType(DeviceProfileType.DEFAULT.getValue());
deviceProfileData.setConfiguration(configuration);
DefaultDeviceProfileTransportConfiguration transportConf = new DefaultDeviceProfileTransportConfiguration();
transportConf.setType(DeviceTransportType.DEFAULT.getValue());
deviceProfileData.setTransportConfiguration(transportConf);
deviceProfile.setProfileData(deviceProfileData);
deviceProfile.setDefault(false);
deviceProfile.setDefaultRuleChainId(null);
DeviceProfile created = client.saveDeviceProfile(deviceProfile);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(deviceProfile.getName(), created.getName());
assertEquals(deviceProfile.getDescription(), created.getDescription());
assertEquals(DeviceProfileType.DEFAULT, created.getType());
assertEquals(DeviceTransportType.DEFAULT, created.getTransportType());
assertFalse(created.getDefault());
createdProfiles.add(created);
}
// Find all, check count
PageDataDeviceProfile allProfiles = client.getDeviceProfiles(100, 0, null, null, null);
assertNotNull(allProfiles);
assertEquals(initialSize + 5, allProfiles.getData().size());
// Find all with text search
PageDataDeviceProfile filteredProfiles = client.getDeviceProfiles(100, 0, "Test Device Profile " + timestamp, null, null);
assertEquals(5, filteredProfiles.getData().size());
// Get by id
DeviceProfile searchProfile = createdProfiles.get(2);
DeviceProfile fetchedProfile = client.getDeviceProfileById(searchProfile.getId().getId().toString(), false);
assertEquals(searchProfile.getName(), fetchedProfile.getName());
assertEquals(searchProfile.getDescription(), fetchedProfile.getDescription());
// Update device profile
fetchedProfile.setDescription("Updated description");
DeviceProfile updatedProfile = client.saveDeviceProfile(fetchedProfile);
assertEquals("Updated description", updatedProfile.getDescription());
assertEquals(fetchedProfile.getName(), updatedProfile.getName());
// Get device profile info by id
DeviceProfileInfo profileInfo = client.getDefaultDeviceProfileInfo();
assertNotNull(profileInfo);
assertEquals(searchProfile.getType().getValue().toLowerCase(), profileInfo.getName());
assertEquals(DeviceTransportType.DEFAULT, profileInfo.getTransportType());
// Get device profile infos (paginated)
PageDataDeviceProfileInfo profileInfos = client.getDeviceProfileInfos(100, 0, null, null, null, null);
assertNotNull(profileInfos);
assertEquals(initialSize + 5, profileInfos.getData().size());
// Set a profile as default
DeviceProfile profileToSetDefault = createdProfiles.get(1);
DeviceProfile newDefault = client.setDefaultDeviceProfile(profileToSetDefault.getId().getId().toString());
assertNotNull(newDefault);
assertTrue(newDefault.getDefault());
// Verify default profile info now points to the new default
DeviceProfileInfo newDefaultInfo = client.getDefaultDeviceProfileInfo();
assertEquals(profileToSetDefault.getName(), newDefaultInfo.getName());
// Get device profile names
List<EntityInfo> profileNames = client.getDeviceProfileNames(false);
assertNotNull(profileNames);
assertEquals(createdProfiles.size() + 1, profileNames.size());
// Delete device profile (cannot delete the default one, so delete a non-default one)
UUID profileToDeleteId = createdProfiles.get(0).getId().getId();
client.deleteDeviceProfile(profileToDeleteId.toString());
// Verify the profile is deleted
assertReturns404(() ->
client.getDeviceProfileById(profileToDeleteId.toString(), false));
// Verify count after deletion
PageDataDeviceProfile profilesAfterDelete = client.getDeviceProfiles(100, 0, null, null, null);
assertEquals(initialSize + 4, profilesAfterDelete.getData().size());
// Restore original default profile
DeviceProfile originalDefault = initialProfiles.getData().stream()
.filter(DeviceProfile::getDefault)
.findFirst()
.orElseThrow();
client.setDefaultDeviceProfile(originalDefault.getId().getId().toString());
}
}

105
application/src/test/java/org/thingsboard/server/client/DomainJavaClientTest.java

@ -0,0 +1,105 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.After;
import org.junit.Test;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.model.Domain;
import org.thingsboard.client.model.DomainInfo;
import org.thingsboard.client.model.PageDataDomainInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class DomainJavaClientTest extends AbstractJavaClientTest {
List<Domain> createdDomains = new ArrayList<>();
@After
public void afterDomainTest() {
createdDomains.forEach(domain -> {
try {
client.deleteDomain(domain.getId().getId());
} catch (ApiException e) {
// ignore
}
});
}
@Test
public void testDomainLifecycle() throws Exception {
client.login("sysadmin@thingsboard.org", "sysadmin");
long timestamp = System.currentTimeMillis();
// create 5 domains
for (int i = 0; i < 5; i++) {
Domain domain = new Domain();
domain.setName("domain." + i + ".com");
domain.setOauth2Enabled(false);
domain.setPropagateToEdge(false);
Domain created = client.saveDomain(domain, null);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(domain.getName(), created.getName());
assertEquals(false, created.getOauth2Enabled());
createdDomains.add(created);
}
// list tenant domains with text search
PageDataDomainInfo filteredDomains = client.getTenantDomainInfos(100, 0,
"domain.", null, null);
assertNotNull(filteredDomains);
assertEquals(5, filteredDomains.getData().size());
// get domain info by id
Domain searchDomain = createdDomains.get(2);
DomainInfo fetchedInfo = client.getDomainInfoById(searchDomain.getId().getId());
assertEquals(searchDomain.getName(), fetchedInfo.getName());
assertEquals(searchDomain.getOauth2Enabled(), fetchedInfo.getOauth2Enabled());
assertNotNull(fetchedInfo.getOauth2ClientInfos());
// update domain
Domain domainToUpdate = createdDomains.get(3);
domainToUpdate.setPropagateToEdge(true);
Domain updatedDomain = client.saveDomain(domainToUpdate, null);
assertEquals(true, updatedDomain.getPropagateToEdge());
// delete domain
UUID domainToDeleteId = createdDomains.get(0).getId().getId();
createdDomains.remove(0);
client.deleteDomain(domainToDeleteId);
// verify deletion
assertReturns404(() ->
client.getDomainInfoById(domainToDeleteId)
);
PageDataDomainInfo domainsAfterDelete = client.getTenantDomainInfos(100, 0,
"domain.", null, null);
assertEquals(4, domainsAfterDelete.getData().size());
}
}

141
application/src/test/java/org/thingsboard/server/client/EdgeJavaClientTest.java

@ -0,0 +1,141 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Edge;
import org.thingsboard.client.model.EdgeInfo;
import org.thingsboard.client.model.PageDataEdge;
import org.thingsboard.client.model.PageDataEdgeInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class EdgeJavaClientTest extends AbstractJavaClientTest {
@Test
public void testEdgeLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Edge> createdEdges = new ArrayList<>();
// create 5 edges
for (int i = 0; i < 5; i++) {
Edge edge = new Edge();
edge.setName(TEST_PREFIX + "Edge_" + timestamp + "_" + i);
edge.setType("gateway");
edge.setLabel("Test Edge " + i);
edge.setRoutingKey("routing_key_" + timestamp + "_" + i);
edge.setSecret("secret_key_" + timestamp + "_" + i);
Edge created = client.saveEdge(edge);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(edge.getName(), created.getName());
assertEquals("gateway", created.getType());
assertNotNull(created.getRoutingKey());
assertNotNull(created.getSecret());
createdEdges.add(created);
}
// list tenant edges with text search
PageDataEdge filteredEdges = client.getTenantEdges(100, 0, null,
TEST_PREFIX + "Edge_" + timestamp, null, null);
assertNotNull(filteredEdges);
assertEquals(5, filteredEdges.getData().size());
// list tenant edges with type filter
PageDataEdge typedEdges = client.getTenantEdges(100, 0, "gateway",
TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(5, typedEdges.getData().size());
// get tenant edge infos
PageDataEdgeInfo edgeInfos = client.getTenantEdgeInfos(100, 0, null,
TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(5, edgeInfos.getData().size());
// get edge by id
Edge searchEdge = createdEdges.get(2);
Edge fetchedEdge = client.getEdgeById(searchEdge.getId().getId().toString());
assertEquals(searchEdge.getName(), fetchedEdge.getName());
assertEquals(searchEdge.getType(), fetchedEdge.getType());
assertEquals(searchEdge.getRoutingKey(), fetchedEdge.getRoutingKey());
// get edge by name
Edge fetchedByName = client.getTenantEdgeByName(searchEdge.getName());
assertEquals(searchEdge.getId().getId(), fetchedByName.getId().getId());
// get edges by list of ids
List<String> idsToFetch = List.of(
createdEdges.get(0).getId().getId().toString(),
createdEdges.get(1).getId().getId().toString()
);
List<Edge> edgeList = client.getEdgeList(idsToFetch);
assertEquals(2, edgeList.size());
// update edge
Edge edgeToUpdate = createdEdges.get(3);
edgeToUpdate.setLabel("Updated Label");
Edge updatedEdge = client.saveEdge(edgeToUpdate);
assertEquals("Updated Label", updatedEdge.getLabel());
// assign edge to customer
String customerId = savedClientCustomer.getId().getId().toString();
String edgeId = createdEdges.get(1).getId().getId().toString();
Edge assignedEdge = client.assignEdgeToCustomer(customerId, edgeId);
assertNotNull(assignedEdge.getCustomerId());
// get customer edges
PageDataEdge customerEdges = client.getCustomerEdges(customerId, 100, 0,
null, TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(1, customerEdges.getData().size());
// get customer edge infos
PageDataEdgeInfo customerEdgeInfos = client.getCustomerEdgeInfos(customerId, 100, 0,
null, TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(1, customerEdgeInfos.getData().size());
EdgeInfo edgeInfo = customerEdgeInfos.getData().get(0);
assertNotNull(edgeInfo.getCustomerTitle());
// unassign edge from customer
Edge unassignedEdge = client.unassignEdgeFromCustomer(edgeId);
assertNotNull(unassignedEdge);
PageDataEdge customerEdgesAfter = client.getCustomerEdges(customerId, 100, 0,
null, TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(0, customerEdgesAfter.getData().size());
// delete edge
UUID edgeToDeleteId = createdEdges.get(0).getId().getId();
client.deleteEdge(edgeToDeleteId.toString());
// verify deletion
assertReturns404(() ->
client.getEdgeById(edgeToDeleteId.toString())
);
PageDataEdge edgesAfterDelete = client.getTenantEdges(100, 0, null,
TEST_PREFIX + "Edge_" + timestamp, null, null);
assertEquals(4, edgesAfterDelete.getData().size());
}
}

289
application/src/test/java/org/thingsboard/server/client/EntityQueryJavaClientTest.java

@ -0,0 +1,289 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AliasEntityId;
import org.thingsboard.client.model.Asset;
import org.thingsboard.client.model.AssetTypeFilter;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.DeviceTypeFilter;
import org.thingsboard.client.model.Direction;
import org.thingsboard.client.model.EntityData;
import org.thingsboard.client.model.EntityDataPageLink;
import org.thingsboard.client.model.EntityDataQuery;
import org.thingsboard.client.model.EntityDataSortOrder;
import org.thingsboard.client.model.EntityKey;
import org.thingsboard.client.model.EntityKeyType;
import org.thingsboard.client.model.EntityKeyValueType;
import org.thingsboard.client.model.EntityListFilter;
import org.thingsboard.client.model.EntityNameFilter;
import org.thingsboard.client.model.EntityType;
import org.thingsboard.client.model.FilterPredicateValueString;
import org.thingsboard.client.model.KeyFilter;
import org.thingsboard.client.model.PageDataEntityData;
import org.thingsboard.client.model.SingleEntityFilter;
import org.thingsboard.client.model.StringFilterPredicate;
import org.thingsboard.client.model.StringOperation;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class EntityQueryJavaClientTest extends AbstractJavaClientTest {
private static final String QUERY_TEST_PREFIX = "QueryTest_";
private EntityDataPageLink pageLink(int pageSize) {
return new EntityDataPageLink()
.pageSize(pageSize)
.page(0)
.sortOrder(new EntityDataSortOrder()
.key(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"))
.direction(Direction.ASC));
}
@Test
public void testFindByDeviceTypeFilter() throws Exception {
long ts = System.currentTimeMillis();
String type1 = "temperatureSensor";
String type2 = "humiditySensor";
for (int i = 0; i < 3; i++) {
Device d = new Device();
d.setName(QUERY_TEST_PREFIX + "temp_" + ts + "_" + i);
d.setType(type1);
client.saveDevice(d, null, null, null, null);
}
for (int i = 0; i < 2; i++) {
Device d = new Device();
d.setName(QUERY_TEST_PREFIX + "hum_" + ts + "_" + i);
d.setType(type2);
client.saveDevice(d, null, null, null, null);
}
// filter by single device type
EntityDataQuery singleTypeQuery = new EntityDataQuery()
.entityFilter(new DeviceTypeFilter()
.deviceTypes(List.of(type1)))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(singleTypeQuery);
assertNotNull(result);
assertEquals(3, result.getTotalElements().intValue());
for (EntityData entity : result.getData()) {
assertNotNull(entity.getEntityId());
}
// filter by multiple device types
EntityDataQuery multiTypeQuery = new EntityDataQuery()
.entityFilter(new DeviceTypeFilter()
.deviceTypes(List.of(type1, type2)))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData multiResult = client.findEntityDataByQuery(multiTypeQuery);
assertNotNull(multiResult);
assertEquals(5, multiResult.getTotalElements().intValue());
// filter by device type + name filter
EntityDataQuery nameFilterQuery = new EntityDataQuery()
.entityFilter(new DeviceTypeFilter()
.deviceTypes(List.of(type1, type2))
.deviceNameFilter(QUERY_TEST_PREFIX + "temp_" + ts))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData nameResult = client.findEntityDataByQuery(nameFilterQuery);
assertNotNull(nameResult);
assertEquals(3, nameResult.getTotalElements().intValue());
}
@Test
public void testFindByEntityNameFilter() throws Exception {
long ts = System.currentTimeMillis();
String prefix = QUERY_TEST_PREFIX + "named_" + ts;
for (int i = 0; i < 4; i++) {
Device d = new Device();
d.setName(prefix + "_" + i);
d.setType("default");
client.saveDevice(d, null, null, null, null);
}
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new EntityNameFilter()
.entityType(EntityType.DEVICE)
.entityNameFilter(prefix))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(query);
assertNotNull(result);
assertEquals(4, result.getTotalElements().intValue());
assertFalse(result.getHasNext());
}
@Test
public void testFindByEntityListFilter() throws Exception {
long ts = System.currentTimeMillis();
Device d1 = client.saveDevice(new Device().name(QUERY_TEST_PREFIX + "list_" + ts + "_1").type("default"), null, null, null, null);
Device d2 = client.saveDevice(new Device().name(QUERY_TEST_PREFIX + "list_" + ts + "_2").type("default"), null, null, null, null);
client.saveDevice(new Device().name(QUERY_TEST_PREFIX + "list_" + ts + "_3").type("default"), null, null, null, null);
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new EntityListFilter()
.entityType(EntityType.DEVICE)
.entityList(List.of(
d1.getId().getId().toString(),
d2.getId().getId().toString())))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(query);
assertNotNull(result);
assertEquals(2, result.getTotalElements().intValue());
List<String> returnedIds = result.getData().stream()
.map(e -> e.getEntityId().getId().toString())
.collect(Collectors.toList());
assertTrue(returnedIds.contains(d1.getId().getId().toString()));
assertTrue(returnedIds.contains(d2.getId().getId().toString()));
}
@Test
public void testFindBySingleEntityFilter() throws Exception {
long ts = System.currentTimeMillis();
Device device = client.saveDevice(new Device().name(QUERY_TEST_PREFIX + "single_" + ts).type("default"), null, null, null, null);
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new SingleEntityFilter()
.singleEntity(new AliasEntityId()
.id(device.getId().getId())
.entityType(EntityType.DEVICE)))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(query);
assertNotNull(result);
assertEquals(1, result.getTotalElements().intValue());
assertEquals(device.getId().getId().toString(),
result.getData().get(0).getEntityId().getId().toString());
}
@Test
public void testFindByAssetTypeFilter() throws Exception {
long ts = System.currentTimeMillis();
String assetType = "building";
for (int i = 0; i < 3; i++) {
Asset a = new Asset();
a.setName(QUERY_TEST_PREFIX + "asset_" + ts + "_" + i);
a.setType(assetType);
client.saveAsset(a, null, null, null);
}
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new AssetTypeFilter()
.assetTypes(List.of(assetType)))
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(query);
assertNotNull(result);
assertEquals(3, result.getTotalElements().intValue());
}
@Test
public void testFindWithKeyFilter() throws Exception {
long ts = System.currentTimeMillis();
String matchName = QUERY_TEST_PREFIX + "kf_match_" + ts;
String noMatchName = QUERY_TEST_PREFIX + "kf_other_" + ts;
client.saveDevice(new Device().name(matchName).type("default"), null, null, null, null);
client.saveDevice(new Device().name(noMatchName).type("default"), null, null, null, null);
KeyFilter nameKeyFilter = new KeyFilter()
.key(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"))
.valueType(EntityKeyValueType.STRING)
.predicate(new StringFilterPredicate()
.operation(StringOperation.CONTAINS)
.value(new FilterPredicateValueString().defaultValue("kf_match"))
.ignoreCase(true));
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new EntityNameFilter()
.entityType(EntityType.DEVICE)
.entityNameFilter(QUERY_TEST_PREFIX + "kf_"))
.addKeyFiltersItem(nameKeyFilter)
.pageLink(pageLink(10))
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
PageDataEntityData result = client.findEntityDataByQuery(query);
assertNotNull(result);
assertEquals(1, result.getTotalElements().intValue());
}
@Test
public void testFindWithPagination() throws Exception {
long ts = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
Device d = new Device();
d.setName(QUERY_TEST_PREFIX + "page_" + ts + "_" + i);
d.setType("default");
client.saveDevice(d, null, null, null, null);
}
EntityDataPageLink smallPage = new EntityDataPageLink()
.pageSize(2)
.page(0)
.sortOrder(new EntityDataSortOrder()
.key(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"))
.direction(Direction.ASC));
EntityDataQuery query = new EntityDataQuery()
.entityFilter(new EntityNameFilter()
.entityType(EntityType.DEVICE)
.entityNameFilter(QUERY_TEST_PREFIX + "page_" + ts))
.pageLink(smallPage)
.addEntityFieldsItem(new EntityKey().type(EntityKeyType.ENTITY_FIELD).key("name"));
// first page
PageDataEntityData page1 = client.findEntityDataByQuery(query);
assertNotNull(page1);
assertEquals(5, page1.getTotalElements().intValue());
assertEquals(3, page1.getTotalPages().intValue());
assertEquals(2, page1.getData().size());
assertTrue(page1.getHasNext());
// last page
smallPage.setPage(2);
PageDataEntityData lastPage = client.findEntityDataByQuery(query);
assertNotNull(lastPage);
assertEquals(1, lastPage.getData().size());
assertFalse(lastPage.getHasNext());
}
}

182
application/src/test/java/org/thingsboard/server/client/EntityRelationJavaClientTest.java

@ -0,0 +1,182 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Asset;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.EntityRelation;
import org.thingsboard.client.model.EntityRelationInfo;
import org.thingsboard.client.model.EntityRelationsQuery;
import org.thingsboard.client.model.EntitySearchDirection;
import org.thingsboard.client.model.EntityType;
import org.thingsboard.client.model.RelationEntityTypeFilter;
import org.thingsboard.client.model.RelationTypeGroup;
import org.thingsboard.client.model.RelationsSearchParameters;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class EntityRelationJavaClientTest extends AbstractJavaClientTest {
@Test
public void testEntityRelationLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// create assets and devices to relate
Asset building = new Asset();
building.setName(TEST_PREFIX + "Building_" + timestamp);
building.setType("building");
building = client.saveAsset(building, null, null, null);
Asset floor = new Asset();
floor.setName(TEST_PREFIX + "Floor_" + timestamp);
floor.setType("floor");
floor = client.saveAsset(floor, null, null, null);
Device device1 = new Device();
device1.setName(TEST_PREFIX + "Sensor_" + timestamp + "_1");
device1.setType("sensor");
device1 = client.saveDevice(device1, null, null, null, null);
Device device2 = new Device();
device2.setName(TEST_PREFIX + "Sensor_" + timestamp + "_2");
device2.setType("sensor");
device2 = client.saveDevice(device2, null, null, null, null);
Device device3 = new Device();
device3.setName(TEST_PREFIX + "Sensor_" + timestamp + "_3");
device3.setType("sensor");
device3 = client.saveDevice(device3, null, null, null, null);
// create relations: building -> Contains -> floor, floor -> Contains -> device1/device2/device3
EntityRelation buildingToFloor = new EntityRelation();
buildingToFloor.setFrom(building.getId());
buildingToFloor.setTo(floor.getId());
buildingToFloor.setType("Contains");
buildingToFloor.setTypeGroup(RelationTypeGroup.COMMON);
EntityRelation savedRelation = client.saveRelation(buildingToFloor);
assertNotNull(savedRelation);
assertEquals("Contains", savedRelation.getType());
client.saveRelation(new EntityRelation()
.from(floor.getId())
.to(device1.getId())
.type("Contains")
.typeGroup(RelationTypeGroup.COMMON));
client.saveRelation(new EntityRelation()
.from(floor.getId())
.to(device2.getId())
.type("Contains").typeGroup(RelationTypeGroup.COMMON));
client.saveRelation(new EntityRelation()
.from(floor.getId())
.to(device3.getId())
.type("Manages")
.typeGroup(RelationTypeGroup.COMMON));
// get specific relation
EntityRelation fetched = client.getRelation(
building.getId().getId().toString(), "ASSET",
"Contains",
floor.getId().getId().toString(), "ASSET",
RelationTypeGroup.COMMON.getValue());
assertNotNull(fetched);
assertEquals("Contains", fetched.getType());
// find all relations from floor
List<EntityRelation> fromFloor = client.findEntityRelationsByFrom("ASSET",
floor.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(3, fromFloor.size());
// find relations from floor with type filter "Contains"
List<EntityRelation> containsFromFloor = client.findEntityRelationsByFromAndRelationType("ASSET",
floor.getId().getId().toString(), "Contains", RelationTypeGroup.COMMON.getValue());
assertEquals(2, containsFromFloor.size());
// find relations to device1
List<EntityRelation> toDevice1 = client.findEntityRelationsByTo("DEVICE",
device1.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(1, toDevice1.size());
assertEquals("Contains", toDevice1.get(0).getType());
// find relations to device3 with type filter "Manages"
List<EntityRelation> managesToDevice3 = client.findEntityRelationsByToAndRelationType("DEVICE",
device3.getId().getId().toString(), "Manages", RelationTypeGroup.COMMON.getValue());
assertEquals(1, managesToDevice3.size());
// find info by from (includes entity names)
List<EntityRelationInfo> infoFromFloor = client.findEntityRelationInfosByFrom("ASSET",
floor.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(3, infoFromFloor.size());
Device finalDevice = device1;
assertTrue(infoFromFloor.stream().anyMatch(info ->
finalDevice.getName().equals(info.getToName())));
// find info by to
List<EntityRelationInfo> infoToDevice2 = client.findEntityRelationInfosByTo("DEVICE",
device2.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(1, infoToDevice2.size());
assertEquals(floor.getName(), infoToDevice2.get(0).getFromName());
// find by query - search from building, direction FROM, max 2 levels
RelationsSearchParameters params = new RelationsSearchParameters();
params.setRootId(building.getId().getId());
params.setRootType(EntityType.ASSET);
params.setDirection(EntitySearchDirection.FROM);
params.setRelationTypeGroup(RelationTypeGroup.COMMON);
params.setMaxLevel(2);
RelationEntityTypeFilter filter = new RelationEntityTypeFilter();
filter.setRelationType("Contains");
filter.setEntityTypes(List.of(EntityType.ASSET, EntityType.DEVICE));
EntityRelationsQuery query = new EntityRelationsQuery();
query.setParameters(params);
query.setFilters(List.of(filter));
List<EntityRelation> queryResult = client.findEntityRelationsByQuery(query);
assertTrue(queryResult.size() >= 3);
// find info by query
List<EntityRelationInfo> infoQueryResult = client.findEntityRelationInfosByQuery(query);
assertTrue(infoQueryResult.size() >= 3);
// delete single relation
client.deleteRelation(
floor.getId().getId().toString(), "ASSET",
"Manages",
device3.getId().getId().toString(), "DEVICE",
RelationTypeGroup.COMMON.getValue());
// verify deletion
List<EntityRelation> afterDelete = client.findEntityRelationsByFrom("ASSET",
floor.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(2, afterDelete.size());
// delete all relations for building
client.deleteRelations(building.getId().getId().toString(), "ASSET");
List<EntityRelation> afterDeleteAll = client.findEntityRelationsByFrom("ASSET",
building.getId().getId().toString(), RelationTypeGroup.COMMON.getValue());
assertEquals(0, afterDeleteAll.size());
}
}

267
application/src/test/java/org/thingsboard/server/client/EntityViewJavaClientTest.java

@ -0,0 +1,267 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AttributesEntityView;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.EntitySubtype;
import org.thingsboard.client.model.EntityView;
import org.thingsboard.client.model.EntityViewInfo;
import org.thingsboard.client.model.PageDataEntityView;
import org.thingsboard.client.model.PageDataEntityViewInfo;
import org.thingsboard.client.model.TelemetryEntityView;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class EntityViewJavaClientTest extends AbstractJavaClientTest {
private static final String EV_PREFIX = "EvTest_";
@Test
public void testSaveAndGetEntityView() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView ev = new EntityView();
ev.setName(EV_PREFIX + "save_" + ts);
ev.setType("testType");
ev.setEntityId(device.getId());
ev.setKeys(new TelemetryEntityView()
.timeseries(List.of("temperature", "humidity"))
.attributes(new AttributesEntityView()
.cs(List.of("firmware"))
.ss(List.of("active"))
.sh(List.of())));
ev.setStartTimeMs(1000L);
ev.setEndTimeMs(2000L);
EntityView saved = client.saveEntityView(ev, null, null, null);
assertNotNull(saved);
assertNotNull(saved.getId());
assertEquals(ev.getName(), saved.getName());
assertEquals("testType", saved.getType());
assertEquals(device.getId().getId(), saved.getEntityId().getId());
assertEquals(List.of("temperature", "humidity"), saved.getKeys().getTimeseries());
assertEquals(1000L, saved.getStartTimeMs().longValue());
assertEquals(2000L, saved.getEndTimeMs().longValue());
// get by id
String evId = saved.getId().getId().toString();
EntityView fetched = client.getEntityViewById(evId);
assertNotNull(fetched);
assertEquals(saved.getName(), fetched.getName());
assertEquals(saved.getType(), fetched.getType());
assertEquals(saved.getEntityId().getId(), fetched.getEntityId().getId());
}
@Test
public void testGetEntityViewInfoById() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView saved = createEntityView(EV_PREFIX + "info_" + ts, "infoType", device);
EntityViewInfo info = client.getEntityViewInfoById(saved.getId().getId().toString());
assertNotNull(info);
assertEquals(saved.getName(), info.getName());
assertEquals("infoType", info.getType());
assertNotNull(info.getEntityId());
}
@Test
public void testUpdateEntityView() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView saved = createEntityView(EV_PREFIX + "update_" + ts, "default", device);
saved.setName(EV_PREFIX + "updated_" + ts);
saved.setKeys(new TelemetryEntityView()
.timeseries(List.of("temperature", "pressure"))
.attributes(new AttributesEntityView()
.cs(List.of())
.ss(List.of())
.sh(List.of())));
EntityView updated = client.saveEntityView(saved, null, null, null);
assertEquals(EV_PREFIX + "updated_" + ts, updated.getName());
assertEquals(List.of("temperature", "pressure"), updated.getKeys().getTimeseries());
assertEquals(saved.getId().getId(), updated.getId().getId());
}
@Test
public void testDeleteEntityView() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView saved = createEntityView(EV_PREFIX + "delete_" + ts, "default", device);
String evId = saved.getId().getId().toString();
client.getEntityViewById(evId);
client.deleteEntityView(evId);
assertReturns404(() -> client.getEntityViewById(evId));
}
@Test
public void testGetTenantEntityViews() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
for (int i = 0; i < 3; i++) {
createEntityView(EV_PREFIX + "tenant_" + ts + "_" + i, "tenantViewType", device);
}
PageDataEntityView page = client.getTenantEntityViews(100, 0, null, EV_PREFIX + "tenant_" + ts, null, null);
assertNotNull(page);
assertEquals(3, page.getTotalElements().intValue());
for (EntityView ev : page.getData()) {
assertTrue(ev.getName().startsWith(EV_PREFIX + "tenant_" + ts));
}
}
@Test
public void testGetTenantEntityViewInfos() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
createEntityView(EV_PREFIX + "tinfo_" + ts, "default", device);
PageDataEntityViewInfo page = client.getTenantEntityViewInfos(100, 0, null, EV_PREFIX + "tinfo_" + ts, null, null);
assertNotNull(page);
assertEquals(1, page.getTotalElements().intValue());
assertEquals(EV_PREFIX + "tinfo_" + ts, page.getData().get(0).getName());
}
@Test
public void testAssignAndUnassignEntityViewToCustomer() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView saved = createEntityView(EV_PREFIX + "assign_" + ts, "default", device);
String evId = saved.getId().getId().toString();
String customerId = savedClientCustomer.getId().getId().toString();
// assign to customer
EntityView assigned = client.assignEntityViewToCustomer(customerId, evId);
assertNotNull(assigned);
assertEquals(savedClientCustomer.getId().getId(), assigned.getCustomerId().getId());
// verify in customer entity views
PageDataEntityView customerViews = client.getCustomerEntityViews(
customerId, 100, 0, null, EV_PREFIX + "assign_" + ts, null, null);
assertEquals(1, customerViews.getTotalElements().intValue());
assertEquals(saved.getName(), customerViews.getData().get(0).getName());
// unassign from customer
EntityView unassigned = client.unassignEntityViewFromCustomer(evId);
assertNotNull(unassigned);
PageDataEntityView afterUnassign = client.getCustomerEntityViews(
customerId, 100, 0, null, EV_PREFIX + "assign_" + ts, null, null);
assertEquals(0, afterUnassign.getTotalElements().intValue());
}
@Test
public void testGetCustomerEntityViewInfos() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
EntityView saved = createEntityView(EV_PREFIX + "cinfo_" + ts, "default", device);
String evId = saved.getId().getId().toString();
String customerId = savedClientCustomer.getId().getId().toString();
client.assignEntityViewToCustomer(customerId, evId);
PageDataEntityViewInfo infos = client.getCustomerEntityViewInfos(
customerId, 100, 0, null, EV_PREFIX + "cinfo_" + ts, null, null);
assertNotNull(infos);
assertEquals(1, infos.getTotalElements().intValue());
assertEquals(saved.getName(), infos.getData().get(0).getName());
}
@Test
public void testGetEntityViewTypes() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
createEntityView(EV_PREFIX + "types_" + ts, "uniqueEvType_" + ts, device);
List<EntitySubtype> types = client.getEntityViewTypes();
assertNotNull(types);
assertFalse(types.isEmpty());
List<String> typeNames = types.stream()
.map(EntitySubtype::getType)
.collect(Collectors.toList());
assertTrue(typeNames.contains("uniqueEvType_" + ts));
}
@Test
public void testGetEntityViewById_notFound() {
String nonExistentId = UUID.randomUUID().toString();
assertReturns404(() -> client.getEntityViewById(nonExistentId));
}
@Test
public void testGetTenantEntityViewsPagination() throws Exception {
long ts = System.currentTimeMillis();
Device device = createTestDevice(String.valueOf(ts));
for (int i = 0; i < 5; i++) {
createEntityView(EV_PREFIX + "paged_" + ts + "_" + i, "default", device);
}
PageDataEntityView page1 = client.getTenantEntityViews(2, 0, null, EV_PREFIX + "paged_" + ts, null, null);
assertNotNull(page1);
assertEquals(5, page1.getTotalElements().intValue());
assertEquals(3, page1.getTotalPages().intValue());
assertEquals(2, page1.getData().size());
assertTrue(page1.getHasNext());
PageDataEntityView lastPage = client.getTenantEntityViews(2, 2, null, EV_PREFIX + "paged_" + ts, null, null);
assertEquals(1, lastPage.getData().size());
assertFalse(lastPage.getHasNext());
}
private Device createTestDevice(String suffix) throws Exception {
Device device = new Device();
device.setName(EV_PREFIX + "device_" + suffix);
device.setType("default");
return client.saveDevice(device, null, null, null, null);
}
private EntityView createEntityView(String name, String type, Device device) throws Exception {
EntityView ev = new EntityView();
ev.setName(name);
ev.setType(type);
ev.setEntityId(device.getId());
ev.setKeys(new TelemetryEntityView()
.timeseries(List.of("temperature"))
.attributes(new AttributesEntityView()
.cs(List.of())
.ss(List.of())
.sh(List.of())));
return client.saveEntityView(ev, null, null, null);
}
}

156
application/src/test/java/org/thingsboard/server/client/MobileAppJavaClientTest.java

@ -0,0 +1,156 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.MobileApp;
import org.thingsboard.client.model.MobileAppBundle;
import org.thingsboard.client.model.MobileAppBundleInfo;
import org.thingsboard.client.model.MobileAppStatus;
import org.thingsboard.client.model.PageDataMobileApp;
import org.thingsboard.client.model.PageDataMobileAppBundleInfo;
import org.thingsboard.client.model.PlatformType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class MobileAppJavaClientTest extends AbstractJavaClientTest {
@Test
public void testMobileAppLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<MobileApp> createdApps = new ArrayList<>();
// create 3 Android apps
for (int i = 0; i < 3; i++) {
MobileApp app = new MobileApp();
app.setPkgName("com.test.android." + timestamp + "." + i);
app.setTitle(TEST_PREFIX + "AndroidApp_" + timestamp + "_" + i);
app.setAppSecret("secret_android_" + timestamp + "_" + i);
app.setPlatformType(PlatformType.ANDROID);
app.setStatus(MobileAppStatus.DRAFT);
MobileApp created = client.saveMobileApp(app);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(app.getPkgName(), created.getPkgName());
assertEquals(PlatformType.ANDROID, created.getPlatformType());
assertEquals(MobileAppStatus.DRAFT, created.getStatus());
createdApps.add(created);
}
// create 2 iOS apps
for (int i = 0; i < 2; i++) {
MobileApp app = new MobileApp();
app.setPkgName("com.test.ios." + timestamp + "." + i);
app.setTitle(TEST_PREFIX + "IosApp_" + timestamp + "_" + i);
app.setAppSecret("secret_ios_" + timestamp + "_" + i);
app.setPlatformType(PlatformType.IOS);
app.setStatus(MobileAppStatus.DRAFT);
MobileApp created = client.saveMobileApp(app);
assertNotNull(created);
createdApps.add(created);
}
// list all tenant mobile apps
PageDataMobileApp allApps = client.getTenantMobileApps(100, 0, null,
null, null, null);
assertNotNull(allApps);
assertEquals(5, allApps.getData().size());
// list with platform type filter
PageDataMobileApp androidApps = client.getTenantMobileApps(100, 0, PlatformType.ANDROID,
null, null, null);
assertEquals(3, androidApps.getData().size());
PageDataMobileApp iosApps = client.getTenantMobileApps(100, 0, PlatformType.IOS,
null, null, null);
assertEquals(2, iosApps.getData().size());
// get mobile app by id
MobileApp searchApp = createdApps.get(1);
MobileApp fetchedApp = client.getMobileAppById(searchApp.getId().getId());
assertEquals(searchApp.getPkgName(), fetchedApp.getPkgName());
assertEquals(searchApp.getTitle(), fetchedApp.getTitle());
assertEquals(searchApp.getPlatformType(), fetchedApp.getPlatformType());
// update mobile app
MobileApp appToUpdate = createdApps.get(2);
appToUpdate.setTitle(appToUpdate.getTitle() + "_updated");
MobileApp updatedApp = client.saveMobileApp(appToUpdate);
assertEquals(appToUpdate.getTitle(), updatedApp.getTitle());
// create mobile app bundle with android and ios apps
MobileAppBundle bundle = new MobileAppBundle();
bundle.setTitle(TEST_PREFIX + "Bundle_" + timestamp);
bundle.setDescription("Test bundle");
bundle.setAndroidAppId(createdApps.get(0).getId());
bundle.setIosAppId(createdApps.get(3).getId());
bundle.setOauth2Enabled(false);
MobileAppBundle savedBundle = client.saveMobileAppBundle(bundle, null);
assertNotNull(savedBundle);
assertNotNull(savedBundle.getId());
assertEquals(bundle.getTitle(), savedBundle.getTitle());
// get bundle info by id
MobileAppBundleInfo bundleInfo = client.getMobileAppBundleInfoById(savedBundle.getId().getId());
assertEquals(savedBundle.getTitle(), bundleInfo.getTitle());
assertEquals("Test bundle", bundleInfo.getDescription());
assertNotNull(bundleInfo.getAndroidPkgName());
assertNotNull(bundleInfo.getIosPkgName());
// list tenant bundles
PageDataMobileAppBundleInfo bundles = client.getTenantMobileAppBundleInfos(100, 0,
TEST_PREFIX + "Bundle_" + timestamp, null, null);
assertEquals(1, bundles.getData().size());
// update bundle
savedBundle.setDescription("Updated description");
MobileAppBundle updatedBundle = client.saveMobileAppBundle(savedBundle, null);
assertEquals("Updated description", updatedBundle.getDescription());
// delete bundle
client.deleteMobileAppBundle(savedBundle.getId().getId());
// verify bundle deletion
assertReturns404(() ->
client.getMobileAppBundleInfoById(savedBundle.getId().getId())
);
// delete mobile app
UUID appToDeleteId = createdApps.get(0).getId().getId();
client.deleteMobileApp(appToDeleteId);
// verify app deletion
assertReturns404(() ->
client.getMobileAppById(appToDeleteId)
);
PageDataMobileApp appsAfterDelete = client.getTenantMobileApps(100, 0, null,
null, null, null);
assertEquals(4, appsAfterDelete.getData().size());
}
}

278
application/src/test/java/org/thingsboard/server/client/NotificationJavaClientTest.java

@ -0,0 +1,278 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.EntityActionNotificationRuleTriggerConfig;
import org.thingsboard.client.model.EntityActionRecipientsConfig;
import org.thingsboard.client.model.EntityType;
import org.thingsboard.client.model.NotificationDeliveryMethod;
import org.thingsboard.client.model.NotificationRequest;
import org.thingsboard.client.model.NotificationRequestInfo;
import org.thingsboard.client.model.NotificationRule;
import org.thingsboard.client.model.NotificationRuleInfo;
import org.thingsboard.client.model.NotificationRuleTriggerType;
import org.thingsboard.client.model.NotificationSettings;
import org.thingsboard.client.model.NotificationTarget;
import org.thingsboard.client.model.NotificationTemplate;
import org.thingsboard.client.model.NotificationTemplateConfig;
import org.thingsboard.client.model.NotificationType;
import org.thingsboard.client.model.PageDataNotification;
import org.thingsboard.client.model.PageDataNotificationRequestInfo;
import org.thingsboard.client.model.PageDataNotificationRuleInfo;
import org.thingsboard.client.model.PageDataNotificationTarget;
import org.thingsboard.client.model.PageDataNotificationTemplate;
import org.thingsboard.client.model.PlatformUsersNotificationTargetConfig;
import org.thingsboard.client.model.TenantAdministratorsFilter;
import org.thingsboard.client.model.WebDeliveryMethodNotificationTemplate;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class NotificationJavaClientTest extends AbstractJavaClientTest {
@Test
public void testNotificationLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// === 1. Notification Target CRUD ===
// Create target
TenantAdministratorsFilter usersFilter = new TenantAdministratorsFilter();
PlatformUsersNotificationTargetConfig targetConfig =
new PlatformUsersNotificationTargetConfig().usersFilter(usersFilter);
NotificationTarget target =
new NotificationTarget()
.name("Test Target " + timestamp)
._configuration(targetConfig);
NotificationTarget savedTarget = client.saveNotificationTarget(target);
assertNotNull(savedTarget);
assertNotNull(savedTarget.getId());
assertEquals("Test Target " + timestamp, savedTarget.getName());
// Get target by ID
NotificationTarget fetchedTarget =
client.getNotificationTargetById(savedTarget.getId().getId());
assertEquals(savedTarget.getName(), fetchedTarget.getName());
// List targets
PageDataNotificationTarget targetsPage =
client.getNotificationTargets(100, 0, null, null, null);
assertNotNull(targetsPage);
assertNotNull(targetsPage.getData());
assertTrue(
targetsPage.getData().stream()
.anyMatch(t -> t.getName().equals(savedTarget.getName())));
// Update target
savedTarget.setName("Updated Target " + timestamp);
NotificationTarget updatedTarget = client.saveNotificationTarget(savedTarget);
assertEquals("Updated Target " + timestamp, updatedTarget.getName());
// === 2. Notification Template CRUD ===
// Create template
WebDeliveryMethodNotificationTemplate webTemplate =
new WebDeliveryMethodNotificationTemplate()
.subject("Test Subject")
.body("Test notification body")
.enabled(true);
NotificationTemplateConfig templateConfig =
new NotificationTemplateConfig()
.putDeliveryMethodsTemplatesItem("WEB", webTemplate);
NotificationTemplate template =
new NotificationTemplate()
.name("Test Template " + timestamp)
.notificationType(NotificationType.GENERAL)
._configuration(templateConfig);
NotificationTemplate savedTemplate = client.saveNotificationTemplate(template);
assertNotNull(savedTemplate);
assertNotNull(savedTemplate.getId());
assertEquals("Test Template " + timestamp, savedTemplate.getName());
// Get template by ID
NotificationTemplate fetchedTemplate =
client.getNotificationTemplateById(savedTemplate.getId().getId());
assertEquals(savedTemplate.getName(), fetchedTemplate.getName());
assertEquals(NotificationType.GENERAL, fetchedTemplate.getNotificationType());
// List templates
PageDataNotificationTemplate templatesPage =
client.getNotificationTemplates(100, 0, null, null, null, null);
assertNotNull(templatesPage);
assertTrue(
templatesPage.getData().stream()
.anyMatch(t -> t.getName().equals(savedTemplate.getName())));
// Update template
savedTemplate.setName("Updated Template " + timestamp);
NotificationTemplate updatedTemplate = client.saveNotificationTemplate(savedTemplate);
assertEquals("Updated Template " + timestamp, updatedTemplate.getName());
// === 3. Send notification & read notifications ===
// Send notification request
NotificationRequest request =
new NotificationRequest()
.targets(List.of(savedTarget.getId().getId()))
.templateId(savedTemplate.getId());
NotificationRequest sentRequest = client.createNotificationRequest(request);
assertNotNull(sentRequest);
assertNotNull(sentRequest.getId());
// Get request by ID
NotificationRequestInfo fetchedRequest =
client.getNotificationRequestById(sentRequest.getId().getId());
assertNotNull(fetchedRequest);
// List requests
PageDataNotificationRequestInfo requestsPage =
client.getNotificationRequests(100, 0, null, null, null);
assertNotNull(requestsPage);
assertFalse(requestsPage.getData().isEmpty());
// Get notifications for current user
PageDataNotification notificationsPage =
client.getNotifications(100, 0, null, null, null, null, null);
assertNotNull(notificationsPage);
assertFalse(notificationsPage.getData().isEmpty());
// Get unread count
Integer unreadCount = client.getUnreadNotificationsCount("WEB");
assertNotNull(unreadCount);
assertTrue("Expected at least one unread notification", unreadCount > 0);
// Mark single notification as read
client.markNotificationAsRead(
notificationsPage.getData().get(0).getId().getId());
// Mark all as read
client.markAllNotificationsAsRead(null);
Integer unreadAfterMarkAll = client.getUnreadNotificationsCount(null);
assertEquals("Expected no unread notifications after marking all as read", 0, unreadAfterMarkAll.intValue());
// === 4. Notification Settings ===
NotificationSettings settings = client.getNotificationSettings();
assertNotNull(settings);
List<NotificationDeliveryMethod> deliveryMethods = client.getAvailableDeliveryMethods();
assertNotNull(deliveryMethods);
assertTrue(deliveryMethods.contains(NotificationDeliveryMethod.WEB));
// === 5. Cleanup ===
// Delete notification request
client.deleteNotificationRequest(sentRequest.getId().getId());
assertReturns404(() -> client.getNotificationRequestById(sentRequest.getId().getId()));
// Delete template
client.deleteNotificationTemplateById(savedTemplate.getId().getId());
assertReturns404(() -> client.getNotificationTemplateById(savedTemplate.getId().getId()));
// Delete target
client.deleteNotificationTargetById(savedTarget.getId().getId());
assertReturns404(() -> client.getNotificationTargetById(savedTarget.getId().getId()));
}
@Test
public void testNotificationRuleLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// Create a target for the rule recipients
TenantAdministratorsFilter usersFilter = new TenantAdministratorsFilter();
PlatformUsersNotificationTargetConfig targetConfig =
new PlatformUsersNotificationTargetConfig().usersFilter(usersFilter);
NotificationTarget target =
new NotificationTarget()
.name("Rule Test Target " + timestamp)
._configuration(targetConfig);
NotificationTarget savedTarget = client.saveNotificationTarget(target);
// Create a template of type ENTITY_ACTION
WebDeliveryMethodNotificationTemplate webTemplate =
new WebDeliveryMethodNotificationTemplate()
.subject("Entity action: ${entityType}")
.body("Entity ${entityName} was ${actionType}")
.enabled(true);
NotificationTemplateConfig templateConfig =
new NotificationTemplateConfig()
.putDeliveryMethodsTemplatesItem("WEB", webTemplate);
NotificationTemplate template =
new NotificationTemplate()
.name("Rule Test Template " + timestamp)
.notificationType(NotificationType.ENTITY_ACTION)
._configuration(templateConfig);
NotificationTemplate savedTemplate = client.saveNotificationTemplate(template);
// Build trigger config: fire on DEVICE create/update
EntityActionNotificationRuleTriggerConfig triggerConfig =
new EntityActionNotificationRuleTriggerConfig()
.addEntityTypesItem(EntityType.DEVICE)
.created(true)
.updated(true)
.deleted(false);
// Build recipients config
EntityActionRecipientsConfig recipientsConfig = new EntityActionRecipientsConfig()
.addTargetsItem(savedTarget.getId().getId());
// saveNotificationRule - create
NotificationRule rule = new NotificationRule()
.name("Test Rule " + timestamp)
.enabled(true)
.templateId(savedTemplate.getId())
.triggerType(NotificationRuleTriggerType.ENTITY_ACTION)
.triggerConfig(triggerConfig)
.recipientsConfig(recipientsConfig);
NotificationRule savedRule = client.saveNotificationRule(rule);
assertNotNull(savedRule);
assertNotNull(savedRule.getId());
assertEquals("Test Rule " + timestamp, savedRule.getName());
assertEquals(NotificationRuleTriggerType.ENTITY_ACTION, savedRule.getTriggerType());
assertEquals(Boolean.TRUE, savedRule.getEnabled());
// getNotificationRuleById
NotificationRuleInfo fetchedRule = client.getNotificationRuleById(savedRule.getId().getId());
assertNotNull(fetchedRule);
assertEquals(savedRule.getName(), fetchedRule.getName());
assertEquals(NotificationRuleTriggerType.ENTITY_ACTION, fetchedRule.getTriggerType());
// getNotificationRules - verify it appears in the list
PageDataNotificationRuleInfo rulesPage = client.getNotificationRules(100, 0, null, null, null);
assertNotNull(rulesPage);
assertTrue(rulesPage.getData().stream()
.anyMatch(r -> r.getId().getId().equals(savedRule.getId().getId())));
// deleteNotificationRule
client.deleteNotificationRule(savedRule.getId().getId());
assertReturns404(() -> client.getNotificationRuleById(savedRule.getId().getId()));
// Cleanup
client.deleteNotificationTemplateById(savedTemplate.getId().getId());
client.deleteNotificationTargetById(savedTarget.getId().getId());
}
}

138
application/src/test/java/org/thingsboard/server/client/Oauth2JavaClientTest.java

@ -0,0 +1,138 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.MapperType;
import org.thingsboard.client.model.OAuth2BasicMapperConfig;
import org.thingsboard.client.model.OAuth2Client;
import org.thingsboard.client.model.OAuth2ClientInfo;
import org.thingsboard.client.model.OAuth2MapperConfig;
import org.thingsboard.client.model.PageDataOAuth2ClientInfo;
import org.thingsboard.client.model.PlatformType;
import org.thingsboard.client.model.TenantNameStrategyType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class Oauth2JavaClientTest extends AbstractJavaClientTest {
private OAuth2Client createOAuth2Client(String title, String clientId, String clientSecret) {
OAuth2BasicMapperConfig basicConfig = new OAuth2BasicMapperConfig();
basicConfig.setEmailAttributeKey("email");
basicConfig.setFirstNameAttributeKey("given_name");
basicConfig.setLastNameAttributeKey("family_name");
basicConfig.setTenantNameStrategy(TenantNameStrategyType.DOMAIN);
OAuth2MapperConfig mapperConfig = new OAuth2MapperConfig();
mapperConfig.setType(MapperType.BASIC);
mapperConfig.setAllowUserCreation(true);
mapperConfig.setActivateUser(false);
mapperConfig.setBasic(basicConfig);
OAuth2Client oAuth2Client = new OAuth2Client();
oAuth2Client.setTitle(title);
oAuth2Client.setClientId(clientId);
oAuth2Client.setClientSecret(clientSecret);
oAuth2Client.setAuthorizationUri("https://accounts.google.com/o/oauth2/v2/auth");
oAuth2Client.setAccessTokenUri("https://oauth2.googleapis.com/token");
oAuth2Client.setScope(List.of("openid", "email", "profile"));
oAuth2Client.setUserInfoUri("https://openidconnect.googleapis.com/v1/userinfo");
oAuth2Client.setUserNameAttributeName("email");
oAuth2Client.setClientAuthenticationMethod("POST");
oAuth2Client.setLoginButtonLabel(title);
oAuth2Client.setMapperConfig(mapperConfig);
oAuth2Client.setPlatforms(List.of(PlatformType.WEB));
return oAuth2Client;
}
@Test
public void testOAuth2ClientLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<OAuth2Client> createdClients = new ArrayList<>();
// create 5 OAuth2 clients
for (int i = 0; i < 5; i++) {
String title = TEST_PREFIX + "OAuth2_" + timestamp + "_" + i;
OAuth2Client oAuth2Client = createOAuth2Client(title,
"client_id_" + timestamp + "_" + i,
"client_secret_" + timestamp + "_" + i);
OAuth2Client created = client.saveOAuth2Client(oAuth2Client);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(title, created.getTitle());
assertEquals("POST", created.getClientAuthenticationMethod());
assertNotNull(created.getMapperConfig());
assertEquals(MapperType.BASIC, created.getMapperConfig().getType());
createdClients.add(created);
}
// list tenant OAuth2 client infos
PageDataOAuth2ClientInfo clientInfos = client.findTenantOAuth2ClientInfos(100, 0,
TEST_PREFIX + "OAuth2_" + timestamp, null, null);
assertNotNull(clientInfos);
assertEquals(5, clientInfos.getData().size());
// get OAuth2 client by id
OAuth2Client searchClient = createdClients.get(2);
OAuth2Client fetchedClient = client.getOAuth2ClientById(searchClient.getId().getId());
assertEquals(searchClient.getTitle(), fetchedClient.getTitle());
assertEquals(searchClient.getClientId(), fetchedClient.getClientId());
assertEquals(searchClient.getAuthorizationUri(), fetchedClient.getAuthorizationUri());
assertEquals(3, fetchedClient.getScope().size());
// fetch client infos by ids
List<String> idsToFetch = List.of(
createdClients.get(0).getId().getId().toString(),
createdClients.get(1).getId().getId().toString()
);
List<OAuth2ClientInfo> fetchedInfos = client.findTenantOAuth2ClientInfosByIds(idsToFetch);
assertEquals(2, fetchedInfos.size());
// update OAuth2 client
OAuth2Client clientToUpdate = client.getOAuth2ClientById(createdClients.get(3).getId().getId());
clientToUpdate.setTitle(clientToUpdate.getTitle() + "_updated");
clientToUpdate.setLoginButtonLabel("Updated Login");
clientToUpdate.setPlatforms(List.of(PlatformType.WEB, PlatformType.ANDROID));
OAuth2Client updatedClient = client.saveOAuth2Client(clientToUpdate);
assertEquals(clientToUpdate.getTitle(), updatedClient.getTitle());
assertEquals("Updated Login", updatedClient.getLoginButtonLabel());
assertEquals(2, updatedClient.getPlatforms().size());
// delete OAuth2 client
UUID clientToDeleteId = createdClients.get(0).getId().getId();
client.deleteOauth2Client(clientToDeleteId);
// verify deletion
assertReturns404(() ->
client.getOAuth2ClientById(clientToDeleteId)
);
PageDataOAuth2ClientInfo clientsAfterDelete = client.findTenantOAuth2ClientInfos(100, 0,
TEST_PREFIX + "OAuth2_" + timestamp, null, null);
assertEquals(4, clientsAfterDelete.getData().size());
}
}

261
application/src/test/java/org/thingsboard/server/client/OtaPackageJavaClientTest.java

@ -0,0 +1,261 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.ChecksumAlgorithm;
import org.thingsboard.client.model.DeviceProfileId;
import org.thingsboard.client.model.DeviceProfileInfo;
import org.thingsboard.client.model.OtaPackage;
import org.thingsboard.client.model.OtaPackageInfo;
import org.thingsboard.client.model.OtaPackageType;
import org.thingsboard.client.model.PageDataOtaPackageInfo;
import org.thingsboard.client.model.SaveOtaPackageInfoRequest;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.io.File;
import java.io.FileWriter;
import java.nio.file.Files;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class OtaPackageJavaClientTest extends AbstractJavaClientTest {
private static final String OTA_PREFIX = "OtaTest_";
private DeviceProfileId getDefaultDeviceProfileId() throws Exception {
DeviceProfileInfo profileInfo = client.getDefaultDeviceProfileInfo();
return (DeviceProfileId) profileInfo.getId();
}
private SaveOtaPackageInfoRequest buildOtaPackageInfoRequest(
String title, String version, OtaPackageType type,
DeviceProfileId deviceProfileId, boolean usesUrl, String url) {
SaveOtaPackageInfoRequest request = new SaveOtaPackageInfoRequest();
request.setTitle(title);
request.setType(type);
request.setUrl(url);
request.setVersion(version);
request.setDeviceProfileId(deviceProfileId);
return request;
}
private OtaPackageInfo createFirmwareInfo(String suffix) throws Exception {
DeviceProfileId profileId = getDefaultDeviceProfileId();
SaveOtaPackageInfoRequest request = buildOtaPackageInfoRequest(
OTA_PREFIX + suffix, "1.0." + System.currentTimeMillis(),
OtaPackageType.FIRMWARE, profileId, false, null);
return client.saveOtaPackageInfo(request);
}
private OtaPackageInfo createFirmwareWithUrl(String suffix) throws Exception {
DeviceProfileId profileId = getDefaultDeviceProfileId();
SaveOtaPackageInfoRequest request = buildOtaPackageInfoRequest(
OTA_PREFIX + suffix, "1.0." + System.currentTimeMillis(),
OtaPackageType.FIRMWARE, profileId, true, "https://example.com/firmware.bin");
return client.saveOtaPackageInfo(request);
}
@Test
public void testSaveAndGetOtaPackageInfo() throws Exception {
long ts = System.currentTimeMillis();
DeviceProfileId profileId = getDefaultDeviceProfileId();
String title = OTA_PREFIX + "save_" + ts;
String version = "1.0." + ts;
SaveOtaPackageInfoRequest request = buildOtaPackageInfoRequest(
title, version, OtaPackageType.FIRMWARE, profileId, true, "https://example.com/fw.bin");
OtaPackageInfo saved = client.saveOtaPackageInfo(request);
assertNotNull(saved);
assertNotNull(saved.getId());
assertEquals(title, saved.getTitle());
assertEquals(version, saved.getVersion());
assertEquals(OtaPackageType.FIRMWARE, saved.getType());
assertTrue(saved.getUrl().contains("example.com"));
// get info by id
String pkgId = saved.getId().getId().toString();
OtaPackageInfo fetched = client.getOtaPackageInfoById(pkgId);
assertNotNull(fetched);
assertEquals(title, fetched.getTitle());
assertEquals(version, fetched.getVersion());
}
@Test
public void testGetOtaPackageById() throws Exception {
long ts = System.currentTimeMillis();
OtaPackageInfo saved = createFirmwareWithUrl("getbyid_" + ts);
OtaPackage fullPkg = client.getOtaPackageById(saved.getId().getId().toString());
assertNotNull(fullPkg);
assertEquals(saved.getTitle(), fullPkg.getTitle());
assertEquals(saved.getVersion(), fullPkg.getVersion());
}
@Test
public void testSaveOtaPackageInfoForSoftware() throws Exception {
long ts = System.currentTimeMillis();
DeviceProfileId profileId = getDefaultDeviceProfileId();
String title = OTA_PREFIX + "sw_" + ts;
SaveOtaPackageInfoRequest request = buildOtaPackageInfoRequest(
title, "2.0." + ts, OtaPackageType.SOFTWARE, profileId, true, "https://example.com/sw.bin");
OtaPackageInfo saved = client.saveOtaPackageInfo(request);
assertNotNull(saved);
assertEquals(OtaPackageType.SOFTWARE, saved.getType());
assertEquals(title, saved.getTitle());
}
@Test
public void testSaveOtaPackageData() throws Exception {
long ts = System.currentTimeMillis();
OtaPackageInfo info = createFirmwareInfo("data_" + ts);
File tempFile = Files.createTempFile("ota_test_", ".bin").toFile();
tempFile.deleteOnExit();
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write("test firmware content " + ts);
}
OtaPackageInfo updated = client.saveOtaPackageData(
info.getId().getId().toString(), "MD5", tempFile, null);
assertNotNull(updated);
assertTrue(updated.getHasData());
assertNotNull(updated.getFileName());
assertNotNull(updated.getDataSize());
assertTrue(updated.getDataSize() > 0);
assertEquals(ChecksumAlgorithm.MD5, updated.getChecksumAlgorithm());
}
@Test
public void testDownloadOtaPackage() throws Exception {
long ts = System.currentTimeMillis();
OtaPackageInfo info = createFirmwareInfo("download_" + ts);
String content = "downloadable firmware " + ts;
File tempFile = Files.createTempFile("ota_dl_", ".bin").toFile();
tempFile.deleteOnExit();
try (FileWriter writer = new FileWriter(tempFile)) {
writer.write(content);
}
client.saveOtaPackageData(info.getId().getId().toString(), "MD5", tempFile, null);
File downloaded = client.downloadOtaPackage(info.getId().getId().toString());
assertNotNull(downloaded);
assertTrue(downloaded.length() > 0);
String downloadedContent = Files.readString(downloaded.toPath());
assertEquals(content, downloadedContent);
}
@Test
public void testDeleteOtaPackage() throws Exception {
long ts = System.currentTimeMillis();
OtaPackageInfo saved = createFirmwareWithUrl("delete_" + ts);
String pkgId = saved.getId().getId().toString();
client.getOtaPackageInfoById(pkgId);
client.deleteOtaPackage(pkgId);
assertReturns404(() -> client.getOtaPackageInfoById(pkgId));
}
@Test
public void testGetOtaPackages() throws Exception {
long ts = System.currentTimeMillis();
for (int i = 0; i < 3; i++) {
createFirmwareWithUrl("list_" + ts + "_" + i);
}
PageDataOtaPackageInfo page = client.getOtaPackages(100, 0, OTA_PREFIX + "list_" + ts, null, null);
assertNotNull(page);
assertEquals(3, page.getTotalElements().intValue());
for (OtaPackageInfo pkg : page.getData()) {
assertTrue(pkg.getTitle().startsWith(OTA_PREFIX + "list_" + ts));
}
}
@Test
public void testGetOtaPackagesByDeviceProfileAndType() throws Exception {
long ts = System.currentTimeMillis();
DeviceProfileId profileId = getDefaultDeviceProfileId();
createFirmwareWithUrl("byprofile_" + ts + "_0");
createFirmwareWithUrl("byprofile_" + ts + "_1");
PageDataOtaPackageInfo page = client.getOtaPackagesByDeviceProfileAndType(
profileId.getId().toString(), "FIRMWARE", 100, 0,
OTA_PREFIX + "byprofile_" + ts, null, null);
assertNotNull(page);
assertEquals(2, page.getTotalElements().intValue());
}
@Test
public void testGetOtaPackageInfoById_notFound() {
String nonExistentId = UUID.randomUUID().toString();
assertReturns404(() -> client.getOtaPackageInfoById(nonExistentId));
}
@Test
public void testGetOtaPackagesPagination() throws Exception {
long ts = System.currentTimeMillis();
for (int i = 0; i < 5; i++) {
createFirmwareWithUrl("paged_" + ts + "_" + i);
}
PageDataOtaPackageInfo page1 = client.getOtaPackages(2, 0, OTA_PREFIX + "paged_" + ts, null, null);
assertNotNull(page1);
assertEquals(5, page1.getTotalElements().intValue());
assertEquals(3, page1.getTotalPages().intValue());
assertEquals(2, page1.getData().size());
assertTrue(page1.getHasNext());
PageDataOtaPackageInfo lastPage = client.getOtaPackages(2, 2, OTA_PREFIX + "paged_" + ts, null, null);
assertEquals(1, lastPage.getData().size());
assertFalse(lastPage.getHasNext());
}
@Test
public void testUpdateOtaPackageInfo() throws Exception {
long ts = System.currentTimeMillis();
OtaPackageInfo saved = createFirmwareWithUrl("update_" + ts);
SaveOtaPackageInfoRequest updateReq = new SaveOtaPackageInfoRequest();
updateReq.setId(saved.getId());
updateReq.setTitle(saved.getTitle());
updateReq.setType(saved.getType());
updateReq.setVersion(saved.getVersion());
updateReq.setDeviceProfileId(saved.getDeviceProfileId());
updateReq.setUrl(saved.getUrl());
updateReq.setAdditionalInfo(OBJECT_MAPPER.createObjectNode().put("infoKey", "infoValue"));
OtaPackageInfo updated = client.saveOtaPackageInfo(updateReq);
assertNotNull(updated);
assertEquals(saved.getId().getId(), updated.getId().getId());
assertEquals("infoValue", updated.getAdditionalInfo().get("infoKey").asText());
}
}

72
application/src/test/java/org/thingsboard/server/client/RpcV1JavaClientTest.java

@ -0,0 +1,72 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.model.Device;
import org.thingsboard.server.dao.service.DaoSqlTest;
import static org.junit.Assert.assertEquals;
@DaoSqlTest
public class RpcV1JavaClientTest extends AbstractJavaClientTest {
private static final String ONE_WAY_BODY =
"{\"method\":\"setGpio\",\"params\":{\"pin\":7,\"value\":1},\"persistent\":true}";
private static final String TWO_WAY_BODY =
"{\"method\":\"getGpio\",\"params\":{\"pin\":7},\"persistent\":true}";
@Test
public void testHandleOneWayDeviceRPCRequest() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
try {
client.handleOneWayDeviceRPCRequestV1(deviceId, ONE_WAY_BODY);
} catch (ApiException e) {
assertEquals("handleOneWayDeviceRPCRequest got an unexpected HTTP error: " + e.getCode(),
0, e.getCode());
}
client.deleteDevice(deviceId);
}
@Test
public void testHandleTwoWayDeviceRPCRequest() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
try {
client.handleTwoWayDeviceRPCRequestV1(deviceId, TWO_WAY_BODY);
} catch (ApiException e) {
assertEquals("handleTwoWayDeviceRPCRequest got an unexpected HTTP error: " + e.getCode(),
0, e.getCode());
}
client.deleteDevice(deviceId);
}
private Device createNewDevice(String name) throws ApiException {
Device device = new Device();
device.setName(name);
device.setType("default");
return client.saveDevice(device, null, null, null, null);
}
}

133
application/src/test/java/org/thingsboard/server/client/RpcV2JavaClientTest.java

@ -0,0 +1,133 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.Rpc;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class RpcV2JavaClientTest extends AbstractJavaClientTest {
private static final String PERSISTENT_BODY =
"{\"method\":\"setGpio\",\"params\":{\"pin\":7,\"value\":1},\"persistent\":true}";
@Test
public void testHandleOneWayDeviceRPCRequest() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
try {
client.handleOneWayDeviceRPCRequestV2(deviceId, PERSISTENT_BODY);
} catch (ApiException e) {
assertEquals("handleOneWayDeviceRPCRequest1 got an unexpected HTTP error: " + e.getCode(),
0, e.getCode());
}
client.deleteDevice(deviceId);
}
@Test
public void testHandleTwoWayDeviceRPCRequest() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
try {
client.handleTwoWayDeviceRPCRequestV2(deviceId, PERSISTENT_BODY);
} catch (ApiException e) {
assertEquals("handleTwoWayDeviceRPCRequest1 got an unexpected HTTP error: " + e.getCode(),
0, e.getCode());
}
client.deleteDevice(deviceId);
}
@Test
public void testGetPersistedRpcAndDeleteRpc() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
String rpcId = postPersistentRpcAndGetId(deviceId);
assertNotNull(rpcId);
Rpc rpc = client.getPersistedRpc(rpcId);
assertNotNull(rpc);
assertNotNull(rpc.getId());
client.deleteRpc(rpcId);
assertReturns404(() -> client.getPersistedRpc(rpcId));
client.deleteDevice(deviceId);
}
@Test
public void testGetPersistedRpcNotFound() {
assertReturns404(() -> client.getPersistedRpc(UUID.randomUUID().toString()));
}
@Test
public void testGetPersistedRpcByDevice() throws Exception {
long ts = System.currentTimeMillis();
Device device = createNewDevice(TEST_PREFIX + ts);
String deviceId = device.getId().getId().toString();
postPersistentRpcAndGetId(deviceId);
try {
client.getPersistedRpcByDevice(deviceId, 100, 0, null, null, null, null);
} catch (ApiException e) {
assertEquals("getPersistedRpcByDevice got an unexpected HTTP error: " + e.getCode(),
0, e.getCode());
}
client.deleteDevice(deviceId);
}
private Device createNewDevice(String name) throws ApiException {
Device device = new Device();
device.setName(name);
device.setType("default");
return client.saveDevice(device, null, null, null, null);
}
private String postPersistentRpcAndGetId(String deviceId) throws IOException, InterruptedException {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(getBaseUrl() + "/api/plugins/rpc/oneway/" + deviceId))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + client.getToken())
.POST(HttpRequest.BodyPublishers.ofString(PERSISTENT_BODY))
.build();
HttpResponse<String> response = HttpClient.newHttpClient().send(request, HttpResponse.BodyHandlers.ofString());
return OBJECT_MAPPER.readTree(response.body()).get("rpcId").asText();
}
}

163
application/src/test/java/org/thingsboard/server/client/RuleChainJavaClientTest.java

@ -0,0 +1,163 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.NodeConnectionInfo;
import org.thingsboard.client.model.PageDataRuleChain;
import org.thingsboard.client.model.RuleChain;
import org.thingsboard.client.model.RuleChainMetaData;
import org.thingsboard.client.model.RuleChainType;
import org.thingsboard.client.model.RuleNode;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class RuleChainJavaClientTest extends AbstractJavaClientTest {
@Test
public void testRuleChainAndNodeLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<RuleChain> createdChains = new ArrayList<>();
// create 5 rule chains
for (int i = 0; i < 5; i++) {
RuleChain ruleChain = new RuleChain();
ruleChain.setName(TEST_PREFIX + "RuleChain_" + timestamp + "_" + i);
ruleChain.setType(RuleChainType.CORE);
ruleChain.setDebugMode(false);
RuleChain created = client.saveRuleChain(ruleChain);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(ruleChain.getName(), created.getName());
assertEquals(RuleChainType.CORE, created.getType());
createdChains.add(created);
}
// list rule chains with text search
PageDataRuleChain filteredChains = client.getRuleChains(100, 0, null,
TEST_PREFIX + "RuleChain_" + timestamp, null, null);
assertNotNull(filteredChains);
assertEquals(5, filteredChains.getData().size());
// get rule chain by id
RuleChain searchChain = createdChains.get(2);
RuleChain fetchedChain = client.getRuleChainById(searchChain.getId().getId().toString());
assertEquals(searchChain.getName(), fetchedChain.getName());
assertEquals(searchChain.getType(), fetchedChain.getType());
// get metadata (initially has default node)
RuleChainMetaData metadata = client.getRuleChainMetaData(searchChain.getId().getId().toString());
assertNotNull(metadata);
assertEquals(searchChain.getId().getId(), metadata.getRuleChainId().getId());
// save metadata with rule nodes and connections
RuleChainMetaData newMetadata = new RuleChainMetaData(metadata.getRuleChainId());
newMetadata.setVersion(metadata.getVersion());
newMetadata.setFirstNodeIndex(0);
// node 0: message type switch
RuleNode switchNode = new RuleNode();
switchNode.setName("Message Type Switch");
switchNode.setType("org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode");
switchNode.setConfiguration(OBJECT_MAPPER.createObjectNode().put("version", 0));
switchNode.setAdditionalInfo(OBJECT_MAPPER.createObjectNode().put("layoutX", 200).put("layoutY", 150));
// node 1: log node for telemetry
RuleNode logNode = new RuleNode();
logNode.setName("Log Telemetry");
logNode.setType("org.thingsboard.rule.engine.action.TbLogNode");
logNode.setConfiguration(OBJECT_MAPPER.createObjectNode()
.put("scriptLang", "TBEL")
.put("jsScript", "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);")
.put("tbelScript", "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);"));
logNode.setAdditionalInfo(OBJECT_MAPPER.createObjectNode().put("layoutX", 500).put("layoutY", 100));
// node 2: save timeseries
RuleNode saveNode = new RuleNode();
saveNode.setName("Save Timeseries");
saveNode.setType("org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode");
saveNode.setConfiguration(OBJECT_MAPPER.createObjectNode()
.put("defaultTTL", 0)
.put("skipLatestPersistence", false)
.put("useServerTs", false));
saveNode.setAdditionalInfo(OBJECT_MAPPER.createObjectNode().put("layoutX", 500).put("layoutY", 250));
newMetadata.setNodes(List.of(switchNode, logNode, saveNode));
// connection: switch -> log (on "Post telemetry")
NodeConnectionInfo conn1 = new NodeConnectionInfo();
conn1.setFromIndex(0);
conn1.setToIndex(1);
conn1.setType("Post telemetry");
// connection: switch -> save timeseries (on "Post telemetry")
NodeConnectionInfo conn2 = new NodeConnectionInfo();
conn2.setFromIndex(0);
conn2.setToIndex(2);
conn2.setType("Post telemetry");
newMetadata.setConnections(List.of(conn1, conn2));
newMetadata.setRuleChainConnections(List.of());
RuleChainMetaData savedMetadata = client.saveRuleChainMetaData(newMetadata, false);
assertNotNull(savedMetadata);
assertEquals(3, savedMetadata.getNodes().size());
assertEquals(2, savedMetadata.getConnections().size());
// verify saved nodes
RuleChainMetaData fetchedMetadata = client.getRuleChainMetaData(searchChain.getId().getId().toString());
assertEquals(3, fetchedMetadata.getNodes().size());
assertTrue(fetchedMetadata.getNodes().stream()
.anyMatch(node -> "Log Telemetry".equals(node.getName())));
assertTrue(fetchedMetadata.getNodes().stream()
.anyMatch(node -> "Save Timeseries".equals(node.getName())));
// get output labels
client.getRuleChainOutputLabels(searchChain.getId().getId().toString());
// update rule chain
RuleChain chainToUpdate = createdChains.get(3);
chainToUpdate.setName(chainToUpdate.getName() + "_updated");
chainToUpdate.setDebugMode(true);
RuleChain updatedChain = client.saveRuleChain(chainToUpdate);
assertEquals(chainToUpdate.getName(), updatedChain.getName());
assertEquals(true, updatedChain.getDebugMode());
// delete rule chain
UUID chainToDeleteId = createdChains.get(0).getId().getId();
client.deleteRuleChain(chainToDeleteId.toString());
// verify deletion
assertReturns404(() ->
client.getRuleChainById(chainToDeleteId.toString())
);
PageDataRuleChain chainsAfterDelete = client.getRuleChains(100, 0, null,
TEST_PREFIX + "RuleChain_" + timestamp, null, null);
assertEquals(4, chainsAfterDelete.getData().size());
}
}

151
application/src/test/java/org/thingsboard/server/client/TbImageJavaClientTest.java

@ -0,0 +1,151 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.PageDataTbResourceInfo;
import org.thingsboard.client.model.ResourceExportData;
import org.thingsboard.client.model.TbImageDeleteResult;
import org.thingsboard.client.model.TbResourceInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import javax.imageio.ImageIO;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class TbImageJavaClientTest extends AbstractJavaClientTest {
private File createTempImage(String name, Color color) throws IOException {
BufferedImage img = new BufferedImage(100, 100, BufferedImage.TYPE_INT_RGB);
Graphics2D g = img.createGraphics();
g.setColor(color);
g.fillRect(0, 0, 100, 100);
g.dispose();
File tempFile = File.createTempFile(name, ".png");
tempFile.deleteOnExit();
ImageIO.write(img, "png", tempFile);
return tempFile;
}
@Test
public void testImageLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<TbResourceInfo> createdImages = new ArrayList<>();
Color[] colors = {Color.RED, Color.GREEN, Color.BLUE, Color.YELLOW, Color.CYAN};
// upload 5 images
for (int i = 0; i < 5; i++) {
String title = TEST_PREFIX + "Image_" + timestamp + "_" + i;
File imageFile = createTempImage("test_image_" + i, colors[i]);
TbResourceInfo uploaded = client.uploadImage(imageFile, title, null);
assertNotNull(uploaded);
assertNotNull(uploaded.getResourceKey());
assertEquals(title, uploaded.getTitle());
assertNotNull(uploaded.getLink());
createdImages.add(uploaded);
}
// list images with text search
PageDataTbResourceInfo filteredImages = client.getImages(100, 0, null, false,
TEST_PREFIX + "Image_" + timestamp, null, null);
assertNotNull(filteredImages);
assertEquals(5, filteredImages.getData().size());
// get image info by type and key
TbResourceInfo searchImage = createdImages.get(2);
TbResourceInfo fetchedInfo = client.getImageInfo("tenant", searchImage.getResourceKey());
assertEquals(searchImage.getTitle(), fetchedInfo.getTitle());
assertEquals(searchImage.getResourceKey(), fetchedInfo.getResourceKey());
// download image
File downloadedImage = client.downloadImage("tenant", searchImage.getResourceKey(), null, null);
assertNotNull(downloadedImage);
assertTrue(downloadedImage.exists());
assertTrue(downloadedImage.length() > 0);
// download image preview
File preview = client.downloadImagePreview("tenant", searchImage.getResourceKey(), null, null);
assertNotNull(preview);
assertTrue(preview.exists());
assertTrue(preview.length() > 0);
// update image file
File updatedImageFile = createTempImage("updated_image", Color.MAGENTA);
TbResourceInfo updatedImage = client.updateImage("tenant", searchImage.getResourceKey(), updatedImageFile);
assertNotNull(updatedImage);
assertEquals(searchImage.getResourceKey(), updatedImage.getResourceKey());
// update image info (title)
TbResourceInfo infoToUpdate = client.getImageInfo("tenant", createdImages.get(3).getResourceKey());
infoToUpdate.setTitle(infoToUpdate.getTitle() + "_updated");
TbResourceInfo updatedInfo = client.updateImageInfo("tenant", infoToUpdate.getResourceKey(), infoToUpdate);
assertEquals(infoToUpdate.getTitle(), updatedInfo.getTitle());
// make image public
TbResourceInfo publicImage = client.updateImagePublicStatus("tenant",
createdImages.get(1).getResourceKey(), true);
assertTrue(publicImage.getPublic());
assertNotNull(publicImage.getPublicResourceKey());
assertNotNull(publicImage.getPublicLink());
// download public image
File publicDownload = client.downloadPublicImage(publicImage.getPublicResourceKey(), null, null);
assertNotNull(publicDownload);
assertTrue(publicDownload.exists());
assertTrue(publicDownload.length() > 0);
// make image private again
TbResourceInfo privateImage = client.updateImagePublicStatus("tenant",
createdImages.get(1).getResourceKey(), false);
assertEquals(false, privateImage.getPublic());
// export image
ResourceExportData exportData = client.exportImage("tenant", createdImages.get(4).getResourceKey());
assertNotNull(exportData);
assertNotNull(exportData.getData());
assertEquals(createdImages.get(4).getTitle(), exportData.getTitle());
assertEquals(createdImages.get(4).getResourceKey(), exportData.getResourceKey());
// delete image
String keyToDelete = createdImages.get(0).getResourceKey();
TbImageDeleteResult deleteResult = client.deleteImage("tenant", keyToDelete, false);
assertNotNull(deleteResult);
assertTrue(deleteResult.getSuccess());
// verify deletion
assertReturns404(() ->
client.getImageInfo("tenant", keyToDelete)
);
PageDataTbResourceInfo imagesAfterDelete = client.getImages(100, 0, null, false,
TEST_PREFIX + "Image_" + timestamp, null, null);
assertEquals(4, imagesAfterDelete.getData().size());
}
}

128
application/src/test/java/org/thingsboard/server/client/TbResourceJavaClientTest.java

@ -0,0 +1,128 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.PageDataTbResourceInfo;
import org.thingsboard.client.model.ResourceType;
import org.thingsboard.client.model.TbResource;
import org.thingsboard.client.model.TbResourceInfo;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.io.File;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class TbResourceJavaClientTest extends AbstractJavaClientTest {
@Test
public void testResourceLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<TbResourceInfo> createdResources = new ArrayList<>();
// create 5 JS_MODULE resources
for (int i = 0; i < 5; i++) {
TbResource resource = new TbResource();
resource.setTitle(TEST_PREFIX + "Resource_" + timestamp + "_" + i);
resource.setResourceType(ResourceType.JS_MODULE);
resource.setResourceKey("test_module_" + timestamp + "_" + i + ".js");
resource.setFileName("test_module_" + timestamp + "_" + i + ".js");
String jsContent = "export default function test" + i + "() { return " + i + "; }";
resource.setData(Base64.getEncoder().encodeToString(jsContent.getBytes()));
TbResourceInfo created = client.saveResource(resource);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(resource.getTitle(), created.getTitle());
assertEquals(ResourceType.JS_MODULE, created.getResourceType());
createdResources.add(created);
}
// get tenant resources, check count
PageDataTbResourceInfo tenantResources = client.getTenantResources(100, 0, null, null, null);
assertNotNull(tenantResources);
assertNotNull(tenantResources.getData());
int initialSize = tenantResources.getData().size();
assertTrue("Expected at least 5 resources, but got " + initialSize, initialSize >= 5);
// find with text search
PageDataTbResourceInfo filteredResources = client.getTenantResources(100, 0,
TEST_PREFIX + "Resource_" + timestamp, null, null);
assertEquals(5, filteredResources.getData().size());
// get resources with type filter
PageDataTbResourceInfo jsResources = client.getResources(100, 0,
ResourceType.JS_MODULE.getValue(), null, TEST_PREFIX + "Resource_" + timestamp, null, null);
assertEquals(5, jsResources.getData().size());
// get resource info by id
TbResourceInfo searchResource = createdResources.get(2);
TbResourceInfo fetchedInfo = client.getResourceInfoById(searchResource.getId().getId().toString());
assertEquals(searchResource.getTitle(), fetchedInfo.getTitle());
assertEquals(searchResource.getResourceKey(), fetchedInfo.getResourceKey());
// get full resource by id (includes data)
TbResource fullResource = client.getResourceById(searchResource.getId().getId().toString());
assertNotNull(fullResource);
assertEquals(searchResource.getTitle(), fullResource.getTitle());
assertNotNull(fullResource.getData());
// download resource
File downloadedFile = client.downloadResource(searchResource.getId().getId().toString());
assertNotNull(downloadedFile);
assertTrue(downloadedFile.exists());
assertTrue(downloadedFile.length() > 0);
// get resources by list of ids
List<String> idsToFetch = List.of(
createdResources.get(0).getId().getId().toString(),
createdResources.get(1).getId().getId().toString()
);
List<TbResourceInfo> resourceList = client.getSystemOrTenantResourcesByIds(idsToFetch);
assertEquals(2, resourceList.size());
// update resource
TbResource resourceToUpdate = client.getResourceById(createdResources.get(3).getId().getId().toString());
resourceToUpdate.setTitle(resourceToUpdate.getTitle() + "_updated");
String updatedContent = "export default function updated() { return 42; }";
resourceToUpdate.setData(Base64.getEncoder().encodeToString(updatedContent.getBytes()));
TbResourceInfo updatedResource = client.saveResource(resourceToUpdate);
assertEquals(resourceToUpdate.getTitle(), updatedResource.getTitle());
// delete resource
UUID resourceToDeleteId = createdResources.get(0).getId().getId();
client.deleteResource(resourceToDeleteId.toString(), false);
// verify deletion
assertReturns404(() ->
client.getResourceInfoById(resourceToDeleteId.toString())
);
PageDataTbResourceInfo resourcesAfterDelete = client.getTenantResources(100, 0,
TEST_PREFIX + "Resource_" + timestamp, null, null);
assertEquals(4, resourcesAfterDelete.getData().size());
}
}

150
application/src/test/java/org/thingsboard/server/client/TelemetryJavaClientTest.java

@ -0,0 +1,150 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AttributeData;
import org.thingsboard.client.model.Device;
import org.thingsboard.client.model.TsData;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class TelemetryJavaClientTest extends AbstractJavaClientTest {
@Test
public void testTelemetryLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
// create a device for telemetry operations
Device device = new Device();
device.setName("TelemetryTestDevice_" + timestamp);
device.setType("default");
Device createdDevice = client.saveDevice(device, null, null, null, null);
assertNotNull(createdDevice);
String entityType = "DEVICE";
String entityId = createdDevice.getId().getId().toString();
// save server-side attributes
String serverAttributes = "{\"serverAttr1\": \"value1\", \"serverAttr2\": 42}";
client.saveEntityAttributesV2(entityType, entityId, "SERVER_SCOPE", serverAttributes);
// save shared attributes
String sharedAttributes = "{\"sharedAttr1\": \"sharedValue1\", \"sharedAttr2\": true}";
client.saveEntityAttributesV2(entityType, entityId, "SHARED_SCOPE", sharedAttributes);
// get attribute keys
List<String> allKeys = client.getAttributeKeys(entityType, entityId);
assertNotNull(allKeys);
assertTrue(allKeys.containsAll(List.of("serverAttr1", "serverAttr2", "sharedAttr1", "sharedAttr2")));
// get attribute keys by scope
List<String> serverKeys = client.getAttributeKeysByScope(entityType, entityId, "SERVER_SCOPE");
assertEquals(2 + 1, serverKeys.size()); //active attribute is automatically added to server scope
assertTrue(serverKeys.containsAll(List.of("serverAttr1", "serverAttr2", "active")));
// get attributes by scope
List<AttributeData> serverAttrs = client.getAttributesByScope(entityType, entityId, "SERVER_SCOPE", "serverAttr1,serverAttr2", null);
assertNotNull(serverAttrs);
assertEquals(2, serverAttrs.size());
// get all attributes
List<AttributeData> allAttrs = client.getAttributes(entityType, entityId, "serverAttr1,sharedAttr1", null);
assertEquals(2, allAttrs.size());
assertEquals("value1", allAttrs.stream().filter(attr -> attr.getKey().equals("serverAttr1")).findFirst().orElseThrow().getValue().toString());
assertEquals("sharedValue1", allAttrs.stream().filter(attr -> attr.getKey().equals("sharedAttr1")).findFirst().orElseThrow().getValue().toString());
// save timeseries data
long ts1 = timestamp - 60000;
long ts2 = timestamp - 30000;
long ts3 = timestamp;
String telemetryBody = "{\"ts\":" + ts1 + ",\"values\":{\"temperature\":25.5,\"humidity\":60}}";
client.saveEntityTelemetry(entityType, entityId, "ANY", telemetryBody);
String telemetryBody2 = "{\"ts\":" + ts2 + ",\"values\":{\"temperature\":26.0,\"humidity\":58}}";
client.saveEntityTelemetry(entityType, entityId, "ANY", telemetryBody2);
String telemetryBody3 = "{\"ts\":" + ts3 + ",\"values\":{\"temperature\":27.1,\"humidity\":55}}";
client.saveEntityTelemetry(entityType, entityId, "ANY", telemetryBody3);
// get timeseries keys
List<String> tsKeys = client.getTimeseriesKeys(entityType, entityId);
assertNotNull(tsKeys);
assertEquals(2, tsKeys.size());
assertTrue(tsKeys.containsAll(List.of("humidity", "temperature")));
// get latest timeseries
Map<String, List<TsData>> latestData = client.getLatestTimeseries(entityType, entityId, "temperature,humidity", false, null);
assertNotNull(latestData);
assertNotNull(latestData.get("temperature"));
assertFalse(latestData.get("temperature").isEmpty());
assertEquals("27.1", latestData.get("temperature").get(0).getValue().toString());
// get timeseries history
Map<String, List<TsData>> historyData = client.getTimeseriesHistory(
entityType, entityId,
ts1 - 1000, ts3 + 1000, "temperature",
null, null, null, null, "NONE", "ASC", false, null);
assertNotNull(historyData);
List<TsData> tempHistory = historyData.get("temperature");
assertNotNull(tempHistory);
assertEquals(3, tempHistory.size());
assertEquals("25.5", tempHistory.get(0).getValue().toString());
assertEquals("27.1", tempHistory.get(2).getValue().toString());
// delete timeseries
client.deleteEntityTimeseries(entityType, entityId, "humidity", true, null, null, true, false, null);
List<String> keysAfterDelete = client.getTimeseriesKeys(entityType, entityId);
assertFalse(keysAfterDelete.contains("humidity"));
// delete attributes
client.deleteEntityAttributes(entityType, entityId, "SERVER_SCOPE", "serverAttr1", null);
List<String> serverKeysAfterDelete = client.getAttributeKeysByScope(entityType, entityId, "SERVER_SCOPE");
assertFalse(serverKeysAfterDelete.contains("serverAttr1"));
assertTrue(serverKeysAfterDelete.contains("serverAttr2"));
// save device attributes using device-specific endpoint
client.saveDeviceAttributes(entityId, "SERVER_SCOPE", "{\"deviceSpecificAttr\": \"test\"}");
List<String> deviceKeys = client.getAttributeKeysByScope(entityType, entityId, "SERVER_SCOPE");
assertTrue(deviceKeys.contains("deviceSpecificAttr"));
// delete device attributes
client.deleteDeviceAttributes(entityId, "SERVER_SCOPE", "deviceSpecificAttr", null);
List<String> deviceKeysAfterDelete = client.getAttributeKeysByScope(entityType, entityId, "SERVER_SCOPE");
assertFalse(deviceKeysAfterDelete.contains("deviceSpecificAttr"));
// save telemetry with TTL
String ttlTelemetry = "{\"ts\":" + timestamp + ",\"values\":{\"shortLived\":99}}";
client.saveEntityTelemetryWithTTL(entityType, entityId, "ANY", 86400L, ttlTelemetry);
Map<String, List<TsData>> latestWithTtl = client.getLatestTimeseries(entityType, entityId, "shortLived", false, null);
assertNotNull(latestWithTtl.get("shortLived"));
assertEquals("99", latestWithTtl.get("shortLived").get(0).getValue().toString());
}
}

119
application/src/test/java/org/thingsboard/server/client/TenantJavaClientTest.java

@ -0,0 +1,119 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.model.Authority;
import org.thingsboard.client.model.PageDataTenant;
import org.thingsboard.client.model.PageDataUser;
import org.thingsboard.client.model.Tenant;
import org.thingsboard.client.model.User;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class TenantJavaClientTest extends AbstractJavaClientTest {
@Test
public void testTenantLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<Tenant> createdTenants = new ArrayList<>();
// authenticate as sysadmin for tenant management
client.login("sysadmin@thingsboard.org", "sysadmin");
// create 20 tenants
for (int i = 0; i < 20; i++) {
Tenant tenant = new Tenant();
String tenantTitle = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i;
tenant.setTitle(tenantTitle);
tenant.setEmail("tenant_" + timestamp + "_" + i + "@test.com");
tenant.setCountry("US");
tenant.setCity("City" + i);
Tenant createdTenant = client.saveTenant(tenant);
assertNotNull(createdTenant);
assertNotNull(createdTenant.getId());
assertEquals(tenantTitle, createdTenant.getTitle());
createdTenants.add(createdTenant);
}
try {
// find all with search text, check count
PageDataTenant filteredTenants = client.getTenants(100, 0, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 tenants matching prefix", 10, filteredTenants.getData().size());
// find by id
Tenant searchTenant = createdTenants.get(10);
Tenant fetchedTenant = client.getTenantById(searchTenant.getId().getId().toString());
assertEquals(searchTenant.getTitle(), fetchedTenant.getTitle());
assertEquals(searchTenant.getEmail(), fetchedTenant.getEmail());
// update tenant
fetchedTenant.setCity("Updated City");
fetchedTenant.setCountry("DE");
Tenant updatedTenant = client.saveTenant(fetchedTenant);
assertEquals("Updated City", updatedTenant.getCity());
assertEquals("DE", updatedTenant.getCountry());
// create a tenant admin for one of the tenants and verify listing
Tenant tenantForAdmin = createdTenants.get(0);
User adminUser = new User();
adminUser.setEmail("tenanttest_admin_" + timestamp + "@test.com");
adminUser.setAuthority(Authority.TENANT_ADMIN);
adminUser.setTenantId(tenantForAdmin.getId());
adminUser.setFirstName("TestAdmin");
User savedAdmin = client.saveUser(adminUser, "false");
assertNotNull(savedAdmin);
PageDataUser tenantAdmins = client.getTenantAdmins(
tenantForAdmin.getId().getId().toString(), 100, 0, null, null, null);
assertEquals(1, tenantAdmins.getData().size());
assertEquals(savedAdmin.getEmail(), tenantAdmins.getData().get(0).getEmail());
// delete tenant
UUID tenantToDeleteId = createdTenants.get(0).getId().getId();
client.deleteTenant(tenantToDeleteId.toString());
createdTenants.remove(0);
// verify deletion
PageDataTenant tenantsAfterDelete = client.getTenants(100, 0, TEST_PREFIX_2, null, null);
assertEquals(10, tenantsAfterDelete.getData().size());
assertReturns404(() ->
client.getTenantById(tenantToDeleteId.toString())
);
} finally {
// clean up all created tenants (deleting tenant cascades to users)
client.login("sysadmin@thingsboard.org", "sysadmin");
for (Tenant tenant : createdTenants) {
try {
client.deleteTenant(tenant.getId().getId().toString());
} catch (ApiException ignored) {
}
}
}
}
}

179
application/src/test/java/org/thingsboard/server/client/TenantProfileJavaClientTest.java

@ -0,0 +1,179 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.ApiException;
import org.thingsboard.client.model.DefaultTenantProfileConfiguration;
import org.thingsboard.client.model.EntityInfo;
import org.thingsboard.client.model.PageDataEntityInfo;
import org.thingsboard.client.model.PageDataTenantProfile;
import org.thingsboard.client.model.TenantProfile;
import org.thingsboard.client.model.TenantProfileData;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class TenantProfileJavaClientTest extends AbstractJavaClientTest {
@Test
public void testTenantProfileLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<TenantProfile> createdProfiles = new ArrayList<>();
// authenticate as sysadmin for tenant profile management
client.login("sysadmin@thingsboard.org", "sysadmin");
// get initial count (there should be a default profile)
PageDataTenantProfile initialProfiles = client.getTenantProfiles(100, 0, null, null, null);
assertNotNull(initialProfiles);
int initialSize = initialProfiles.getData().size();
assertTrue("Expected at least 1 default tenant profile", initialSize >= 1);
// get default tenant profile info
EntityInfo defaultProfileInfo = client.getDefaultTenantProfileInfo();
assertNotNull(defaultProfileInfo);
assertNotNull(defaultProfileInfo.getName());
try {
// create 5 tenant profiles
for (int i = 0; i < 5; i++) {
TenantProfile profile = new TenantProfile();
profile.setName(TEST_PREFIX + "TenantProfile_" + timestamp + "_" + i);
profile.setDescription("Test tenant profile " + i);
profile.setIsolatedTbRuleEngine(false);
TenantProfileData profileData = new TenantProfileData();
DefaultTenantProfileConfiguration config = new DefaultTenantProfileConfiguration();
config.setMaxDevices(100L);
config.setMaxAssets(100L);
config.setMaxCustomers(50L);
config.setMaxUsers(50L);
config.setMaxDashboards(50L);
config.setMaxRuleChains(20L);
config.setMaxDataPointsPerRollingArg(20L);
config.setMaxRelatedEntitiesToReturnPerCfArgument(20);
config.setMaxRelationLevelPerCfArgument(20);
profileData.setConfiguration(config);
profile.setProfileData(profileData);
profile.setDefault(false);
TenantProfile created = client.saveTenantProfile(profile);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(profile.getName(), created.getName());
assertEquals(profile.getDescription(), created.getDescription());
assertFalse(created.getDefault());
createdProfiles.add(created);
}
// find all, check count
PageDataTenantProfile allProfiles = client.getTenantProfiles(100, 0, null, null, null);
assertNotNull(allProfiles);
assertEquals(initialSize + 5, allProfiles.getData().size());
// find with text search
PageDataTenantProfile filteredProfiles = client.getTenantProfiles(100, 0,
TEST_PREFIX + "TenantProfile_" + timestamp, null, null);
assertEquals(5, filteredProfiles.getData().size());
// get by id
TenantProfile searchProfile = createdProfiles.get(2);
TenantProfile fetchedProfile = client.getTenantProfileById(searchProfile.getId().getId().toString());
assertEquals(searchProfile.getName(), fetchedProfile.getName());
assertEquals(searchProfile.getDescription(), fetchedProfile.getDescription());
// update tenant profile
fetchedProfile.setDescription("Updated description");
TenantProfile updatedProfile = client.saveTenantProfile(fetchedProfile);
assertEquals("Updated description", updatedProfile.getDescription());
assertEquals(fetchedProfile.getName(), updatedProfile.getName());
// get tenant profile infos (paginated)
PageDataEntityInfo profileInfos = client.getTenantProfileInfos(100, 0, null, null, null);
assertNotNull(profileInfos);
assertEquals(initialSize + 5, profileInfos.getData().size());
// get profiles by list of ids
List<String> idsToFetch = List.of(
createdProfiles.get(0).getId().getId().toString(),
createdProfiles.get(1).getId().getId().toString()
);
List<TenantProfile> profileList = client.getTenantProfileList(idsToFetch);
assertEquals(2, profileList.size());
// set a profile as default
TenantProfile profileToSetDefault = createdProfiles.get(1);
client.setDefaultTenantProfile(profileToSetDefault.getId().getId().toString());
EntityInfo defaultTenantProfileInfo = client.getDefaultTenantProfileInfo();
assertEquals(profileToSetDefault.getName(), defaultTenantProfileInfo.getName());
// verify default profile info now points to the new default
EntityInfo newDefaultInfo = client.getDefaultTenantProfileInfo();
assertEquals(profileToSetDefault.getName(), newDefaultInfo.getName());
// restore original default profile
TenantProfile originalDefault = initialProfiles.getData().stream()
.filter(TenantProfile::getDefault)
.findFirst()
.orElseThrow();
client.setDefaultTenantProfile(originalDefault.getId().getId().toString());
// delete tenant profile (cannot delete the default one)
UUID profileToDeleteId = createdProfiles.get(0).getId().getId();
client.deleteTenantProfile(profileToDeleteId.toString());
createdProfiles.remove(0);
// verify deletion
assertReturns404(() ->
client.getTenantProfileById(profileToDeleteId.toString())
);
PageDataTenantProfile profilesAfterDelete = client.getTenantProfiles(100, 0, null, null, null);
assertEquals(initialSize + 4, profilesAfterDelete.getData().size());
} finally {
// clean up created profiles
client.login("sysadmin@thingsboard.org", "sysadmin");
// ensure original default is restored before deleting test profiles
TenantProfile originalDefault = initialProfiles.getData().stream()
.filter(TenantProfile::getDefault)
.findFirst()
.orElseThrow();
try {
client.setDefaultTenantProfile(originalDefault.getId().getId().toString());
} catch (ApiException ignored) {
}
for (TenantProfile profile : createdProfiles) {
try {
client.deleteTenantProfile(profile.getId().getId().toString());
} catch (ApiException ignored) {
}
}
}
}
}

78
application/src/test/java/org/thingsboard/server/client/TwoFactorAuthJavaClientTest.java

@ -0,0 +1,78 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.AccountTwoFaSettings;
import org.thingsboard.client.model.PlatformTwoFaSettings;
import org.thingsboard.client.model.TotpTwoFaAccountConfig;
import org.thingsboard.client.model.TotpTwoFaProviderConfig;
import org.thingsboard.client.model.TwoFaAccountConfig;
import org.thingsboard.client.model.TwoFaProviderType;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@DaoSqlTest
public class TwoFactorAuthJavaClientTest extends AbstractJavaClientTest {
@Test
public void testTwoFactorAuthLifecycle() throws Exception {
// save original platform 2FA settings as sysadmin
client.login("sysadmin@thingsboard.org", "sysadmin");
// configure platform 2FA settings with TOTP provider
TotpTwoFaProviderConfig totpProviderConfig = new TotpTwoFaProviderConfig();
totpProviderConfig.setIssuerName("TestThingsBoard");
PlatformTwoFaSettings newSettings = new PlatformTwoFaSettings();
newSettings.setProviders(List.of(totpProviderConfig));
newSettings.setMinVerificationCodeSendPeriod(30);
newSettings.setTotalAllowedTimeForVerification(300);
newSettings.setMaxVerificationFailuresBeforeUserLockout(5);
PlatformTwoFaSettings savedSettings = client.savePlatformTwoFaSettings(newSettings);
assertNotNull(savedSettings);
assertNotNull(savedSettings.getProviders());
assertFalse(savedSettings.getProviders().isEmpty());
assertEquals(30, savedSettings.getMinVerificationCodeSendPeriod().intValue());
assertEquals(300, savedSettings.getTotalAllowedTimeForVerification().intValue());
// get available 2FA providers (should include TOTP)
List<TwoFaProviderType> providerTypes = client.getAvailableTwoFaProviderTypes();
assertNotNull(providerTypes);
assertTrue(providerTypes.contains(TwoFaProviderType.TOTP));
// get account 2FA settings (should be empty initially)
AccountTwoFaSettings accountSettings = client.getAccountTwoFaSettings();
assertNull(accountSettings);
// generate TOTP account config
TwoFaAccountConfig generatedConfig = client.generateTwoFaAccountConfig(TwoFaProviderType.TOTP.getValue());
assertNotNull(generatedConfig);
TotpTwoFaAccountConfig totpConfig = (TotpTwoFaAccountConfig) generatedConfig;
assertNotNull(totpConfig);
assertNotNull(totpConfig.getAuthUrl());
assertTrue(totpConfig.getAuthUrl().startsWith("otpauth://totp/"));
}
}

135
application/src/test/java/org/thingsboard/server/client/UserJavaClientTest.java

@ -0,0 +1,135 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import org.junit.Test;
import org.thingsboard.client.model.Authority;
import org.thingsboard.client.model.Customer;
import org.thingsboard.client.model.JwtPair;
import org.thingsboard.client.model.PageDataUser;
import org.thingsboard.client.model.User;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class UserJavaClientTest extends AbstractJavaClientTest {
@Test
public void testUserLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<User> createdUsers = new ArrayList<>();
// create 20 tenant admin users
for (int i = 0; i < 20; i++) {
User user = new User();
String email = ((i % 2 == 0) ? TEST_PREFIX : TEST_PREFIX_2) + timestamp + "_" + i + "@test.com";
user.setEmail(email);
user.setAuthority(Authority.TENANT_ADMIN);
user.setTenantId(savedClientTenant.getId());
user.setFirstName("First" + i);
user.setLastName("Last" + i);
User createdUser = client.saveUser(user, "false");
assertNotNull(createdUser);
assertNotNull(createdUser.getId());
assertEquals(email, createdUser.getEmail());
assertEquals(Authority.TENANT_ADMIN, createdUser.getAuthority());
createdUsers.add(createdUser);
}
// find all tenant admins, check count (20 created + 1 from setup)
PageDataUser allUsers = client.getUsers(100, 0, null, null, null);
assertNotNull(allUsers);
assertNotNull(allUsers.getData());
int initialSize = allUsers.getData().size();
assertEquals("Expected 21 users (20 created + 2 from setup), but got " + initialSize, 22, initialSize);
// find with search text, check count
PageDataUser filteredUsers = client.getUsers(100, 0, TEST_PREFIX_2, null, null);
assertEquals("Expected exactly 10 users matching prefix", 10, filteredUsers.getData().size());
// find by id
User searchUser = createdUsers.get(10);
User fetchedUser = client.getUserById(searchUser.getId().getId().toString());
assertEquals(searchUser.getEmail(), fetchedUser.getEmail());
assertEquals(searchUser.getFirstName(), fetchedUser.getFirstName());
// update user
fetchedUser.setFirstName("UpdatedFirst");
fetchedUser.setLastName("UpdatedLast");
User updatedUser = client.saveUser(fetchedUser, "false");
assertEquals("UpdatedFirst", updatedUser.getFirstName());
assertEquals("UpdatedLast", updatedUser.getLastName());
// activate user and get token
activateUser(createdUsers.get(0).getId(), "password123", false);
JwtPair userToken = client.getUserToken(createdUsers.get(0).getId().getId().toString());
assertNotNull(userToken);
assertNotNull(userToken.getToken());
// disable user credentials
client.setUserCredentialsEnabled(createdUsers.get(0).getId().getId().toString(), "false");
// re-enable user credentials
client.setUserCredentialsEnabled(createdUsers.get(0).getId().getId().toString(), "true");
// create customer users and verify listing
Customer customer2 = new Customer();
customer2.setTitle("User test customer " + timestamp);
customer2.setEmail("usertest_" + timestamp + "@test.com");
Customer savedCustomer2 = client.saveCustomer(customer2, null, null, null);
List<User> customerUsers = new ArrayList<>();
for (int i = 0; i < 5; i++) {
User customerUser = new User();
customerUser.setEmail("custuser_" + timestamp + "_" + i + "@test.com");
customerUser.setAuthority(Authority.CUSTOMER_USER);
customerUser.setTenantId(savedClientTenant.getId());
customerUser.setCustomerId(savedCustomer2.getId());
customerUser.setFirstName("CustFirst" + i);
customerUser.setLastName("CustLast" + i);
User created = client.saveUser(customerUser, "false");
assertNotNull(created);
customerUsers.add(created);
}
// list customer users
PageDataUser customerUserPage = client.getCustomerUsers(
savedCustomer2.getId().getId().toString(), 100, 0, null, null, null);
assertEquals("Expected 5 customer users", 5, customerUserPage.getData().size());
// delete user
UUID userToDeleteId = createdUsers.get(0).getId().getId();
client.deleteUser(userToDeleteId.toString());
// verify deletion
PageDataUser usersAfterDelete = client.getUsers(100, 0, null, null, null);
assertEquals(initialSize + 5 - 1, usersAfterDelete.getData().size());
assertReturns404(() ->
client.getUserById(userToDeleteId.toString())
);
}
}

155
application/src/test/java/org/thingsboard/server/client/WidgetTypeJavaClientTest.java

@ -0,0 +1,155 @@
/**
* Copyright © 2016-2026 The Thingsboard Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.thingsboard.server.client;
import com.fasterxml.jackson.databind.JsonNode;
import org.junit.Test;
import org.thingsboard.client.model.PageDataWidgetTypeInfo;
import org.thingsboard.client.model.WidgetTypeDetails;
import org.thingsboard.client.model.WidgetTypeInfo;
import org.thingsboard.client.model.WidgetsBundle;
import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@DaoSqlTest
public class WidgetTypeJavaClientTest extends AbstractJavaClientTest {
private JsonNode createDescriptor(String type) {
return OBJECT_MAPPER.createObjectNode()
.put("type", type)
.put("sizeX", 7.5)
.put("sizeY", 5)
.put("resources", "[]")
.put("templateHtml", "<div class='test-widget'>Test</div>")
.put("templateCss", ".test-widget { font-size: 14px; }")
.put("controllerScript", "self.onInit = function() {};")
.put("settingsSchema", "{}")
.put("dataKeySettingsSchema", "{}");
}
@Test
public void testWidgetTypeLifecycle() throws Exception {
long timestamp = System.currentTimeMillis();
List<WidgetTypeDetails> createdWidgetTypes = new ArrayList<>();
// create a widgets bundle
WidgetsBundle bundle = new WidgetsBundle(null, null, null,
TEST_PREFIX + "Bundle_" + timestamp, null, false,
"Test bundle description", null, null);
WidgetsBundle savedBundle = client.saveWidgetsBundle(bundle);
assertNotNull(savedBundle);
assertNotNull(savedBundle.getId());
assertEquals(bundle.getTitle(), savedBundle.getTitle());
// create 5 widget types
for (int i = 0; i < 5; i++) {
String name = TEST_PREFIX + "Widget_" + timestamp + "_" + i;
JsonNode descriptor = createDescriptor("latest");
WidgetTypeDetails widgetType = new WidgetTypeDetails(null, null, null, name, descriptor);
widgetType.setDescription("Test widget " + i);
widgetType.setDeprecated(false);
widgetType.setTags(List.of("test", "automated"));
WidgetTypeDetails created = client.saveWidgetType(widgetType, false);
assertNotNull(created);
assertNotNull(created.getId());
assertEquals(name, created.getName());
assertNotNull(created.getFqn());
createdWidgetTypes.add(created);
}
// list widget types with text search (tenant only)
PageDataWidgetTypeInfo filteredTypes = client.getWidgetTypes(100, 0,
TEST_PREFIX + "Widget_" + timestamp, null, null,
true, false, null, null, null);
assertNotNull(filteredTypes);
assertEquals(5, filteredTypes.getData().size());
// get widget type details by id
WidgetTypeDetails searchWidget = createdWidgetTypes.get(2);
WidgetTypeDetails fetchedDetails = client.getWidgetTypeById(
searchWidget.getId().getId().toString(), true);
assertEquals(searchWidget.getName(), fetchedDetails.getName());
assertEquals(searchWidget.getFqn(), fetchedDetails.getFqn());
assertEquals("Test widget 2", fetchedDetails.getDescription());
// get widget type info by id
WidgetTypeInfo fetchedInfo = client.getWidgetTypeInfoById(
searchWidget.getId().getId().toString());
assertEquals(searchWidget.getName(), fetchedInfo.getName());
// add widget types to bundle
List<String> widgetTypeIds = createdWidgetTypes.stream()
.map(wt -> wt.getId().getId().toString())
.collect(Collectors.toList());
client.updateWidgetsBundleWidgetTypes(savedBundle.getId().getId().toString(), widgetTypeIds);
// get bundle widget type fqns
List<String> bundleFqns = client.getBundleWidgetTypeFqns(savedBundle.getId().getId().toString());
assertEquals(5, bundleFqns.size());
// get bundle widget types details
List<WidgetTypeDetails> bundleDetails = client.getBundleWidgetTypesDetails(
savedBundle.getId().getId().toString(), false);
assertEquals(5, bundleDetails.size());
// get bundle widget types infos (paginated)
PageDataWidgetTypeInfo bundleInfos = client.getBundleWidgetTypesInfos(
savedBundle.getId().getId().toString(), 100, 0,
null, null, null, null, null, null);
assertEquals(5, bundleInfos.getData().size());
// update widget type
WidgetTypeDetails widgetToUpdate = client.getWidgetTypeById(
createdWidgetTypes.get(3).getId().getId().toString(), true);
widgetToUpdate.setDescription("Updated description");
widgetToUpdate.setDeprecated(true);
widgetToUpdate.setTags(List.of("test", "updated"));
WidgetTypeDetails updatedWidget = client.saveWidgetType(widgetToUpdate, false);
assertEquals("Updated description", updatedWidget.getDescription());
assertEquals(true, updatedWidget.getDeprecated());
// delete widget type
String widgetToDeleteId = createdWidgetTypes.get(0).getId().getId().toString();
client.deleteWidgetType(widgetToDeleteId);
// verify deletion
assertReturns404(() ->
client.getWidgetTypeById(widgetToDeleteId, false)
);
PageDataWidgetTypeInfo typesAfterDelete = client.getWidgetTypes(100, 0,
TEST_PREFIX + "Widget_" + timestamp, null, null,
true, false, null, null, null);
assertEquals(4, typesAfterDelete.getData().size());
// delete widgets bundle
client.deleteWidgetsBundle(savedBundle.getId().getId().toString());
assertReturns404(() ->
client.getWidgetsBundleById(savedBundle.getId().getId().toString(), false)
);
}
}

7
pom.xml

@ -38,6 +38,7 @@
<pkg.implementationTitle>${project.name}</pkg.implementationTitle>
<pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>
<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>
<thingsboard.client.version>4.4.0-SNAPSHOT</thingsboard.client.version>
<spring-boot.version>3.4.13</spring-boot.version>
<tomcat.version>10.1.52</tomcat.version> <!-- to fix CVE-2026-24734 and CVE-2025-66614. TODO: remove when fixed in spring-boot-dependencies -->
<jackson.version>2.18.6</jackson.version> <!-- to fix CWE-770. TODO: remove when fixed in spring-boot-dependencies -->
@ -1134,6 +1135,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thingsboard.client</groupId>
<artifactId>thingsboard-ce-client</artifactId>
<version>${thingsboard.client.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thingsboard.msa</groupId>
<artifactId>js-executor</artifactId>

Loading…
Cancel
Save