added tests for the device attributes node
This commit is contained in:
		
							parent
							
								
									633411f9e1
								
							
						
					
					
						commit
						83308a68d6
					
				@ -15,20 +15,21 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.metadata;
 | 
			
		||||
 | 
			
		||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import org.junit.jupiter.api.AfterEach;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.junit.jupiter.api.Assertions;
 | 
			
		||||
import org.junit.jupiter.api.extension.ExtendWith;
 | 
			
		||||
import org.junit.jupiter.params.provider.Arguments;
 | 
			
		||||
import org.mockito.ArgumentCaptor;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.Spy;
 | 
			
		||||
import org.mockito.junit.jupiter.MockitoExtension;
 | 
			
		||||
import org.thingsboard.common.util.AbstractListeningExecutor;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.util.TbMsgSource;
 | 
			
		||||
@ -43,7 +44,6 @@ import org.thingsboard.server.common.data.kv.JsonDataEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.StringDataEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.kv.TsKvEntry;
 | 
			
		||||
import org.thingsboard.server.common.data.msg.TbMsgType;
 | 
			
		||||
import org.thingsboard.server.common.data.util.TbPair;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.dao.attributes.AttributesService;
 | 
			
		||||
@ -51,25 +51,26 @@ import org.thingsboard.server.dao.timeseries.TimeseriesService;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.stream.Collectors;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertEquals;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertTrue;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertThrows;
 | 
			
		||||
import static org.junit.jupiter.api.Assertions.assertTrue;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.Mockito.lenient;
 | 
			
		||||
import static org.mockito.Mockito.never;
 | 
			
		||||
import static org.mockito.Mockito.timeout;
 | 
			
		||||
import static org.mockito.Mockito.verify;
 | 
			
		||||
import static org.mockito.Mockito.when;
 | 
			
		||||
 | 
			
		||||
@ExtendWith(MockitoExtension.class)
 | 
			
		||||
public class TbGetAttributesNodeTest {
 | 
			
		||||
public class TbGetAttributesNodeTest extends AbstractRuleNodeUpgradeTest {
 | 
			
		||||
 | 
			
		||||
    private static final EntityId ORIGINATOR = new DeviceId(Uuids.timeBased());
 | 
			
		||||
    private static final TenantId TENANT_ID = TenantId.fromUUID(Uuids.timeBased());
 | 
			
		||||
    private final EntityId ORIGINATOR_ID = new DeviceId(UUID.fromString("965f2975-787a-4f21-87e6-9aa4738186ff"));
 | 
			
		||||
    private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("befd3239-79b8-4263-a8d1-95b69f44f798"));
 | 
			
		||||
    private AbstractListeningExecutor dbExecutor;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
@ -84,6 +85,8 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    private List<String> sharedAttributes;
 | 
			
		||||
    private List<String> tsKeys;
 | 
			
		||||
    private long ts;
 | 
			
		||||
 | 
			
		||||
    @Spy
 | 
			
		||||
    private TbGetAttributesNode node;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
@ -107,16 +110,16 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
        tsKeys = List.of("temperature", "humidity", "unknown");
 | 
			
		||||
        ts = System.currentTimeMillis();
 | 
			
