diff --git a/application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java b/application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java index 63f05710ae..2099f405f0 100644 --- a/application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java +++ b/application/src/test/java/org/thingsboard/server/client/Oauth2ApiClientTest.java @@ -90,7 +90,7 @@ public class Oauth2ApiClientTest extends AbstractApiClientTest { } // list tenant OAuth2 client infos - PageDataOAuth2ClientInfo clientInfos = client.findTenantOAuth2ClientInfos(100, 0, + PageDataOAuth2ClientInfo clientInfos = client.findOAuth2ClientInfos(100, 0, TEST_PREFIX + "OAuth2_" + timestamp, null, null); assertNotNull(clientInfos); assertEquals(5, clientInfos.getData().size()); @@ -130,7 +130,7 @@ public class Oauth2ApiClientTest extends AbstractApiClientTest { client.getOAuth2ClientById(clientToDeleteId) ); - PageDataOAuth2ClientInfo clientsAfterDelete = client.findTenantOAuth2ClientInfos(100, 0, + PageDataOAuth2ClientInfo clientsAfterDelete = client.findOAuth2ClientInfos(100, 0, TEST_PREFIX + "OAuth2_" + timestamp, null, null); assertEquals(4, clientsAfterDelete.getData().size()); } diff --git a/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java b/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java index 3966bbb97a..0ee1a65cd8 100644 --- a/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java +++ b/application/src/test/java/org/thingsboard/server/controller/DeviceProfileControllerTest.java @@ -16,6 +16,7 @@ package org.thingsboard.server.controller; import com.fasterxml.jackson.core.type.TypeReference; +import org.awaitility.Awaitility; import org.junit.After; import org.junit.Assert; 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.security.Authority; import org.thingsboard.server.dao.device.DeviceProfileDao; -import org.thingsboard.server.exception.DataValidationException; import org.thingsboard.server.dao.service.DaoSqlTest; +import org.thingsboard.server.exception.DataValidationException; import java.util.ArrayList; import java.util.Arrays; @@ -62,6 +63,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.UUID; +import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; @@ -93,6 +95,7 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { public DeviceProfileDao deviceProfileDao(DeviceProfileDao deviceProfileDao) { return Mockito.mock(DeviceProfileDao.class, AdditionalAnswers.delegatesTo(deviceProfileDao)); } + } @Before @@ -640,364 +643,364 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { @Test public void testSaveProtoDeviceProfileWithInvalidProtoFile() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SchemaValidationTest {\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"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SchemaValidationTest {\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"); } @Test public void testSaveProtoDeviceProfileWithInvalidProtoSyntax() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto2\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SchemaValidationTest {\n" + - " required int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SchemaValidationTest {\n" + + " required int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid schema syntax: proto2 for attributes proto schema provided! Only proto3 allowed!"); } @Test public void testSaveProtoDeviceProfileOptionsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "option java_package = \"com.test.schemavalidation\";\n" + - "option java_multiple_files = true;\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SchemaValidationTest {\n" + - " optional int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!"); + "\n" + + "option java_package = \"com.test.schemavalidation\";\n" + + "option java_multiple_files = true;\n" + + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SchemaValidationTest {\n" + + " optional int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema options don't support!"); } @Test public void testSaveProtoDeviceProfilePublicImportsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "import public \"oldschema.proto\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SchemaValidationTest {\n" + - " optional int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!"); + "\n" + + "import public \"oldschema.proto\";\n" + + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SchemaValidationTest {\n" + + " optional int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema public imports don't support!"); } @Test public void testSaveProtoDeviceProfileImportsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "import \"oldschema.proto\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SchemaValidationTest {\n" + - " optional int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!"); + "\n" + + "import \"oldschema.proto\";\n" + + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SchemaValidationTest {\n" + + " optional int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema imports don't support!"); } @Test public void testSaveProtoDeviceProfileExtendDeclarationsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "extend google.protobuf.MethodOptions {\n" + - " MyMessage my_method_option = 50007;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "extend google.protobuf.MethodOptions {\n" + + " MyMessage my_method_option = 50007;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Schema extend declarations don't support!"); } @Test public void testSaveProtoDeviceProfileEnumOptionsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "enum testEnum {\n" + - " option allow_alias = true;\n" + - " DEFAULT = 0;\n" + - " STARTED = 1;\n" + - " RUNNING = 2;\n" + - "}\n" + - "\n" + - "message testMessage {\n" + - " optional int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "enum testEnum {\n" + + " option allow_alias = true;\n" + + " DEFAULT = 0;\n" + + " STARTED = 1;\n" + + " RUNNING = 2;\n" + + "}\n" + + "\n" + + "message testMessage {\n" + + " optional int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Enum definitions options are not supported!"); } @Test public void testSaveProtoDeviceProfileNoOneMessageTypeExists() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "enum testEnum {\n" + - " DEFAULT = 0;\n" + - " STARTED = 1;\n" + - " RUNNING = 2;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "enum testEnum {\n" + + " DEFAULT = 0;\n" + + " STARTED = 1;\n" + + " RUNNING = 2;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! At least one Message definition should exists!"); } @Test public void testSaveProtoDeviceProfileMessageTypeOptionsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message testMessage {\n" + - " option allow_alias = true;\n" + - " optional int32 parameter = 1;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message testMessage {\n" + + " option allow_alias = true;\n" + + " optional int32 parameter = 1;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition options don't support!"); } @Test public void testSaveProtoDeviceProfileMessageTypeExtensionsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message TestMessage {\n" + - " extensions 100 to 199;\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message TestMessage {\n" + + " extensions 100 to 199;\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition extensions don't support!"); } @Test public void testSaveProtoDeviceProfileMessageTypeReservedElementsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message Foo {\n" + - " reserved 2, 15, 9 to 11;\n" + - " reserved \"foo\", \"bar\";\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message Foo {\n" + + " reserved 2, 15, 9 to 11;\n" + + " reserved \"foo\", \"bar\";\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition reserved elements don't support!"); } @Test public void testSaveProtoDeviceProfileMessageTypeGroupsElementsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message TestMessage {\n" + - " repeated group Result = 1 {\n" + - " optional string url = 2;\n" + - " optional string title = 3;\n" + - " repeated string snippets = 4;\n" + - " }\n" + - "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message TestMessage {\n" + + " repeated group Result = 1 {\n" + + " optional string url = 2;\n" + + " optional string title = 3;\n" + + " repeated string snippets = 4;\n" + + " }\n" + + "}", "[Transport Configuration] invalid attributes proto schema provided! Message definition groups don't support!"); } @Test public void testSaveProtoDeviceProfileOneOfsGroupsElementsNotSupported() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax = \"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message SampleMessage {\n" + - " oneof test_oneof {\n" + - " string name = 1;\n" + - " group Result = 2 {\n" + - " \tstring url = 3;\n" + - " \tstring title = 4;\n" + - " \trepeated string snippets = 5;\n" + - " }\n" + - " }" + - "}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message SampleMessage {\n" + + " oneof test_oneof {\n" + + " string name = 1;\n" + + " group Result = 2 {\n" + + " \tstring url = 3;\n" + + " \tstring title = 4;\n" + + " \trepeated string snippets = 5;\n" + + " }\n" + + " }" + + "}", "[Transport Configuration] invalid attributes proto schema provided! OneOf definition groups don't support!"); } @Test public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsField() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message PostTelemetry {\n" + - " int64 ts = 1;\n" + - " Values values = 2;\n" + - " \n" + - " message Values {\n" + - " string key1 = 3;\n" + - " bool key2 = 4;\n" + - " double key3 = 5;\n" + - " int32 key4 = 6;\n" + - " JsonObject key5 = 7;\n" + - " }\n" + - " \n" + - " message JsonObject {\n" + - " optional int32 someNumber = 8;\n" + - " repeated int32 someArray = 9;\n" + - " NestedJsonObject someNestedObject = 10;\n" + - " message NestedJsonObject {\n" + - " optional string key = 11;\n" + - " }\n" + - " }\n" + - "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message PostTelemetry {\n" + + " int64 ts = 1;\n" + + " Values values = 2;\n" + + " \n" + + " message Values {\n" + + " string key1 = 3;\n" + + " bool key2 = 4;\n" + + " double key3 = 5;\n" + + " int32 key4 = 6;\n" + + " JsonObject key5 = 7;\n" + + " }\n" + + " \n" + + " message JsonObject {\n" + + " optional int32 someNumber = 8;\n" + + " repeated int32 someArray = 9;\n" + + " NestedJsonObject someNestedObject = 10;\n" + + " message NestedJsonObject {\n" + + " optional string key = 11;\n" + + " }\n" + + " }\n" + + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid label. Field 'ts' should have optional keyword!"); } @Test public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaTsDateType() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message PostTelemetry {\n" + - " optional int32 ts = 1;\n" + - " Values values = 2;\n" + - " \n" + - " message Values {\n" + - " string key1 = 3;\n" + - " bool key2 = 4;\n" + - " double key3 = 5;\n" + - " int32 key4 = 6;\n" + - " JsonObject key5 = 7;\n" + - " }\n" + - " \n" + - " message JsonObject {\n" + - " optional int32 someNumber = 8;\n" + - " }\n" + - "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid data type. Only int64 type is supported!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message PostTelemetry {\n" + + " optional int32 ts = 1;\n" + + " Values values = 2;\n" + + " \n" + + " message Values {\n" + + " string key1 = 3;\n" + + " bool key2 = 4;\n" + + " double key3 = 5;\n" + + " int32 key4 = 6;\n" + + " JsonObject key5 = 7;\n" + + " }\n" + + " \n" + + " message JsonObject {\n" + + " optional int32 someNumber = 8;\n" + + " }\n" + + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'ts' has invalid data type. Only int64 type is supported!"); } @Test public void testSaveProtoDeviceProfileWithInvalidTelemetrySchemaValuesDateType() throws Exception { testSaveDeviceProfileWithInvalidProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message PostTelemetry {\n" + - " optional int64 ts = 1;\n" + - " string values = 2;\n" + - " \n" + - "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'values' has invalid data type. Only message type is supported!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message PostTelemetry {\n" + + " optional int64 ts = 1;\n" + + " string values = 2;\n" + + " \n" + + "}", "[Transport Configuration] invalid telemetry proto schema provided! Field 'values' has invalid data type. Only message type is supported!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodDateType() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional int32 method = 1;\n" + - " optional int32 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid data type. Only string type is supported!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional int32 method = 1;\n" + + " optional int32 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid data type. Only string type is supported!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdDateType() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string method = 1;\n" + - " optional int64 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid data type. Only int32 type is supported!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string method = 1;\n" + + " optional int64 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid data type. Only int32 type is supported!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaMethodLabel() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " repeated string method = 1;\n" + - " optional int32 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid label!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " repeated string method = 1;\n" + + " optional int32 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'method' has invalid label!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaRequestIdLabel() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string method = 1;\n" + - " repeated int32 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid label!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string method = 1;\n" + + " repeated int32 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'requestId' has invalid label!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaParamsLabel() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string method = 1;\n" + - " optional int32 requestId = 2;\n" + - " repeated string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'params' has invalid label!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string method = 1;\n" + + " optional int32 requestId = 2;\n" + + " repeated string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Field 'params' has invalid label!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldsCount() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional int32 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! RpcRequestMsg message should always contains 3 fields: method, requestId and params!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional int32 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! RpcRequestMsg message should always contains 3 fields: method, requestId and params!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldMethodIsNoSet() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string methodName = 1;\n" + - " optional int32 requestId = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: method!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string methodName = 1;\n" + + " optional int32 requestId = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: method!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldRequestIdIsNotSet() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string method = 1;\n" + - " optional int32 requestIdentifier = 2;\n" + - " optional string params = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: requestId!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string method = 1;\n" + + " optional int32 requestIdentifier = 2;\n" + + " optional string params = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: requestId!"); } @Test public void testSaveProtoDeviceProfileWithInvalidRpcRequestSchemaFieldParamsIsNotSet() throws Exception { testSaveDeviceProfileWithInvalidRpcRequestProtoSchema("syntax =\"proto3\";\n" + - "\n" + - "package schemavalidation;\n" + - "\n" + - "message RpcRequestMsg {\n" + - " optional string method = 1;\n" + - " optional int32 requestId = 2;\n" + - " optional string parameters = 3;\n" + - " \n" + - "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: params!"); + "\n" + + "package schemavalidation;\n" + + "\n" + + "message RpcRequestMsg {\n" + + " optional string method = 1;\n" + + " optional int32 requestId = 2;\n" + + " optional string parameters = 3;\n" + + " \n" + + "}", "[Transport Configuration] invalid rpc request proto schema provided! Failed to get field descriptor for field: params!"); } @Test @@ -1068,11 +1071,17 @@ public class DeviceProfileControllerTest extends AbstractControllerTest { MqttDeviceProfileTransportConfiguration mqttDeviceProfileTransportConfiguration = this.createMqttDeviceProfileTransportConfiguration(protoTransportPayloadConfiguration, false); DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile", mqttDeviceProfileTransportConfiguration); - Mockito.reset(tbClusterService, auditLogService); - - doPost("/api/deviceProfile", deviceProfile) - .andExpect(status().isBadRequest()) - .andExpect(statusReason(containsString(errorMsg))); + // 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 + // request returns the expected 400 Bad Request for the invalid schema. Mockito.reset is inside the + // retry loop so the subsequent verify* assertions see only the invocations from the last attempt. + 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(), tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(errorMsg)); diff --git a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java index f344335748..91dd0669b1 100644 --- a/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java +++ b/application/src/test/java/org/thingsboard/server/edge/UserEdgeTest.java @@ -20,7 +20,6 @@ import org.junit.Assert; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.test.web.servlet.ResultMatcher; import org.testcontainers.shaded.org.awaitility.Awaitility; import org.thingsboard.common.util.JacksonUtil; import org.thingsboard.server.common.data.Customer; @@ -224,7 +223,12 @@ public class UserEdgeTest extends AbstractEdgeTest { User savedUser = createUser(user, password); Assert.assertTrue(edgeImitator.waitForMessages()); 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(); User userMsg = JacksonUtil.fromString(userUpdateMsg.getEntity(), User.class, true); diff --git a/application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java b/application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java index c227ec76aa..d33f1755d5 100644 --- a/application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java +++ b/application/src/test/java/org/thingsboard/server/service/resource/DefaultResourceDataCacheTest.java @@ -15,6 +15,7 @@ */ package org.thingsboard.server.service.resource; +import org.awaitility.Awaitility; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; 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.service.DaoSqlTest; +import java.util.concurrent.TimeUnit; + import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.timeout; @@ -61,6 +64,8 @@ public class DefaultResourceDataCacheTest extends AbstractControllerTest { TbResourceInfo savedResource = tbResourceService.save(resource); 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(); assertThat(cachedData.getData()).isEqualTo(data); 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()); tbResourceService.delete(resourceById, true, null); verify(resourceDataCache, timeout(2000).times(2)).evictResourceData(tenantId, savedResource.getId()); - TbResourceDataInfo cachedDataAfterDeletion = resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get(); - assertThat(cachedDataAfterDeletion).isEqualTo(null); + Awaitility.await().atMost(2, TimeUnit.SECONDS).untilAsserted(() -> + assertThat(resourceDataCache.getResourceDataInfoAsync(tenantId, savedResource.getId()).get()).isNull()); } } diff --git a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/dp/StringDataPoint.java b/common/edqs/src/main/java/org/thingsboard/server/edqs/data/dp/StringDataPoint.java index 8ae3499d63..2d9e77633d 100644 --- a/common/edqs/src/main/java/org/thingsboard/server/edqs/data/dp/StringDataPoint.java +++ b/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; } + @Override + public boolean getBool() { + return Boolean.parseBoolean(value); + } + @Override public double getDouble() { return Double.parseDouble(value); diff --git a/dao/src/test/java/org/thingsboard/server/dao/TbTimescaleDBContainerProvider.java b/dao/src/test/java/org/thingsboard/server/dao/TbTimescaleDBContainerProvider.java new file mode 100644 index 0000000000..8afcaa1c22 --- /dev/null +++ b/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; + } +} diff --git a/dao/src/test/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider b/dao/src/test/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider new file mode 100644 index 0000000000..ab36744aa9 --- /dev/null +++ b/dao/src/test/resources/META-INF/services/org.testcontainers.containers.JdbcDatabaseContainerProvider @@ -0,0 +1 @@ +org.thingsboard.server.dao.TbTimescaleDBContainerProvider diff --git a/dao/src/test/resources/nosql-test.properties b/dao/src/test/resources/nosql-test.properties index b688c3c40f..921aebd5fe 100644 --- a/dao/src/test/resources/nosql-test.properties +++ b/dao/src/test/resources/nosql-test.properties @@ -13,6 +13,6 @@ spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none spring.datasource.username=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.hikari.maximumPoolSize=16 diff --git a/dao/src/test/resources/sql-test.properties b/dao/src/test/resources/sql-test.properties index e3f4861aa9..0639c461a3 100644 --- a/dao/src/test/resources/sql-test.properties +++ b/dao/src/test/resources/sql-test.properties @@ -14,7 +14,7 @@ spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none spring.datasource.username=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.hikari.maximumPoolSize=16 diff --git a/dao/src/test/resources/timescale-test.properties b/dao/src/test/resources/timescale-test.properties index 2c5552cb75..e0c0bef25e 100644 --- a/dao/src/test/resources/timescale-test.properties +++ b/dao/src/test/resources/timescale-test.properties @@ -13,6 +13,6 @@ spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=none spring.datasource.username=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.hikari.maximumPoolSize = 50 diff --git a/edqs/src/test/java/org/thingsboard/server/edqs/repo/DeviceTypeFilterTest.java b/edqs/src/test/java/org/thingsboard/server/edqs/repo/DeviceTypeFilterTest.java index 5d4905f0ef..11423d7484 100644 --- a/edqs/src/test/java/org/thingsboard/server/edqs/repo/DeviceTypeFilterTest.java +++ b/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.Before; import org.junit.Test; +import org.thingsboard.server.common.data.AttributeScope; import org.thingsboard.server.common.data.Device; import org.thingsboard.server.common.data.DeviceProfile; import org.thingsboard.server.common.data.DeviceProfileType; 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.id.DeviceId; 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.BooleanDataEntry; 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.EntityDataPageLink; 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.StringFilterPredicate; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; import java.util.UUID; 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()); } + @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) { + return getDeviceTypeQuery(deviceType, null); + } + + private static EntityDataQuery getDeviceTypeQuery(String deviceType, List extraFilters) { DeviceTypeFilter filter = new DeviceTypeFilter(); filter.setDeviceTypes(Collections.singletonList(deviceType)); 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.setValueType(EntityKeyValueType.STRING); - return new EntityDataQuery(filter, pageLink, entityFields, latestValues, Arrays.asList(nameFilter)); + List keyFilters = new ArrayList<>(); + keyFilters.add(nameFilter); + if (extraFilters != null) { + keyFilters.addAll(extraFilters); + } + return new EntityDataQuery(filter, pageLink, entityFields, latestValues, keyFilters); } } diff --git a/pom.xml b/pom.xml index a5f5de166f..653b410d76 100755 --- a/pom.xml +++ b/pom.xml @@ -620,7 +620,6 @@ org.apache.maven.plugins maven-assembly-plugin - ${pkg.skip.zip} ${pkg.name} ${main.dir}/packaging/${pkg.type}/assembly/windows.xml @@ -633,6 +632,9 @@ single + + ${pkg.skip.zip} +