|
|
@ -15,139 +15,319 @@ |
|
|
*/ |
|
|
*/ |
|
|
package org.thingsboard.rule.engine.transform; |
|
|
package org.thingsboard.rule.engine.transform; |
|
|
|
|
|
|
|
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
|
|
|
|
|
import com.google.common.util.concurrent.Futures; |
|
|
import com.google.common.util.concurrent.Futures; |
|
|
import org.junit.jupiter.api.BeforeEach; |
|
|
import org.junit.jupiter.api.BeforeEach; |
|
|
import org.junit.jupiter.api.Test; |
|
|
import org.junit.jupiter.api.Test; |
|
|
import org.junit.jupiter.api.extension.ExtendWith; |
|
|
import org.junit.jupiter.api.extension.ExtendWith; |
|
|
|
|
|
import org.junit.jupiter.params.ParameterizedTest; |
|
|
|
|
|
import org.junit.jupiter.params.provider.Arguments; |
|
|
|
|
|
import org.junit.jupiter.params.provider.MethodSource; |
|
|
|
|
|
import org.junit.jupiter.params.provider.NullAndEmptySource; |
|
|
import org.mockito.ArgumentCaptor; |
|
|
import org.mockito.ArgumentCaptor; |
|
|
import org.mockito.Mock; |
|
|
import org.mockito.Mock; |
|
|
import org.mockito.junit.jupiter.MockitoExtension; |
|
|
import org.mockito.junit.jupiter.MockitoExtension; |
|
|
import org.thingsboard.common.util.JacksonUtil; |
|
|
import org.thingsboard.common.util.JacksonUtil; |
|
|
import org.thingsboard.common.util.ListeningExecutor; |
|
|
import org.thingsboard.common.util.ListeningExecutor; |
|
|
import org.thingsboard.rule.engine.TestDbCallbackExecutor; |
|
|
import org.thingsboard.rule.engine.TestDbCallbackExecutor; |
|
|
|
|
|
import org.thingsboard.rule.engine.api.RuleEngineAlarmService; |
|
|
import org.thingsboard.rule.engine.api.TbContext; |
|
|
import org.thingsboard.rule.engine.api.TbContext; |
|
|
import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
|
|
import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
|
|
import org.thingsboard.rule.engine.api.TbNodeException; |
|
|
import org.thingsboard.rule.engine.api.TbNodeException; |
|
|
|
|
|
import org.thingsboard.rule.engine.api.util.TbNodeUtils; |
|
|
|
|
|
import org.thingsboard.rule.engine.data.RelationsQuery; |
|
|
|
|
|
import org.thingsboard.server.common.data.Device; |
|
|
|
|
|
import org.thingsboard.server.common.data.EntityType; |
|
|
|
|
|
import org.thingsboard.server.common.data.alarm.Alarm; |
|
|
import org.thingsboard.server.common.data.asset.Asset; |
|
|
import org.thingsboard.server.common.data.asset.Asset; |
|
|
|
|
|
import org.thingsboard.server.common.data.id.AlarmId; |
|
|
import org.thingsboard.server.common.data.id.AssetId; |
|
|
import org.thingsboard.server.common.data.id.AssetId; |
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
import org.thingsboard.server.common.data.id.CustomerId; |
|
|
|
|
|
import org.thingsboard.server.common.data.id.DeviceId; |
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
import org.thingsboard.server.common.data.id.EntityId; |
|
|
import org.thingsboard.server.common.data.id.RuleChainId; |
|
|
import org.thingsboard.server.common.data.id.TenantId; |
|
|
import org.thingsboard.server.common.data.id.RuleNodeId; |
|
|
|
|
|
import org.thingsboard.server.common.data.msg.TbMsgType; |
|
|
import org.thingsboard.server.common.data.msg.TbMsgType; |
|
|
|
|
|
import org.thingsboard.server.common.data.relation.EntityRelation; |
|
|
|
|
|
import org.thingsboard.server.common.data.relation.EntityRelationsQuery; |
|
|
|
|
|
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 org.thingsboard.server.common.msg.TbMsg; |
|
|
import org.thingsboard.server.common.msg.TbMsg; |
|
|
import org.thingsboard.server.common.msg.TbMsgDataType; |
|
|
|
|
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|
|
import org.thingsboard.server.common.msg.TbMsgMetaData; |
|
|
import org.thingsboard.server.dao.asset.AssetService; |
|
|
import org.thingsboard.server.dao.asset.AssetService; |
|
|
|
|
|
import org.thingsboard.server.dao.device.DeviceService; |
|
|
|
|
|
import org.thingsboard.server.dao.relation.RelationService; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.Collections; |
|
|
|
|
|
import java.util.Map; |
|
|
import java.util.NoSuchElementException; |
|
|
import java.util.NoSuchElementException; |
|
|
|
|
|
import java.util.UUID; |
|
|
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
import static org.junit.jupiter.api.Assertions.assertEquals; |
|
|
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.any; |
|
|
import static org.mockito.ArgumentMatchers.eq; |
|
|
import static org.mockito.ArgumentMatchers.eq; |
|
|
import static org.mockito.ArgumentMatchers.same; |
|
|
import static org.mockito.BDDMockito.given; |
|
|
import static org.mockito.Mockito.verify; |
|
|
import static org.mockito.BDDMockito.then; |
|
|
import static org.mockito.Mockito.when; |
|
|
import static org.thingsboard.rule.engine.transform.OriginatorSource.ALARM_ORIGINATOR; |
|
|
|
|
|
import static org.thingsboard.rule.engine.transform.OriginatorSource.CUSTOMER; |
|
|
|
|
|
import static org.thingsboard.rule.engine.transform.OriginatorSource.ENTITY; |
|
|
|
|
|
import static org.thingsboard.rule.engine.transform.OriginatorSource.RELATED; |
|
|
|
|
|
import static org.thingsboard.rule.engine.transform.OriginatorSource.TENANT; |
|
|
|
|
|
|
|
|
@ExtendWith(MockitoExtension.class) |
|
|
@ExtendWith(MockitoExtension.class) |
|
|
public class TbChangeOriginatorNodeTest { |
|
|
public class TbChangeOriginatorNodeTest { |
|
|
|
|
|
|
|
|
private static final String CUSTOMER_SOURCE = "CUSTOMER"; |
|
|
private final TenantId TENANT_ID = TenantId.fromUUID(UUID.fromString("79830b6d-4f93-49bd-9b5b-d31ce51da77b")); |
|
|
|
|
|
private final CustomerId CUSTOMER_ID = new CustomerId(UUID.fromString("c6b2c94b-5517-4f20-bf8e-ae9407eb8a7a")); |
|
|
|
|
|
private final DeviceId DEVICE_ID = new DeviceId(UUID.fromString("990605a4-db46-4ed4-942f-e18200453571")); |
|
|
|
|
|
private final AssetId ASSET_ID = new AssetId(UUID.fromString("55de3f10-1b55-4950-b711-ed132896b260")); |
|
|
|
|
|
|
|
|
|
|
|
private final ListeningExecutor dbExecutor = new TestDbCallbackExecutor(); |
|
|
|
|
|
|
|
|
private TbChangeOriginatorNode node; |
|
|
private TbChangeOriginatorNode node; |
|
|
|
|
|
private TbChangeOriginatorNodeConfiguration config; |
|
|
|
|
|
|
|
|
@Mock |
|
|
@Mock |
|
|
private TbContext ctx; |
|
|
private TbContext ctxMock; |
|
|
@Mock |
|
|
@Mock |
|
|
private AssetService assetService; |
|
|
private AssetService assetServiceMock; |
|
|
|
|
|
@Mock |
|
|
private ListeningExecutor dbExecutor; |
|
|
private DeviceService deviceServiceMock; |
|
|
|
|
|
@Mock |
|
|
|
|
|
private RelationService relationServiceMock; |
|
|
|
|
|
@Mock |
|
|
|
|
|
private RuleEngineAlarmService alarmServiceMock; |
|
|
|
|
|
|
|
|
@BeforeEach |
|
|
@BeforeEach |
|
|
public void before() throws TbNodeException { |
|
|
public void before() throws TbNodeException { |
|
|
dbExecutor = new TestDbCallbackExecutor(); |
|
|
node = new TbChangeOriginatorNode(); |
|
|
init(); |
|
|
config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Test |
|
|
@Test |
|
|
public void originatorCanBeChangedToCustomerId() { |
|
|
public void verifyDefaultConfig() { |
|
|
AssetId assetId = new AssetId(Uuids.timeBased()); |
|
|
var config = new TbChangeOriginatorNodeConfiguration().defaultConfiguration(); |
|
|
CustomerId customerId = new CustomerId(Uuids.timeBased()); |
|
|
assertThat(config.getOriginatorSource()).isEqualTo(CUSTOMER); |
|
|
Asset asset = new Asset(); |
|
|
RelationsQuery relationsQuery = new RelationsQuery(); |
|
|
asset.setCustomerId(customerId); |
|
|
relationsQuery.setDirection(EntitySearchDirection.FROM); |
|
|
|
|
|
relationsQuery.setMaxLevel(1); |
|
|
RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); |
|
|
RelationEntityTypeFilter relationEntityTypeFilter = new RelationEntityTypeFilter(EntityRelation.CONTAINS_TYPE, Collections.emptyList()); |
|
|
RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); |
|
|
relationsQuery.setFilters(Collections.singletonList(relationEntityTypeFilter)); |
|
|
|
|
|
assertThat(config.getRelationsQuery()).isEqualTo(relationsQuery); |
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON, TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); |
|
|
assertThat(config.getEntityType()).isNull(); |
|
|
|
|
|
assertThat(config.getEntityNamePattern()).isNull(); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
when(ctx.getAssetService()).thenReturn(assetService); |
|
|
@Test |
|
|
when(assetService.findAssetByIdAsync(any(),eq( assetId))).thenReturn(Futures.immediateFuture(asset)); |
|
|
public void givenRelatedSourceIsNull_whenInit_thenThrowsException() { |
|
|
|
|
|
config.setOriginatorSource(null); |
|
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) |
|
|
|
|
|
.isInstanceOf(IllegalArgumentException.class) |
|
|
|
|
|
.hasMessage("Originator source should be specified."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); |
|
|
@Test |
|
|
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class); |
|
|
public void givenRelatedSourceAndRelatedQueryIsNull_whenInit_thenThrowsException() { |
|
|
verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); |
|
|
config.setOriginatorSource(RELATED); |
|
|
|
|
|
config.setRelationsQuery(null); |
|
|
|
|
|
|
|
|
assertEquals(customerId, originatorCaptor.getValue()); |
|
|
assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) |
|
|
|
|
|
.isInstanceOf(IllegalArgumentException.class) |
|
|
|
|
|
.hasMessage("Relations query should be specified if 'Related entity' source is selected."); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Test |
|
|
@Test |
|
|
public void newChainCanBeStarted() { |
|
|
public void givenEntitySourceAndEntityTypeIsNull_whenInit_thenThrowsException() { |
|
|
AssetId assetId = new AssetId(Uuids.timeBased()); |
|
|
config.setOriginatorSource(ENTITY); |
|
|
CustomerId customerId = new CustomerId(Uuids.timeBased()); |
|
|
config.setEntityType(null); |
|
|
Asset asset = new Asset(); |
|
|
|
|
|
asset.setCustomerId(customerId); |
|
|
|
|
|
|
|
|
|
|
|
RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); |
|
|
assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) |
|
|
RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); |
|
|
.isInstanceOf(IllegalArgumentException.class) |
|
|
|
|
|
.hasMessage("Entity type should be specified if 'Entity by name pattern' source is selected."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); |
|
|
@ParameterizedTest |
|
|
|
|
|
@NullAndEmptySource |
|
|
|
|
|
public void givenEntitySourceAndEntityNamePatternIsEmpty_whenInit_thenThrowsException(String entityName) { |
|
|
|
|
|
config.setOriginatorSource(ENTITY); |
|
|
|
|
|
config.setEntityType(EntityType.DEVICE.name()); |
|
|
|
|
|
config.setEntityNamePattern(entityName); |
|
|
|
|
|
|
|
|
when(ctx.getAssetService()).thenReturn(assetService); |
|
|
assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) |
|
|
when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(asset)); |
|
|
.isInstanceOf(IllegalArgumentException.class) |
|
|
|
|
|
.hasMessage("Name pattern should be specified if 'Entity by name pattern' source is selected."); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
@Test |
|
|
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class); |
|
|
public void givenEntitySourceAndUnexpectedEntityType_whenInit_thenThrowsException() { |
|
|
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class); |
|
|
config.setOriginatorSource(ENTITY); |
|
|
verify(ctx).transformMsgOriginator(msgCaptor.capture(), originatorCaptor.capture()); |
|
|
config.setEntityType(EntityType.TENANT.name()); |
|
|
|
|
|
config.setEntityNamePattern("tenant-A"); |
|
|
|
|
|
|
|
|
|
|
|
assertThatThrownBy(() -> node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config)))) |
|
|
|
|
|
.isInstanceOf(IllegalStateException.class) |
|
|
|
|
|
.hasMessage("Unexpected entity type TENANT"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
assertEquals(customerId, originatorCaptor.getValue()); |
|
|
@Test |
|
|
|
|
|
public void givenOriginatorSourceIsCustomer_whenOnMsg_thenTellSuccess() throws TbNodeException { |
|
|
|
|
|
Device device = new Device(DEVICE_ID); |
|
|
|
|
|
device.setCustomerId(CUSTOMER_ID); |
|
|
|
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); |
|
|
|
|
|
TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, CUSTOMER_ID); |
|
|
|
|
|
|
|
|
|
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getDeviceService()).willReturn(deviceServiceMock); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(deviceServiceMock.findDeviceById(any(TenantId.class), any(DeviceId.class))).willReturn(device); |
|
|
|
|
|
given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); |
|
|
|
|
|
|
|
|
|
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
|
|
|
then(deviceServiceMock).should().findDeviceById(TENANT_ID, DEVICE_ID); |
|
|
|
|
|
then(ctxMock).should().transformMsgOriginator(msg, CUSTOMER_ID); |
|
|
|
|
|
ArgumentCaptor<TbMsg> actualMsg = ArgumentCaptor.forClass(TbMsg.class); |
|
|
|
|
|
then(ctxMock).should().tellSuccess(actualMsg.capture()); |
|
|
|
|
|
assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@Test |
|
|
@Test |
|
|
public void exceptionThrownIfCannotFindNewOriginator() { |
|
|
public void givenOriginatorSourceIsTenant_whenOnMsg_thenTellSuccess() throws TbNodeException { |
|
|
AssetId assetId = new AssetId(Uuids.timeBased()); |
|
|
config.setOriginatorSource(TENANT); |
|
|
CustomerId customerId = new CustomerId(Uuids.timeBased()); |
|
|
|
|
|
Asset asset = new Asset(); |
|
|
|
|
|
asset.setCustomerId(customerId); |
|
|
|
|
|
|
|
|
|
|
|
RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased()); |
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); |
|
|
RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased()); |
|
|
TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, TENANT_ID); |
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, assetId, TbMsgMetaData.EMPTY, TbMsgDataType.JSON,TbMsg.EMPTY_JSON_OBJECT, ruleChainId, ruleNodeId); |
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); |
|
|
|
|
|
|
|
|
when(ctx.getAssetService()).thenReturn(assetService); |
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
when(assetService.findAssetByIdAsync(any(), eq(assetId))).thenReturn(Futures.immediateFuture(null)); |
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
ArgumentCaptor<NoSuchElementException> exceptionCaptor = ArgumentCaptor.forClass(NoSuchElementException.class); |
|
|
then(ctxMock).should().transformMsgOriginator(msg, TENANT_ID); |
|
|
|
|
|
ArgumentCaptor<TbMsg> actualMsg = ArgumentCaptor.forClass(TbMsg.class); |
|
|
|
|
|
then(ctxMock).should().tellSuccess(actualMsg.capture()); |
|
|
|
|
|
assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
node.onMsg(ctx, msg); |
|
|
@Test |
|
|
verify(ctx).tellFailure(same(msg), exceptionCaptor.capture()); |
|
|
public void givenOriginatorSourceIsRelatedAndNewOriginatorIsNull_whenOnMsg_thenTellFailure() throws TbNodeException { |
|
|
|
|
|
config.setOriginatorSource(RELATED); |
|
|
|
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, ASSET_ID, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); |
|
|
|
|
|
|
|
|
|
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getRelationService()).willReturn(relationServiceMock); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(relationServiceMock.findByQuery(any(TenantId.class), any(EntityRelationsQuery.class))).willReturn(Futures.immediateFuture(Collections.emptyList())); |
|
|
|
|
|
|
|
|
|
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
|
|
|
var query = new EntityRelationsQuery(); |
|
|
|
|
|
var relationsQuery = config.getRelationsQuery(); |
|
|
|
|
|
var parameters = new RelationsSearchParameters( |
|
|
|
|
|
ASSET_ID, |
|
|
|
|
|
relationsQuery.getDirection(), |
|
|
|
|
|
relationsQuery.getMaxLevel(), |
|
|
|
|
|
relationsQuery.isFetchLastLevelOnly() |
|
|
|
|
|
); |
|
|
|
|
|
query.setParameters(parameters); |
|
|
|
|
|
query.setFilters(relationsQuery.getFilters()); |
|
|
|
|
|
then(relationServiceMock).should().findByQuery(TENANT_ID, query); |
|
|
|
|
|
ArgumentCaptor<Throwable> throwable = ArgumentCaptor.forClass(Throwable.class); |
|
|
|
|
|
then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); |
|
|
|
|
|
assertThat(throwable.getValue()).isInstanceOf(NoSuchElementException.class).hasMessage("Failed to find new originator!"); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
assertEquals("Failed to find new originator!", exceptionCaptor.getValue().getMessage()); |
|
|
@Test |
|
|
|
|
|
public void givenOriginatorSourceIsAlarmOriginator_whenOnMsg_thenTellSuccess() throws TbNodeException { |
|
|
|
|
|
config.setOriginatorSource(ALARM_ORIGINATOR); |
|
|
|
|
|
|
|
|
|
|
|
AlarmId alarmId = new AlarmId(UUID.fromString("6b43f694-cb5f-4199-9023-e9e40eeb82dd")); |
|
|
|
|
|
Alarm alarm = new Alarm(alarmId); |
|
|
|
|
|
alarm.setOriginator(DEVICE_ID); |
|
|
|
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.ALARM, alarmId, TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT); |
|
|
|
|
|
TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, DEVICE_ID); |
|
|
|
|
|
|
|
|
|
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getAlarmService()).willReturn(alarmServiceMock); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(alarmServiceMock.findAlarmByIdAsync(any(TenantId.class), any(AlarmId.class))).willReturn(Futures.immediateFuture(alarm)); |
|
|
|
|
|
given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); |
|
|
|
|
|
|
|
|
|
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
|
|
|
then(alarmServiceMock).should().findAlarmByIdAsync(TENANT_ID, alarmId); |
|
|
|
|
|
then(ctxMock).should().transformMsgOriginator(msg, DEVICE_ID); |
|
|
|
|
|
ArgumentCaptor<TbMsg> actualMsg = ArgumentCaptor.forClass(TbMsg.class); |
|
|
|
|
|
then(ctxMock).should().tellSuccess(actualMsg.capture()); |
|
|
|
|
|
assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
public void init() throws TbNodeException { |
|
|
@ParameterizedTest |
|
|
TbChangeOriginatorNodeConfiguration config = new TbChangeOriginatorNodeConfiguration(); |
|
|
@MethodSource |
|
|
config.setOriginatorSource(CUSTOMER_SOURCE); |
|
|
public void givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess(String entityNamePattern, TbMsgMetaData metaData, String data) throws TbNodeException { |
|
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config)); |
|
|
config.setOriginatorSource(ENTITY); |
|
|
|
|
|
config.setEntityType(EntityType.ASSET.name()); |
|
|
|
|
|
config.setEntityNamePattern(entityNamePattern); |
|
|
|
|
|
|
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, data); |
|
|
|
|
|
TbMsg expectedMsg = TbMsg.transformMsgOriginator(msg, ASSET_ID); |
|
|
|
|
|
|
|
|
|
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getAssetService()).willReturn(assetServiceMock); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(assetServiceMock.findAssetByTenantIdAndName(any(TenantId.class), any(String.class))).willReturn(new Asset(ASSET_ID)); |
|
|
|
|
|
given(ctxMock.transformMsgOriginator(any(TbMsg.class), any(EntityId.class))).willReturn(expectedMsg); |
|
|
|
|
|
|
|
|
|
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
|
|
|
String expectedEntityName = TbNodeUtils.processPattern(entityNamePattern, msg); |
|
|
|
|
|
then(assetServiceMock).should().findAssetByTenantIdAndName(TENANT_ID, expectedEntityName); |
|
|
|
|
|
then(ctxMock).should().transformMsgOriginator(msg, ASSET_ID); |
|
|
|
|
|
ArgumentCaptor<TbMsg> actualMsg = ArgumentCaptor.forClass(TbMsg.class); |
|
|
|
|
|
then(ctxMock).should().tellSuccess(actualMsg.capture()); |
|
|
|
|
|
assertThat(actualMsg.getValue()).usingRecursiveComparison().ignoringFields("ctx").isEqualTo(expectedMsg); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor); |
|
|
private static Stream<Arguments> givenOriginatorSourceIsEntity_whenOnMsg_thenTellSuccess() { |
|
|
|
|
|
return Stream.of( |
|
|
|
|
|
Arguments.of("test-asset", TbMsgMetaData.EMPTY, TbMsg.EMPTY_JSON_OBJECT), |
|
|
|
|
|
Arguments.of("${md-name-pattern}", new TbMsgMetaData(Map.of("md-name-pattern", "md-test-asset")), TbMsg.EMPTY_JSON_OBJECT), |
|
|
|
|
|
Arguments.of("${msg-name-pattern}", TbMsgMetaData.EMPTY, "{\"msg-name-pattern\":\"msg-test-asset\"}") |
|
|
|
|
|
); |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
node = new TbChangeOriginatorNode(); |
|
|
@Test |
|
|
node.init(null, nodeConfiguration); |
|
|
public void givenOriginatorSourceIsEntityAndEntityCouldNotFound_whenOnMsg_thenTellFailure() throws TbNodeException { |
|
|
|
|
|
config.setOriginatorSource(ENTITY); |
|
|
|
|
|
config.setEntityType(EntityType.ASSET.name()); |
|
|
|
|
|
config.setEntityNamePattern("${md-name-pattern}"); |
|
|
|
|
|
|
|
|
|
|
|
TbMsgMetaData metaData = new TbMsgMetaData(); |
|
|
|
|
|
metaData.putValue("md-name-pattern", "test-asset"); |
|
|
|
|
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, DEVICE_ID, metaData, TbMsg.EMPTY_JSON_OBJECT); |
|
|
|
|
|
|
|
|
|
|
|
given(ctxMock.getDbCallbackExecutor()).willReturn(dbExecutor); |
|
|
|
|
|
given(ctxMock.getAssetService()).willReturn(assetServiceMock); |
|
|
|
|
|
given(ctxMock.getTenantId()).willReturn(TENANT_ID); |
|
|
|
|
|
given(assetServiceMock.findAssetByTenantIdAndName(any(TenantId.class), any(String.class))).willReturn(null); |
|
|
|
|
|
|
|
|
|
|
|
node.init(ctxMock, new TbNodeConfiguration(JacksonUtil.valueToTree(config))); |
|
|
|
|
|
node.onMsg(ctxMock, msg); |
|
|
|
|
|
|
|
|
|
|
|
ArgumentCaptor<Throwable> throwable = ArgumentCaptor.forClass(Throwable.class); |
|
|
|
|
|
then(ctxMock).should().tellFailure(eq(msg), throwable.capture()); |
|
|
|
|
|
assertThat(throwable.getValue()).isInstanceOf(IllegalStateException.class).hasMessage("Failed to find asset with name 'test-asset'!"); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
} |
|
|
} |
|
|
|