		||||
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.CLIENT_SCOPE, clientAttributes))
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.CLIENT_SCOPE, clientAttributes))
 | 
			
		||||
                .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(clientAttributes, ts)));
 | 
			
		||||
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SERVER_SCOPE, serverAttributes))
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.SERVER_SCOPE, serverAttributes))
 | 
			
		||||
                .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(serverAttributes, ts)));
 | 
			
		||||
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR, AttributeScope.SHARED_SCOPE, sharedAttributes))
 | 
			
		||||
        lenient().when(attributesServiceMock.find(TENANT_ID, ORIGINATOR_ID, AttributeScope.SHARED_SCOPE, sharedAttributes))
 | 
			
		||||
                .thenReturn(Futures.immediateFuture(getListAttributeKvEntry(sharedAttributes, ts)));
 | 
			
		||||
 | 
			
		||||
        lenient().when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR, tsKeys))
 | 
			
		||||
        lenient().when(timeseriesServiceMock.findLatest(TENANT_ID, ORIGINATOR_ID, tsKeys))
 | 
			
		||||
                .thenReturn(Futures.immediateFuture(getListTsKvEntry(tsKeys, ts)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -129,7 +132,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.METADATA, false, false);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -148,7 +151,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchLatestTimeseriesToMetadata_whenOnMsg_thenShouldTellSuccess() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.METADATA, true, false);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -167,7 +170,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchAttributesToData_whenOnMsg_thenShouldTellSuccess() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.DATA, false, false);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -186,7 +189,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellSuccess() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.DATA, true, false);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -205,7 +208,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchAttributesToMetadata_whenOnMsg_thenShouldTellFailure() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.METADATA, false, true);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -224,7 +227,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchLatestTimeseriesToData_whenOnMsg_thenShouldTellFailure() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.DATA, true, true);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR);
 | 
			
		||||
        var msg = getTbMsg(ORIGINATOR_ID);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
@ -243,7 +246,7 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
    public void givenFetchLatestTimeseriesToDataAndDataIsNotJsonObject_whenOnMsg_thenException() throws Exception {
 | 
			
		||||
        // GIVEN
 | 
			
		||||
        node = initNode(TbMsgSource.DATA, true, true);
 | 
			
		||||
        var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY);
 | 
			
		||||
        var msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ORIGINATOR_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_ARRAY);
 | 
			
		||||
 | 
			
		||||
        // WHEN
 | 
			
		||||
        var exception = assertThrows(IllegalArgumentException.class, () -> node.onMsg(ctxMock, msg));
 | 
			
		||||
