From ad511e935539e41a897d1455804e0e974b5a025a Mon Sep 17 00:00:00 2001 From: dshvaika Date: Wed, 13 Aug 2025 13:19:02 +0300 Subject: [PATCH] Added test for RelationQueryDynamicSourceConfiguration class --- ...lationQueryDynamicSourceConfiguration.java | 3 +- ...onQueryDynamicSourceConfigurationTest.java | 202 ++++++++++++++++++ 2 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfigurationTest.java diff --git a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfiguration.java b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfiguration.java index fdf8815591..fb36417a35 100644 --- a/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfiguration.java +++ b/common/data/src/main/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfiguration.java @@ -17,6 +17,7 @@ package org.thingsboard.server.common.data.cf.configuration; import lombok.Data; import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.StringUtils; import org.thingsboard.server.common.data.id.EntityId; import org.thingsboard.server.common.data.relation.EntityRelation; import org.thingsboard.server.common.data.relation.EntityRelationsQuery; @@ -50,7 +51,7 @@ public class RelationQueryDynamicSourceConfiguration implements CfArgumentDynami if (direction == null) { throw new IllegalArgumentException("Relation query dynamic source configuration direction must be specified!"); } - if (relationType == null) { + if (StringUtils.isBlank(relationType)) { throw new IllegalArgumentException("Relation query dynamic source configuration relation type must be specified!"); } } diff --git a/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfigurationTest.java b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfigurationTest.java new file mode 100644 index 0000000000..3ab45858ab --- /dev/null +++ b/common/data/src/test/java/org/thingsboard/server/common/data/cf/configuration/RelationQueryDynamicSourceConfigurationTest.java @@ -0,0 +1,202 @@ +/** + * Copyright © 2016-2025 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.common.data.cf.configuration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.thingsboard.server.common.data.EntityType; +import org.thingsboard.server.common.data.id.EntityId; +import org.thingsboard.server.common.data.relation.EntityRelation; +import org.thingsboard.server.common.data.relation.EntitySearchDirection; +import org.thingsboard.server.common.data.relation.RelationEntityTypeFilter; +import org.thingsboard.server.common.data.relation.RelationsSearchParameters; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +public class RelationQueryDynamicSourceConfigurationTest { + + @Mock + EntityId rootEntityId; + + @Mock + EntityRelation rel1; + @Mock + EntityRelation rel2; + + @Test + void typeShouldBeRelationQuery() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + assertThat(cfg.getType()).isEqualTo(CFArgumentDynamicSourceType.RELATION_QUERY); + } + + @Test + void validateShouldThrowWhenMaxLevelGreaterThanTwo() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(3); + cfg.setDirection(EntitySearchDirection.FROM); + cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation query dynamic source configuration max relation level can't be greater than 2!"); + } + + @Test + void validateShouldThrowWhenDirectionIsNull() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(1); + cfg.setDirection(null); + cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation query dynamic source configuration direction must be specified!"); + } + + @ParameterizedTest + @ValueSource(strings = {" "}) + @NullAndEmptySource + void validateShouldThrowWhenRelationTypeIsNull(String relationType) { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(1); + cfg.setDirection(EntitySearchDirection.TO); + cfg.setRelationType(relationType); + + assertThatThrownBy(cfg::validate) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Relation query dynamic source configuration relation type must be specified!"); + } + + @ParameterizedTest + @NullAndEmptySource + void isSimpleRelationTrueWhenLevelIsOneAndEntityTypesEmptyOrNull(List entityTypes) { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(1); + cfg.setEntityTypes(entityTypes); + + assertThat(cfg.isSimpleRelation()).isTrue(); + } + + @Test + void isSimpleRelationFalseWhenMaxLevelNotOne() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(2); + cfg.setEntityTypes(null); + + assertThat(cfg.isSimpleRelation()).isFalse(); + } + + @Test + void isSimpleRelationFalseWhenEntityTypesProvided() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(1); + cfg.setEntityTypes(List.of(EntityType.DEVICE)); + + assertThat(cfg.isSimpleRelation()).isFalse(); + } + + @ParameterizedTest + @NullAndEmptySource + void toEntityRelationsQueryShouldThrowForSimpleRelation(List entityTypes) { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(1); + cfg.setFetchLastLevelOnly(false); + cfg.setDirection(EntitySearchDirection.FROM); + cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + cfg.setEntityTypes(entityTypes); + + assertThatThrownBy(() -> cfg.toEntityRelationsQuery(rootEntityId)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("Entity relations query can't be created for a simple relation!"); + } + + @Test + void toEntityRelationsQueryShouldBuildQueryForNonSimpleRelation() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(2); + cfg.setFetchLastLevelOnly(true); + cfg.setDirection(EntitySearchDirection.TO); + cfg.setRelationType(EntityRelation.MANAGES_TYPE); + cfg.setEntityTypes(List.of(EntityType.DEVICE, EntityType.ASSET)); + + var query = cfg.toEntityRelationsQuery(rootEntityId); + + assertThat(query).isNotNull(); + RelationsSearchParameters params = query.getParameters(); + assertThat(params).isNotNull(); + assertThat(params.getRootId()).isEqualTo(rootEntityId.getId()); + assertThat(params.getDirection()).isEqualTo(EntitySearchDirection.TO); + assertThat(params.getMaxLevel()).isEqualTo(2); + assertThat(params.isFetchLastLevelOnly()).isTrue(); + + assertThat(query.getFilters()).hasSize(1); + assertThat(query.getFilters().get(0)).isInstanceOf(RelationEntityTypeFilter.class); + RelationEntityTypeFilter filter = query.getFilters().get(0); + assertThat(filter.getRelationType()).isEqualTo(EntityRelation.MANAGES_TYPE); + assertThat(filter.getEntityTypes()).containsExactly(EntityType.DEVICE, EntityType.ASSET); + } + + @Test + void resolveEntityIdsFromDirectionFROMReturnsToIds() { + when(rel1.getTo()).thenReturn(mock(EntityId.class)); + when(rel2.getTo()).thenReturn(mock(EntityId.class)); + + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setDirection(EntitySearchDirection.FROM); + + var out = cfg.resolveEntityIds(List.of(rel1, rel2)); + + assertThat(out).containsExactly(rel1.getTo(), rel2.getTo()); + } + + @Test + void resolveEntityIdsFromDirectionTOReturnsFromIds() { + when(rel1.getFrom()).thenReturn(mock(EntityId.class)); + when(rel2.getFrom()).thenReturn(mock(EntityId.class)); + + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setDirection(EntitySearchDirection.TO); + + var out = cfg.resolveEntityIds(List.of(rel1, rel2)); + + assertThat(out).containsExactly(rel1.getFrom(), rel2.getFrom()); + } + + @Test + void validateShouldPassForValidConfig() { + var cfg = new RelationQueryDynamicSourceConfiguration(); + cfg.setMaxLevel(2); + cfg.setFetchLastLevelOnly(false); + cfg.setDirection(EntitySearchDirection.FROM); + cfg.setRelationType(EntityRelation.CONTAINS_TYPE); + cfg.setEntityTypes(List.of(EntityType.DEVICE)); + + assertThatCode(cfg::validate).doesNotThrowAnyException(); + } + +}