Browse Source

Merge remote-tracking branch 'origin/lts-4.3' into rc

pull/15506/head
Viacheslav Klimov 5 months ago
parent
commit
395c6d43ff
Failed to extract signature
  1. 4
      application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java
  2. 497
      application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java
  3. 8
      application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java
  4. 9
      application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java
  5. 5
      common/edqs/src/main/java/org/thingsboard/server/edqs/data/dp/StringDataPoint.java
  6. 47
      dao/src/test/java/org/thingsboard/server/dao/TbTimescaleDBContainerProvider.java
  7. 1
      dao/src/test/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider
  8. 2
      dao/src/test/resources/nosql-test.properties
  9. 2
      dao/src/test/resources/sql-test.properties
  10. 2
      dao/src/test/resources/timescale-test.properties
  11. 57
      edqs/src/test/java/org/thingsboard/server/edqs/repo/DeviceTypeFilterTest.java
  12. 4
      pom.xml

4
application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java

@ -90,7 +90,7 @@ public class Oauth2ApiClientTest extends AbstractApiClientTest {
} }
// list tenant OAuth2 client infos // list tenant OAuth2 client infos
PageDataOAuth2ClientInfo clientInfos = client.findTenantOAuth2ClientInfos(100, 0, PageDataOAuth2ClientInfo clientInfos = client.findOAuth2ClientInfos(100, 0,
TEST_PREFIX + "OAuth2_" + timestamp, null, null); TEST_PREFIX + "OAuth2_" + timestamp, null, null);
assertNotNull(clientInfos); assertNotNull(clientInfos);
assertEquals(5, clientInfos.getData().size()); assertEquals(5, clientInfos.getData().size());
@ -130,7 +130,7 @@ public class Oauth2ApiClientTest extends AbstractApiClientTest {
client.getOAuth2ClientById(clientToDeleteId) client.getOAuth2ClientById(clientToDeleteId)
); );
PageDataOAuth2ClientInfo clientsAfterDelete = client.findTenantOAuth2ClientInfos(100, 0, PageDataOAuth2ClientInfo clientsAfterDelete = client.findOAuth2ClientInfos(100, 0,
TEST_PREFIX + "OAuth2_" + timestamp, null, null); TEST_PREFIX + "OAuth2_" + timestamp, null, null);
assertEquals(4, clientsAfterDelete.getData().size()); assertEquals(4, clientsAfterDelete.getData().size());
} }

497
application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java