@ -253,56 +256,6 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
        assertThat(exception.getMessage()).isEqualTo("Message body is not an object!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetAttributesNode();
 | 
			
		||||
        String oldConfig = "{\"fetchToData\":false," +
 | 
			
		||||
                "\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfigWithNoFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetAttributesNode();
 | 
			
		||||
        String oldConfig = "{\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfigWithNullFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetAttributesNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetAttributesNode();
 | 
			
		||||
        String oldConfig = "{\"fetchToData\":null," +
 | 
			
		||||
                "\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private TbMsg checkMsg(boolean checkSuccess) {
 | 
			
		||||
        var msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
 | 
			
		||||
        if (checkSuccess) {
 | 
			
		||||
@ -421,4 +374,117 @@ public class TbGetAttributesNodeTest {
 | 
			
		||||
        return kvEntriesList;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() {
 | 
			
		||||
        return Stream.of(
 | 
			
		||||
                // config for version 1 with upgrade from version 0
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "fetchToData":false,
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],
 | 
			
		||||
                                "serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 0 (old config with no fetchToData property)
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],"serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 0 (old config with null fetchToData property)
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "fetchToData":null,
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],
 | 
			
		||||
                                "serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 1
 | 
			
		||||
                Arguments.of(1,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        false,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected TbNode getTestNode() {
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,68 +15,236 @@
 | 
			
		||||
 */
 | 
			
		||||
package org.thingsboard.rule.engine.metadata;
 | 
			
		||||
 | 
			
		||||
import com.fasterxml.jackson.databind.JsonNode;
 | 
			
		||||
import com.google.common.util.concurrent.Futures;
 | 
			
		||||
import org.junit.jupiter.api.BeforeEach;
 | 
			
		||||
import org.junit.jupiter.api.Test;
 | 
			
		||||
import org.junit.jupiter.api.Assertions;
 | 
			
		||||
import org.junit.jupiter.api.extension.ExtendWith;
 | 
			
		||||
import org.junit.jupiter.params.provider.Arguments;
 | 
			
		||||
import org.mockito.ArgumentCaptor;
 | 
			
		||||
import org.mockito.Mock;
 | 
			
		||||
import org.mockito.junit.jupiter.MockitoExtension;
 | 
			
		||||
import org.thingsboard.common.util.JacksonUtil;
 | 
			
		||||
import org.thingsboard.server.common.data.util.TbPair;
 | 
			
		||||
import org.thingsboard.common.util.ListeningExecutor;
 | 
			
		||||
import org.thingsboard.rule.engine.AbstractRuleNodeUpgradeTest;
 | 
			
		||||
import org.thingsboard.rule.engine.TestDbCallbackExecutor;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbContext;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNode;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
 | 
			
		||||
import org.thingsboard.rule.engine.api.TbNodeException;
 | 
			
		||||
import org.thingsboard.rule.engine.data.DeviceRelationsQuery;
 | 
			
		||||
import org.thingsboard.rule.engine.util.TbMsgSource;
 | 
			
		||||
import org.thingsboard.server.common.data.device.DeviceSearchQuery;
 | 
			
		||||
import org.thingsboard.server.common.data.id.DeviceId;
 | 
			
		||||
import org.thingsboard.server.common.data.id.TenantId;
 | 
			
		||||
import org.thingsboard.server.common.data.msg.TbMsgType;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.EntityRelation;
 | 
			
		||||
import org.thingsboard.server.common.data.relation.EntitySearchDirection;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsg;
 | 
			
		||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
 | 
			
		||||
import org.thingsboard.server.dao.device.DeviceService;
 | 
			
		||||
 | 
			
		||||
public class TbGetDeviceAttrNodeTest {
 | 
			
		||||
import java.util.Arrays;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.NoSuchElementException;
 | 
			
		||||
import java.util.UUID;
 | 
			
		||||
import java.util.stream.Stream;
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfig_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetDeviceAttrNode();
 | 
			
		||||
        String oldConfig = "{\"fetchToData\":false," +
 | 
			
		||||
                "\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false," +
 | 
			
		||||
                "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," +
 | 
			
		||||
                "\"fetchLastLevelOnly\":false}}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThat;
 | 
			
		||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.any;
 | 
			
		||||
import static org.mockito.ArgumentMatchers.eq;
 | 
			
		||||
import static org.mockito.BDDMockito.given;
 | 
			
		||||
import static org.mockito.BDDMockito.spy;
 | 
			
		||||
import static org.mockito.BDDMockito.then;
 | 
			
		||||
 | 
			
		||||
@ExtendWith(MockitoExtension.class)
 | 
			
		||||
public class TbGetDeviceAttrNodeTest extends AbstractRuleNodeUpgradeTest {
 | 
			
		||||
 | 
			
		||||
    private final TenantId TENANT_ID = new TenantId(UUID.fromString("5aea576c-66c4-4732-86b8-dc6bfcde7443"));
 | 
			
		||||
    private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("40b6b393-6ddf-47f9-973a-18550ca70384"));
 | 
			
		||||
    private final ListeningExecutor executor = new TestDbCallbackExecutor();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    private TbGetDeviceAttrNode node;
 | 
			
		||||
    private TbGetDeviceAttrNodeConfiguration config;
 | 
			
		||||
 | 
			
		||||
    @Mock
 | 
			
		||||
    private TbContext ctxMock;
 | 
			
		||||
    @Mock
 | 
			
		||||
    private DeviceService deviceServiceMock;
 | 
			
		||||
 | 
			
		||||
    @BeforeEach
 | 
			
		||||
    public void setUp() {
 | 
			
		||||
        node = spy(new TbGetDeviceAttrNode());
 | 
			
		||||
        config = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfigWithNoFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetDeviceAttrNode();
 | 
			
		||||
        String oldConfig = "{\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false," +
 | 
			
		||||
                "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," +
 | 
			
		||||
                "\"fetchLastLevelOnly\":false}}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
    public void verifyDefaultConfig() {
 | 
			
		||||
        assertThat(config.getClientAttributeNames()).isEmpty();
 | 
			
		||||
        assertThat(config.getSharedAttributeNames()).isEmpty();
 | 
			
		||||
        assertThat(config.getServerAttributeNames()).isEmpty();
 | 
			
		||||
        assertThat(config.getLatestTsKeyNames()).isEmpty();
 | 
			
		||||
        assertThat(config.isTellFailureIfAbsent()).isTrue();
 | 
			
		||||
        assertThat(config.isGetLatestValueWithTs()).isFalse();
 | 
			
		||||
        assertThat(config.getFetchTo()).isEqualTo(TbMsgSource.METADATA);
 | 
			
		||||
 | 
			
		||||
        var deviceRelationsQuery = new DeviceRelationsQuery();
 | 
			
		||||
        deviceRelationsQuery.setDirection(EntitySearchDirection.FROM);
 | 
			
		||||
        deviceRelationsQuery.setMaxLevel(1);
 | 
			
		||||
        deviceRelationsQuery.setRelationType(EntityRelation.CONTAINS_TYPE);
 | 
			
		||||
        deviceRelationsQuery.setDeviceTypes(Collections.singletonList("default"));
 | 
			
		||||
 | 
			
		||||
        assertThat(config.getDeviceRelationsQuery()).isEqualTo(deviceRelationsQuery);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenOldConfigWithNullFetchToDataProperty_whenUpgrade_thenShouldReturnTrueResultWithNewConfig() throws Exception {
 | 
			
		||||
        var defaultConfig = new TbGetDeviceAttrNodeConfiguration().defaultConfiguration();
 | 
			
		||||
        var node = new TbGetDeviceAttrNode();
 | 
			
		||||
        String oldConfig = "{\"fetchToData\":null," +
 | 
			
		||||
                "\"clientAttributeNames\":[]," +
 | 
			
		||||
                "\"sharedAttributeNames\":[]," +
 | 
			
		||||
                "\"serverAttributeNames\":[]," +
 | 
			
		||||
                "\"latestTsKeyNames\":[]," +
 | 
			
		||||
                "\"tellFailureIfAbsent\":true," +
 | 
			
		||||
                "\"getLatestValueWithTs\":false," +
 | 
			
		||||
                "\"deviceRelationsQuery\":{\"direction\":\"FROM\",\"maxLevel\":1,\"relationType\":\"Contains\",\"deviceTypes\":[\"default\"]," +
 | 
			
		||||
                "\"fetchLastLevelOnly\":false}}";
 | 
			
		||||
        JsonNode configJson = JacksonUtil.toJsonNode(oldConfig);
 | 
			
		||||
        TbPair<Boolean, JsonNode> upgrade = node.upgrade(0, configJson);
 | 
			
		||||
        Assertions.assertTrue(upgrade.getFirst());
 | 
			
		||||
        Assertions.assertEquals(defaultConfig, JacksonUtil.treeToValue(upgrade.getSecond(), defaultConfig.getClass()));
 | 
			
		||||
    public void givenFetchToIsNull_whenInit_thenThrowsException() {
 | 
			
		||||
        config.setFetchTo(null);
 | 
			
		||||
        assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))))
 | 
			
		||||
                .isInstanceOf(TbNodeException.class)
 | 
			
		||||
                .hasMessage("FetchTo option can't be null! Allowed values: " + Arrays.toString(TbMsgSource.values()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Test
 | 
			
		||||
    public void givenDeviceDoesNotExist_whenOnMsg_thenTellFailure() throws TbNodeException {
 | 
			
		||||
        node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)));
 | 
			
		||||
 | 
			
		||||
        given(ctxMock.getDeviceService()).willReturn(deviceServiceMock);
 | 
			
		||||
        given(ctxMock.getTenantId()).willReturn(TENANT_ID);
 | 
			
		||||
        given(deviceServiceMock.findDevicesByQuery(any(TenantId.class), any(DeviceSearchQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList()));
 | 
			
		||||
        given(ctxMock.getDbCallbackExecutor()).willReturn(executor);
 | 
			
		||||
 | 
			
		||||
        TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT);
 | 
			
		||||
        node.onMsg(ctxMock, msg);
 | 
			
		||||
 | 
			
		||||
        ArgumentCaptor<Throwable> actualException = ArgumentCaptor.forClass(Throwable.class);
 | 
			
		||||
        then(ctxMock).should().tellFailure(eq(msg), actualException.capture());
 | 
			
		||||
        assertThat(actualException.getValue())
 | 
			
		||||
                .isInstanceOf(NoSuchElementException.class)
 | 
			
		||||
                .hasMessage("Failed to find related device to message originator using relation query specified in the configuration!");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static Stream<Arguments> givenFromVersionAndConfig_whenUpgrade_thenVerifyHasChangesAndConfig() {
 | 
			
		||||
        return Stream.of(
 | 
			
		||||
                // config for version 1 with upgrade from version 0
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "fetchToData":false,
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],
 | 
			
		||||
                                "serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false,
 | 
			
		||||
                                "deviceRelationsQuery":{"direction":"FROM","maxLevel":1,"relationType":"Contains","deviceTypes":["default"],"fetchLastLevelOnly":false}
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "deviceRelationsQuery": {"direction": "FROM","maxLevel": 1, "relationType": "Contains","deviceTypes": ["default"],"fetchLastLevelOnly": false},
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 0 (old config with no fetchToData property)
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],
 | 
			
		||||
                                "serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false,
 | 
			
		||||
                                "deviceRelationsQuery":{"direction":"FROM","maxLevel":1,"relationType":"Contains","deviceTypes":["default"],"fetchLastLevelOnly":false}
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "deviceRelationsQuery": {"direction": "FROM","maxLevel": 1, "relationType": "Contains","deviceTypes": ["default"],"fetchLastLevelOnly": false},
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 0 (old config with null fetchToData property)
 | 
			
		||||
                Arguments.of(0,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "fetchToData":null,
 | 
			
		||||
                                "clientAttributeNames":[],
 | 
			
		||||
                                "sharedAttributeNames":[],
 | 
			
		||||
                                "serverAttributeNames":[],
 | 
			
		||||
                                "latestTsKeyNames":[],
 | 
			
		||||
                                "tellFailureIfAbsent":true,
 | 
			
		||||
                                "getLatestValueWithTs":false,
 | 
			
		||||
                                "deviceRelationsQuery":{"direction":"FROM","maxLevel":1,"relationType":"Contains","deviceTypes":["default"],"fetchLastLevelOnly":false}
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        true,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "deviceRelationsQuery": {"direction": "FROM","maxLevel": 1, "relationType": "Contains","deviceTypes": ["default"],"fetchLastLevelOnly": false},
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                ),
 | 
			
		||||
                // config for version 1 with upgrade from version 1
 | 
			
		||||
                Arguments.of(1,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "deviceRelationsQuery": {"direction": "FROM","maxLevel": 1, "relationType": "Contains","deviceTypes": ["default"],"fetchLastLevelOnly": false},
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """,
 | 
			
		||||
                        false,
 | 
			
		||||
                        """
 | 
			
		||||
                                {
 | 
			
		||||
                                "deviceRelationsQuery": {"direction": "FROM","maxLevel": 1, "relationType": "Contains","deviceTypes": ["default"],"fetchLastLevelOnly": false},
 | 
			
		||||
                                "tellFailureIfAbsent": true,
 | 
			
		||||
                                "fetchTo": "METADATA",
 | 
			
		||||
                                "clientAttributeNames": [],
 | 
			
		||||
                                "sharedAttributeNames": [],
 | 
			
		||||
                                "serverAttributeNames": [],
 | 
			
		||||
                                "latestTsKeyNames": [],
 | 
			
		||||
                                "getLatestValueWithTs": false
 | 
			
		||||
                                }
 | 
			
		||||
                        """
 | 
			
		||||
                )
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    protected TbNode getTestNode() {
 | 
			
		||||
        return node;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user