@ -16,6 +16,7 @@
package org.thingsboard.server.controller; package org.thingsboard.server.controller;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import org.awaitility.Awaitility;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -52,8 +53,8 @@ import org.thingsboard.server.common.data.page.PageLink;
import org.thingsboard.server.common.data.rule.RuleChain; import org.thingsboard.server.common.data.rule.RuleChain;
import org.thingsboard.server.common.data.security.Authority; import org.thingsboard.server.common.data.security.Authority;
import org.thingsboard.server.dao.device.DeviceProfileDao; import org.thingsboard.server.dao.device.DeviceProfileDao;
import org.thingsboard.server.exception.DataValidationException;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import org.thingsboard.server.exception.DataValidationException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -62,6 +63,7 @@ import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -93,6 +95,7 @@ public class DeviceProfileControllerTest extends AbstractControllerTest {
public DeviceProfileDao deviceProfileDao(DeviceProfileDao deviceProfileDao) { public DeviceProfileDao deviceProfileDao(DeviceProfileDao deviceProfileDao) {
return Mockito.mock(DeviceProfileDao.class, AdditionalAnswers.delegatesTo(deviceProfileDao)); return Mockito.mock(DeviceProfileDao.class, AdditionalAnswers.delegatesTo(deviceProfileDao));
} }
} }
@Before @Before
@ -640,364 +643,364 @@ public class DeviceProfileControllerTest extends AbstractControllerTest {
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidProtoFile() throws Exception { public void testSaveProtoDeviceProfileWithInvalidProtoFile() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SchemaValidationTest {\n" + "message SchemaValidationTest {\n" +
" required int32 parameter = 1;\n" + " required int32 parameter = 1;\n" +
"}", "[Transport Configuration] failed to parse attributes proto schema due to: Syntax error in :6:4: 'required' label forbidden in proto3 field declarations"); "}", "[Transport Configuration] failed to parse attributes proto schema due to: Syntax error in :6:4: 'required' label forbidden in proto3 field declarations");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidProtoSyntax() throws Exception { public void testSaveProtoDeviceProfileWithInvalidProtoSyntax() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto2\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto2\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SchemaValidationTest {\n" + "message SchemaValidationTest {\n" +
" required int32 parameter = 1;\n" + " required int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!"); "}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!");
} }
@Test @Test
public void testSaveProtoDeviceProfileOptionsNotSupported() throws Exception { public void testSaveProtoDeviceProfileOptionsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"option java_package = \"com.test.schemavalidation\";\n" + "option java_package = \"com.test.schemavalidation\";\n" +
"option java_multiple_files = true;\n" + "option java_multiple_files = true;\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SchemaValidationTest {\n" + "message SchemaValidationTest {\n" +
" optional int32 parameter = 1;\n" + " optional int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfilePublicImportsNotSupported() throws Exception { public void testSaveProtoDeviceProfilePublicImportsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"import public \"oldschema.proto\";\n" + "import public \"oldschema.proto\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SchemaValidationTest {\n" + "message SchemaValidationTest {\n" +
" optional int32 parameter = 1;\n" + " optional int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileImportsNotSupported() throws Exception { public void testSaveProtoDeviceProfileImportsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"import \"oldschema.proto\";\n" + "import \"oldschema.proto\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SchemaValidationTest {\n" + "message SchemaValidationTest {\n" +
" optional int32 parameter = 1;\n" + " optional int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileExtendDeclarationsNotSupported() throws Exception { public void testSaveProtoDeviceProfileExtendDeclarationsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"extend google.protobuf.MethodOptions {\n" + "extend google.protobuf.MethodOptions {\n" +
" MyMessage my_method_option = 50007;\n" + " MyMessage my_method_option = 50007;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileEnumOptionsNotSupported() throws Exception { public void testSaveProtoDeviceProfileEnumOptionsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"enum testEnum {\n" + "enum testEnum {\n" +
" option allow_alias = true;\n" + " option allow_alias = true;\n" +
" DEFAULT = 0;\n" + " DEFAULT = 0;\n" +
" STARTED = 1;\n" + " STARTED = 1;\n" +
" RUNNING = 2;\n" + " RUNNING = 2;\n" +
"}\n" + "}\n" +
"\n" + "\n" +
"message testMessage {\n" + "message testMessage {\n" +
" optional int32 parameter = 1;\n" + " optional int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!");
} }
@Test @Test
public void testSaveProtoDeviceProfileNoOneMessageTypeExists() throws Exception { public void testSaveProtoDeviceProfileNoOneMessageTypeExists() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"enum testEnum {\n" + "enum testEnum {\n" +
" DEFAULT = 0;\n" + " DEFAULT = 0;\n" +
" STARTED = 1;\n" + " STARTED = 1;\n" +
" RUNNING = 2;\n" + " RUNNING = 2;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!"); "}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!");
} }
@Test @Test
public void testSaveProtoDeviceProfileMessageTypeOptionsNotSupported() throws Exception { public void testSaveProtoDeviceProfileMessageTypeOptionsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message testMessage {\n" + "message testMessage {\n" +
" option allow_alias = true;\n" + " option allow_alias = true;\n" +
" optional int32 parameter = 1;\n" + " optional int32 parameter = 1;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileMessageTypeExtensionsNotSupported() throws Exception { public void testSaveProtoDeviceProfileMessageTypeExtensionsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message TestMessage {\n" + "message TestMessage {\n" +
" extensions 100 to 199;\n" + " extensions 100 to 199;\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileMessageTypeReservedElementsNotSupported() throws Exception { public void testSaveProtoDeviceProfileMessageTypeReservedElementsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message Foo {\n" + "message Foo {\n" +
" reserved 2, 15, 9 to 11;\n" + " reserved 2, 15, 9 to 11;\n" +
" reserved \"foo\", \"bar\";\n" + " reserved \"foo\", \"bar\";\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileMessageTypeGroupsElementsNotSupported() throws Exception { public void testSaveProtoDeviceProfileMessageTypeGroupsElementsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message TestMessage {\n" + "message TestMessage {\n" +
" repeated group Result = 1 {\n" + " repeated group Result = 1 {\n" +
" optional string url = 2;\n" + " optional string url = 2;\n" +
" optional string title = 3;\n" + " optional string title = 3;\n" +
" repeated string snippets = 4;\n" + " repeated string snippets = 4;\n" +
" }\n" + " }\n" +
"}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileOneOfsGroupsElementsNotSupported() throws Exception { public void testSaveProtoDeviceProfileOneOfsGroupsElementsNotSupported() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message SampleMessage {\n" + "message SampleMessage {\n" +
" oneof test_oneof {\n" + " oneof test_oneof {\n" +
" string name = 1;\n" + " string name = 1;\n" +
" group Result = 2 {\n" + " group Result = 2 {\n" +
" \tstring url = 3;\n" + " \tstring url = 3;\n" +
" \tstring title = 4;\n" + " \tstring title = 4;\n" +
" \trepeated string snippets = 5;\n" + " \trepeated string snippets = 5;\n" +
" }\n" + " }\n" +
" }" + " }" +
"}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!"); "}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsField() throws Exception { public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsField() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message PostTelemetry {\n" + "message PostTelemetry {\n" +
" int64 ts = 1;\n" + " int64 ts = 1;\n" +
" Values values = 2;\n" + " Values values = 2;\n" +
" \n" + " \n" +
" message Values {\n" + " message Values {\n" +
" string key1 = 3;\n" + " string key1 = 3;\n" +
" bool key2 = 4;\n" + " bool key2 = 4;\n" +
" double key3 = 5;\n" + " double key3 = 5;\n" +
" int32 key4 = 6;\n" + " int32 key4 = 6;\n" +
" JsonObject key5 = 7;\n" + " JsonObject key5 = 7;\n" +
" }\n" + " }\n" +
" \n" + " \n" +
" message JsonObject {\n" + " message JsonObject {\n" +
" optional int32 someNumber = 8;\n" + " optional int32 someNumber = 8;\n" +
" repeated int32 someArray = 9;\n" + " repeated int32 someArray = 9;\n" +
" NestedJsonObject someNestedObject = 10;\n" + " NestedJsonObject someNestedObject = 10;\n" +
" message NestedJsonObject {\n" + " message NestedJsonObject {\n" +
" optional string key = 11;\n" + " optional string key = 11;\n" +
" }\n" + " }\n" +
" }\n" + " }\n" +
"}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid label. Field 'ts' should have optional keyword!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsDateType() throws Exception { public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsDateType() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message PostTelemetry {\n" + "message PostTelemetry {\n" +
" optional int32 ts = 1;\n" + " optional int32 ts = 1;\n" +
" Values values = 2;\n" + " Values values = 2;\n" +
" \n" + " \n" +
" message Values {\n" + " message Values {\n" +
" string key1 = 3;\n" + " string key1 = 3;\n" +
" bool key2 = 4;\n" + " bool key2 = 4;\n" +
" double key3 = 5;\n" + " double key3 = 5;\n" +
" int32 key4 = 6;\n" + " int32 key4 = 6;\n" +
" JsonObject key5 = 7;\n" + " JsonObject key5 = 7;\n" +
" }\n" + " }\n" +
" \n" + " \n" +
" message JsonObject {\n" + " message JsonObject {\n" +
" optional int32 someNumber = 8;\n" + " optional int32 someNumber = 8;\n" +
" }\n" + " }\n" +
"}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid data type. Only int64 type is supported!"); "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid data type. Only int64 type is supported!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaValuesDateType() throws Exception { public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaValuesDateType() throws Exception {
testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message PostTelemetry {\n" + "message PostTelemetry {\n" +
" optional int64 ts = 1;\n" + " optional int64 ts = 1;\n" +
" string values = 2;\n" + " string values = 2;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'values' has invalid data type. Only message type is supported!"); "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'values' has invalid data type. Only message type is supported!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodDateType() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodDateType() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional int32 method = 1;\n" + " optional int32 method = 1;\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid data type. Only string type is supported!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid data type. Only string type is supported!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdDateType() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdDateType() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string method = 1;\n" + " optional string method = 1;\n" +
" optional int64 requestId = 2;\n" + " optional int64 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid data type. Only int32 type is supported!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid data type. Only int32 type is supported!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodLabel() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodLabel() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" repeated string method = 1;\n" + " repeated string method = 1;\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid label!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid label!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdLabel() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdLabel() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string method = 1;\n" + " optional string method = 1;\n" +
" repeated int32 requestId = 2;\n" + " repeated int32 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid label!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid label!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaParamsLabel() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaParamsLabel() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string method = 1;\n" + " optional string method = 1;\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" repeated string params = 3;\n" + " repeated string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'params' has invalid label!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'params' has invalid label!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldsCount() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldsCount() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! RpcRequestMsg message should always contains 3 fields: method, requestId and params!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! RpcRequestMsg message should always contains 3 fields: method, requestId and params!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldMethodIsNoSet() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldMethodIsNoSet() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string methodName = 1;\n" + " optional string methodName = 1;\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: method!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: method!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldRequestIdIsNotSet() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldRequestIdIsNotSet() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string method = 1;\n" + " optional string method = 1;\n" +
" optional int32 requestIdentifier = 2;\n" + " optional int32 requestIdentifier = 2;\n" +
" optional string params = 3;\n" + " optional string params = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: requestId!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: requestId!");
} }
@Test @Test
public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldParamsIsNotSet() throws Exception { public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldParamsIsNotSet() throws Exception {
testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" +
"\n" + "\n" +
"package schemavalidation;\n" + "package schemavalidation;\n" +
"\n" + "\n" +
"message RpcRequestMsg {\n" + "message RpcRequestMsg {\n" +
" optional string method = 1;\n" + " optional string method = 1;\n" +
" optional int32 requestId = 2;\n" + " optional int32 requestId = 2;\n" +
" optional string parameters = 3;\n" + " optional string parameters = 3;\n" +
" \n" + " \n" +
"}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: params!"); "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: params!");
} }
@Test @Test
@ -1068,11 +1071,17 @@ public class DeviceProfileControllerTest extends AbstractControllerTest {
MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false); MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false);
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration); DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration);
Mockito.reset(tbClusterService, auditLogService); // The request may hit a transient TenantNotFoundException right after the @Before tenant creation
// if the tenant profile cache is not yet warmed up for the newly created tenant. Retry until the
doPost("/api/deviceProfile", deviceProfile) // request returns the expected 400 Bad Request for the invalid schema. Mockito.reset is inside the
.andExpect(status().isBadRequest()) // retry loop so the subsequent verify* assertions see only the invocations from the last attempt.
.andExpect(statusReason(containsString(errorMsg))); Awaitility.await().atMost(10, TimeUnit.SECONDS).pollInterval(500, TimeUnit.MILLISECONDS)
.ignoreExceptions().untilAsserted(() -> {
Mockito.reset(tbClusterService, auditLogService);
doPost("/api/deviceProfile", deviceProfile)
.andExpect(status().isBadRequest())
.andExpect(statusReason(containsString(errorMsg)));
});
testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(), testNotifyEntityEqualsOneTimeServiceNeverError(deviceProfile, savedTenant.getId(),
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg));

8
application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java

@ -20,7 +20,6 @@ import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.test.web.servlet.ResultMatcher;
import org.testcontainers.shaded.org.awaitility.Awaitility; import org.testcontainers.shaded.org.awaitility.Awaitility;
import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.Customer; import org.thingsboard.server.common.data.Customer;
@ -224,7 +223,12 @@ public class UserEdgeTest extends AbstractEdgeTest {
User savedUser = createUser(user, password); User savedUser = createUser(user, password);
Assert.assertTrue(edgeImitator.waitForMessages()); Assert.assertTrue(edgeImitator.waitForMessages());
Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size()); Assert.assertEquals(1, edgeImitator.findAllMessagesByType(UserUpdateMsg.class).size());
Assert.assertEquals(2, edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size()); // The initial USER ADDED edge event may bundle a UserCredentialsUpdateMsg when
// user activation completes before the event is processed, in addition to the 2
// messages from the CREDENTIALS_UPDATED events fired during activation. Accept 2 or 3.
int credMsgCount = edgeImitator.findAllMessagesByType(UserCredentialsUpdateMsg.class).size();
Assert.assertTrue("Expected 2 or 3 UserCredentialsUpdateMsg (ADDED/activation race), got " + credMsgCount,
credMsgCount == 2 || credMsgCount == 3);
UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg(); UserUpdateMsg userUpdateMsg = getLatestUserUpdateMsg();
User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true);

9
application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java

@ -15,6 +15,7 @@
*/ */
package org.thingsboard.server.service.resource; package org.thingsboard.server.service.resource;
import org.awaitility.Awaitility;
import org.junit.Test; import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.bean.override.mockito.MockitoSpyBean; import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
@ -29,6 +30,8 @@ import org.thingsboard.server.dao.resource.ResourceService;
import org.thingsboard.server.dao.resource.TbResourceDataCache; import org.thingsboard.server.dao.resource.TbResourceDataCache;
import org.thingsboard.server.dao.service.DaoSqlTest; import org.thingsboard.server.dao.service.DaoSqlTest;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.timeout;
@ -61,6 +64,8 @@ public class DefaultResourceDataCacheTest extends AbstractControllerTest {
TbResourceInfo savedResource = tbResourceService.save(resource); TbResourceInfo savedResource = tbResourceService.save(resource);
verify(resourceDataCache, timeout(2000).times(1)).evictResourceData(tenantId, savedResource.getId()); verify(resourceDataCache, timeout(2000).times(1)).evictResourceData(tenantId, savedResource.getId());
Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() ->
assertThat(resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get()).isNotNull());
TbResourceDataInfo cachedData = resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get(); TbResourceDataInfo cachedData = resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get();
assertThat(cachedData.getData()).isEqualTo(data); assertThat(cachedData.getData()).isEqualTo(data);
assertThat(JacksonUtil.treeToValue(cachedData.getDescriptor(), GeneralFileDescriptor.class)).isEqualTo(descriptor); assertThat(JacksonUtil.treeToValue(cachedData.getDescriptor(), GeneralFileDescriptor.class)).isEqualTo(descriptor);
@ -76,8 +81,8 @@ public class DefaultResourceDataCacheTest extends AbstractControllerTest {
TbResource resourceById = resourceService.findResourceById(tenantId, savedResource.getId()); TbResource resourceById = resourceService.findResourceById(tenantId, savedResource.getId());
tbResourceService.delete(resourceById, true, null); tbResourceService.delete(resourceById, true, null);
verify(resourceDataCache, timeout(2000).times(2)).evictResourceData(tenantId, savedResource.getId()); verify(resourceDataCache, timeout(2000).times(2)).evictResourceData(tenantId, savedResource.getId());
TbResourceDataInfo cachedDataAfterDeletion = resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get(); Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() ->
assertThat(cachedDataAfterDeletion).isEqualTo(null); assertThat(resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get()).isNull());
} }
} }

5
common/edqs/src/main/java/org/thingsboard/server/edqs/data/dp/StringDataPoint.java

@ -33,6 +33,11 @@ public class StringDataPoint extends AbstractDataPoint {
this.value = deduplicate ? TbStringPool.intern(value) : value; this.value = deduplicate ? TbStringPool.intern(value) : value;
} }
@Override
public boolean getBool() {
return Boolean.parseBoolean(value);
}
@Override @Override
public double getDouble() { public double getDouble() {
return Double.parseDouble(value); return Double.parseDouble(value);

47
dao/src/test/java/org/thingsboard/server/dao/TbTimescaleDBContainerProvider.java

@ -0,0 +1,47 @@
/**
* 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.dao;
import org.testcontainers.containers.JdbcDatabaseContainer;
import org.testcontainers.containers.TimescaleDBContainerProvider;
/**
* Extends the upstream {@link TimescaleDBContainerProvider} to disable the
* timescaledb-tune entrypoint script via NO_TS_TUNE=true.
*
* Works around a shell bug in /docker-entrypoint-initdb.d/001_timescaledb_tune.sh
* that crashes the container entrypoint on cgroup v2 hosts (including CI agents)
* when the kernel reports the 64-bit max for memory.max.
*
* Activated by the jdbc:tc:tbtimescaledb:<tag>:///... URL prefix
* registered via META-INF/services.
*/
public class TbTimescaleDBContainerProvider extends TimescaleDBContainerProvider {
private static final String NAME = "tbtimescaledb";
@Override
public boolean supports(String databaseType) {
return NAME.equals(databaseType);
}
@Override
public JdbcDatabaseContainer newInstance(String tag) {
JdbcDatabaseContainer container = super.newInstance(tag);
container.withEnv("NO_TS_TUNE", "true");
return container;
}
}

1
dao/src/test/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider

@ -0,0 +1 @@
org.thingsboard.server.dao.TbTimescaleDBContainerProvider

2
dao/src/test/resources/nosql-test.properties

@ -13,6 +13,6 @@ spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.ddl-auto=none
spring.datasource.username=postgres spring.datasource.username=postgres
spring.datasource.password=postgres spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:16.6:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb spring.datasource.url=jdbc:tc:postgresql:18:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize=16 spring.datasource.hikari.maximumPoolSize=16

2
dao/src/test/resources/sql-test.properties

@ -14,7 +14,7 @@ spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.ddl-auto=none
spring.datasource.username=postgres spring.datasource.username=postgres
spring.datasource.password=postgres spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:postgresql:16.6:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb spring.datasource.url=jdbc:tc:postgresql:18:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.PostgreSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize=16 spring.datasource.hikari.maximumPoolSize=16

2
dao/src/test/resources/timescale-test.properties

@ -13,6 +13,6 @@ spring.jpa.show-sql=false
spring.jpa.hibernate.ddl-auto=none spring.jpa.hibernate.ddl-auto=none
spring.datasource.username=postgres spring.datasource.username=postgres
spring.datasource.password=postgres spring.datasource.password=postgres
spring.datasource.url=jdbc:tc:timescaledb:latest-pg12:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.TimescaleSqlInitializer::initDb spring.datasource.url=jdbc:tc:tbtimescaledb:latest-pg18:///thingsboard?TC_DAEMON=true&TC_TMPFS=/testtmpfs:rw&?TC_INITFUNCTION=org.thingsboard.server.dao.TimescaleSqlInitializer::initDb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.hikari.maximumPoolSize = 50 spring.datasource.hikari.maximumPoolSize = 50

57
edqs/src/test/java/org/thingsboard/server/edqs/repo/DeviceTypeFilterTest.java

@ -19,15 +19,20 @@ import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.Device;
import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfile;
import org.thingsboard.server.common.data.DeviceProfileType; import org.thingsboard.server.common.data.DeviceProfileType;
import org.thingsboard.server.common.data.EntityType; import org.thingsboard.server.common.data.EntityType;
import org.thingsboard.server.common.data.edqs.AttributeKv;
import org.thingsboard.server.common.data.edqs.LatestTsKv; import org.thingsboard.server.common.data.edqs.LatestTsKv;
import org.thingsboard.server.common.data.id.DeviceId; import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.DeviceProfileId; import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.kv.BaseAttributeKvEntry;
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; import org.thingsboard.server.common.data.kv.BasicTsKvEntry;
import org.thingsboard.server.common.data.kv.BooleanDataEntry;
import org.thingsboard.server.common.data.kv.StringDataEntry; import org.thingsboard.server.common.data.kv.StringDataEntry;
import org.thingsboard.server.common.data.query.BooleanFilterPredicate;
import org.thingsboard.server.common.data.query.DeviceTypeFilter; import org.thingsboard.server.common.data.query.DeviceTypeFilter;
import org.thingsboard.server.common.data.query.EntityDataPageLink; import org.thingsboard.server.common.data.query.EntityDataPageLink;
import org.thingsboard.server.common.data.query.EntityDataQuery; import org.thingsboard.server.common.data.query.EntityDataQuery;
@ -39,8 +44,10 @@ import org.thingsboard.server.common.data.query.FilterPredicateValue;
import org.thingsboard.server.common.data.query.KeyFilter; import org.thingsboard.server.common.data.query.KeyFilter;
import org.thingsboard.server.common.data.query.StringFilterPredicate; import org.thingsboard.server.common.data.query.StringFilterPredicate;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.UUID; import java.util.UUID;
public class DeviceTypeFilterTest extends AbstractEDQTest { public class DeviceTypeFilterTest extends AbstractEDQTest {
@ -119,7 +126,50 @@ public class DeviceTypeFilterTest extends AbstractEDQTest {
Assert.assertEquals("42", first.getLatest().get(EntityKeyType.ENTITY_FIELD).get("createdTime").getValue()); Assert.assertEquals("42", first.getLatest().get(EntityKeyType.ENTITY_FIELD).get("createdTime").getValue());
} }
@Test
public void testFindDeviceByBooleanAttributeWithMixedTypes() {
DeviceId device1Id = createLoraDevice("LoRa-1");
DeviceId device2Id = createLoraDevice("LoRa-2");
DeviceId device3Id = createLoraDevice("LoRa-3");
long ts = System.currentTimeMillis();
addOrUpdate(new AttributeKv(device1Id, AttributeScope.SERVER_SCOPE,
new BaseAttributeKvEntry(new BooleanDataEntry("active", true), ts), 1L));
addOrUpdate(new AttributeKv(device2Id, AttributeScope.SERVER_SCOPE,
new BaseAttributeKvEntry(new BooleanDataEntry("active", false), ts), 1L));
addOrUpdate(new AttributeKv(device3Id, AttributeScope.SERVER_SCOPE,
new BaseAttributeKvEntry(new StringDataEntry("active", "true"), ts), 1L));
KeyFilter activeFilter = new KeyFilter();
activeFilter.setKey(new EntityKey(EntityKeyType.SERVER_ATTRIBUTE, "active"));
activeFilter.setValueType(EntityKeyValueType.BOOLEAN);
BooleanFilterPredicate predicate = new BooleanFilterPredicate();
predicate.setOperation(BooleanFilterPredicate.BooleanOperation.EQUAL);
predicate.setValue(FilterPredicateValue.fromBoolean(true));
activeFilter.setPredicate(predicate);
var result = repository.countEntitiesByQuery(tenantId, null,
getDeviceTypeQuery("LoRa", List.of(activeFilter)), false);
Assert.assertEquals(2, result);
}
private DeviceId createLoraDevice(String name) {
DeviceId deviceId = new DeviceId(UUID.randomUUID());
Device device = new Device();
device.setId(deviceId);
device.setTenantId(tenantId);
device.setDeviceProfileId(loraProfileId);
device.setName(name);
device.setCreatedTime(42L);
addOrUpdate(EntityType.DEVICE, device);
return deviceId;
}
private static EntityDataQuery getDeviceTypeQuery(String deviceType) { private static EntityDataQuery getDeviceTypeQuery(String deviceType) {
return getDeviceTypeQuery(deviceType, null);
}
private static EntityDataQuery getDeviceTypeQuery(String deviceType, List<KeyFilter> extraFilters) {
DeviceTypeFilter filter = new DeviceTypeFilter(); DeviceTypeFilter filter = new DeviceTypeFilter();
filter.setDeviceTypes(Collections.singletonList(deviceType)); filter.setDeviceTypes(Collections.singletonList(deviceType));
var pageLink = new EntityDataPageLink(20, 0, null, new EntityDataSortOrder(new EntityKey(EntityKeyType.TIME_SERIES, "state"), EntityDataSortOrder.Direction.DESC), false); var pageLink = new EntityDataPageLink(20, 0, null, new EntityDataSortOrder(new EntityKey(EntityKeyType.TIME_SERIES, "state"), EntityDataSortOrder.Direction.DESC), false);
@ -135,7 +185,12 @@ public class DeviceTypeFilterTest extends AbstractEDQTest {
nameFilter.setPredicate(predicate); nameFilter.setPredicate(predicate);
nameFilter.setValueType(EntityKeyValueType.STRING); nameFilter.setValueType(EntityKeyValueType.STRING);
return new EntityDataQuery(filter, pageLink, entityFields, latestValues, Arrays.asList(nameFilter)); List<KeyFilter> keyFilters = new ArrayList<>();
keyFilters.add(nameFilter);
if (extraFilters != null) {
keyFilters.addAll(extraFilters);
}
return new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters);
} }
} }

4
pom.xml

@ -620,7 +620,6 @@
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId> <artifactId>maven-assembly-plugin</artifactId>
<configuration> <configuration>
<skipAssembly>${pkg.skip.zip}</skipAssembly>
<finalName>${pkg.name}</finalName> <finalName>${pkg.name}</finalName>
<descriptors> <descriptors>
<descriptor>${main.dir}/packaging/${pkg.type}/assembly/windows.xml</descriptor> <descriptor>${main.dir}/packaging/${pkg.type}/assembly/windows.xml</descriptor>
@ -633,6 +632,9 @@
<goals> <goals>
<goal>single</goal> <goal>single</goal>
</goals> </goals>
<configuration>
<skipAssembly>${pkg.skip.zip}</skipAssembly>
</configuration>
</execution> </execution>
</executions> </executions>
</plugin> </plugin>

Loading…
Cancel
Save