Merge pull request #10532 from dskarzh/create-alarm-node-tests-refactor
Create alarm node tests: refactor existing and add new
This commit is contained in:
commit
4d8a816f74
@ -176,7 +176,7 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
|
|||||||
existingAlarm.setPropagateRelationTypes(relationTypes);
|
existingAlarm.setPropagateRelationTypes(relationTypes);
|
||||||
existingAlarm.setDetails(details);
|
existingAlarm.setDetails(details);
|
||||||
}
|
}
|
||||||
existingAlarm.setEndTs(System.currentTimeMillis());
|
existingAlarm.setEndTs(currentTimeMillis());
|
||||||
return ctx.getAlarmService().updateAlarm(AlarmUpdateRequest.fromAlarm(existingAlarm));
|
return ctx.getAlarmService().updateAlarm(AlarmUpdateRequest.fromAlarm(existingAlarm));
|
||||||
}, ctx.getDbCallbackExecutor());
|
}, ctx.getDbCallbackExecutor());
|
||||||
return Futures.transform(asyncUpdated, TbAlarmResult::fromAlarmResult, MoreExecutors.directExecutor());
|
return Futures.transform(asyncUpdated, TbAlarmResult::fromAlarmResult, MoreExecutors.directExecutor());
|
||||||
@ -209,4 +209,8 @@ public class TbCreateAlarmNode extends TbAbstractAlarmNode<TbCreateAlarmNodeConf
|
|||||||
return severity;
|
return severity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long currentTimeMillis() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,642 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright © 2016-2024 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.rule.engine.action;
|
|
||||||
|
|
||||||
import com.datastax.oss.driver.api.core.uuid.Uuids;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import org.apache.commons.lang3.NotImplementedException;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.mockito.ArgumentCaptor;
|
|
||||||
import org.mockito.Captor;
|
|
||||||
import org.mockito.Mock;
|
|
||||||
import org.mockito.junit.MockitoJUnitRunner;
|
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
|
||||||
import org.thingsboard.common.util.ListeningExecutor;
|
|
||||||
import org.thingsboard.rule.engine.TestDbCallbackExecutor;
|
|
||||||
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
|
|
||||||
import org.thingsboard.rule.engine.api.ScriptEngine;
|
|
||||||
import org.thingsboard.rule.engine.api.TbContext;
|
|
||||||
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
|
||||||
import org.thingsboard.rule.engine.api.TbNodeException;
|
|
||||||
import org.thingsboard.server.common.data.DataConstants;
|
|
||||||
import org.thingsboard.server.common.data.alarm.Alarm;
|
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmCreateOrUpdateActiveRequest;
|
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
|
||||||
import org.thingsboard.server.common.data.alarm.AlarmUpdateRequest;
|
|
||||||
import org.thingsboard.server.common.data.id.AlarmId;
|
|
||||||
import org.thingsboard.server.common.data.id.DeviceId;
|
|
||||||
import org.thingsboard.server.common.data.id.EntityId;
|
|
||||||
import org.thingsboard.server.common.data.id.RuleChainId;
|
|
||||||
import org.thingsboard.server.common.data.id.RuleNodeId;
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
|
||||||
import org.thingsboard.server.common.data.msg.TbMsgType;
|
|
||||||
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
|
||||||
import org.thingsboard.server.common.msg.TbMsg;
|
|
||||||
import org.thingsboard.server.common.msg.TbMsgDataType;
|
|
||||||
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
|
||||||
|
|
||||||
import javax.script.ScriptException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertNotSame;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.nullable;
|
|
||||||
import static org.mockito.Mockito.anyLong;
|
|
||||||
import static org.mockito.Mockito.atMost;
|
|
||||||
import static org.mockito.Mockito.eq;
|
|
||||||
import static org.mockito.Mockito.same;
|
|
||||||
import static org.mockito.Mockito.times;
|
|
||||||
import static org.mockito.Mockito.verify;
|
|
||||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
@RunWith(MockitoJUnitRunner.class)
|
|
||||||
public class TbAlarmNodeTest {
|
|
||||||
|
|
||||||
private TbAbstractAlarmNode node;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private TbContext ctx;
|
|
||||||
@Mock
|
|
||||||
private RuleEngineAlarmService alarmService;
|
|
||||||
|
|
||||||
@Mock
|
|
||||||
private ScriptEngine detailsJs;
|
|
||||||
|
|
||||||
@Captor
|
|
||||||
private ArgumentCaptor<Runnable> successCaptor;
|
|
||||||
@Captor
|
|
||||||
private ArgumentCaptor<Consumer<Throwable>> failureCaptor;
|
|
||||||
|
|
||||||
private final RuleChainId ruleChainId = new RuleChainId(Uuids.timeBased());
|
|
||||||
private final RuleNodeId ruleNodeId = new RuleNodeId(Uuids.timeBased());
|
|
||||||
|
|
||||||
private ListeningExecutor dbExecutor;
|
|
||||||
|
|
||||||
private final EntityId originator = new DeviceId(Uuids.timeBased());
|
|
||||||
private final EntityId alarmOriginator = new AlarmId(Uuids.timeBased());
|
|
||||||
private final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
|
|
||||||
private final TbMsgMetaData metaData = new TbMsgMetaData();
|
|
||||||
private final String rawJson = "{\"name\": \"Vit\", \"passed\": 5}";
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void before() {
|
|
||||||
dbExecutor = new TestDbCallbackExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void newAlarmCanBeCreated() {
|
|
||||||
initWithCreateAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
long ts = msg.getTs();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null);
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.startTs(ts)
|
|
||||||
.endTs(ts)
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.CRITICAL)
|
|
||||||
.propagate(true)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.build();
|
|
||||||
when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.created(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Created"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void buildDetailsThrowsException() {
|
|
||||||
initWithCreateAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFailedFuture(new NotImplementedException("message")));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null);
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verifyError(msg, "message", NotImplementedException.class);
|
|
||||||
|
|
||||||
verify(ctx).createScriptEngine(ScriptLanguage.JS, "DETAILS");
|
|
||||||
verify(ctx).getAlarmService();
|
|
||||||
verify(ctx, times(2)).getDbCallbackExecutor();
|
|
||||||
verify(ctx).logJsEvalRequest();
|
|
||||||
verify(ctx).getTenantId();
|
|
||||||
verify(alarmService).findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType");
|
|
||||||
|
|
||||||
verifyNoMoreInteractions(ctx, alarmService);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void ifAlarmClearedCreateNew() {
|
|
||||||
initWithCreateAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
long ts = msg.getTs();
|
|
||||||
Alarm clearedAlarm = Alarm.builder().cleared(true).acknowledged(true).build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(clearedAlarm);
|
|
||||||
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.startTs(ts)
|
|
||||||
.endTs(ts)
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.CRITICAL)
|
|
||||||
.propagate(true)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.build();
|
|
||||||
when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.created(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Created"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void alarmCanBeUpdated() {
|
|
||||||
initWithCreateAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
|
|
||||||
long oldEndDate = System.currentTimeMillis();
|
|
||||||
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(activeAlarm);
|
|
||||||
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.CRITICAL)
|
|
||||||
.propagate(true)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.endTs(activeAlarm.getEndTs())
|
|
||||||
.build();
|
|
||||||
when(alarmService.updateAlarm(any(AlarmUpdateRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.modified(true)
|
|
||||||
.old(new Alarm(activeAlarm))
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Updated"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_EXISTING_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertTrue(activeAlarm.getEndTs() >= oldEndDate);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void alarmCanBeCleared() {
|
|
||||||
initWithClearAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
|
|
||||||
long oldEndDate = System.currentTimeMillis();
|
|
||||||
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build();
|
|
||||||
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.cleared(true)
|
|
||||||
.severity(AlarmSeverity.WARNING)
|
|
||||||
.propagate(false)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.endTs(oldEndDate)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(activeAlarm);
|
|
||||||
when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class)))
|
|
||||||
.thenReturn(AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.cleared(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Cleared"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void alarmCanBeClearedWithAlarmOriginator() throws ScriptException, IOException {
|
|
||||||
initWithClearAlarmScript();
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, alarmOriginator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
|
|
||||||
long oldEndDate = System.currentTimeMillis();
|
|
||||||
AlarmId id = new AlarmId(alarmOriginator.getId());
|
|
||||||
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(originator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build();
|
|
||||||
activeAlarm.setId(id);
|
|
||||||
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.cleared(true)
|
|
||||||
.severity(AlarmSeverity.WARNING)
|
|
||||||
.propagate(false)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.endTs(oldEndDate)
|
|
||||||
.build();
|
|
||||||
expectedAlarm.setId(id);
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findAlarmById(tenantId, id)).thenReturn(activeAlarm);
|
|
||||||
when(alarmService.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class)))
|
|
||||||
.thenReturn(AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.cleared(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Cleared"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(alarmOriginator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateAlarmWithDynamicSeverityFromMessageBody() throws Exception {
|
|
||||||
TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration();
|
|
||||||
config.setPropagate(true);
|
|
||||||
config.setSeverity("$[alarmSeverity]");
|
|
||||||
config.setAlarmType("SomeType");
|
|
||||||
config.setScriptLang(ScriptLanguage.JS);
|
|
||||||
config.setAlarmDetailsBuildJs("DETAILS");
|
|
||||||
config.setDynamicSeverity(true);
|
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
|
||||||
|
|
||||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(detailsJs);
|
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
|
||||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
|
||||||
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
|
||||||
|
|
||||||
node = new TbCreateAlarmNode();
|
|
||||||
node.init(ctx, nodeConfiguration);
|
|
||||||
|
|
||||||
String rawJson = "{\"alarmSeverity\": \"WARNING\", \"passed\": 5}";
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
long ts = msg.getTs();
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.startTs(ts)
|
|
||||||
.endTs(ts)
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.WARNING)
|
|
||||||
.propagate(true)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null);
|
|
||||||
when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.created(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Created"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateAlarmWithDynamicSeverityFromMetadata() throws Exception {
|
|
||||||
TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration();
|
|
||||||
config.setPropagate(true);
|
|
||||||
config.setScriptLang(ScriptLanguage.JS);
|
|
||||||
config.setSeverity("${alarmSeverity}");
|
|
||||||
config.setAlarmType("SomeType");
|
|
||||||
config.setAlarmDetailsBuildJs("DETAILS");
|
|
||||||
config.setDynamicSeverity(true);
|
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
|
||||||
|
|
||||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(detailsJs);
|
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
|
||||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
|
||||||
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
|
||||||
|
|
||||||
node = new TbCreateAlarmNode();
|
|
||||||
node.init(ctx, nodeConfiguration);
|
|
||||||
|
|
||||||
metaData.putValue("alarmSeverity", "WARNING");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
long ts = msg.getTs();
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.startTs(ts)
|
|
||||||
.endTs(ts)
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.WARNING)
|
|
||||||
.propagate(true)
|
|
||||||
.type("SomeType")
|
|
||||||
.details(null)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType")).thenReturn(null);
|
|
||||||
when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.created(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx).tellNext(any(), eq("Created"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testCreateAlarmsWithPropagationToTenantWithDynamicTypes() throws Exception {
|
|
||||||
for (int i = 0; i < 10; i++) {
|
|
||||||
var config = new TbCreateAlarmNodeConfiguration();
|
|
||||||
config.setPropagateToTenant(true);
|
|
||||||
config.setSeverity(AlarmSeverity.CRITICAL.name());
|
|
||||||
config.setAlarmType("SomeType" + i);
|
|
||||||
config.setScriptLang(ScriptLanguage.JS);
|
|
||||||
config.setAlarmDetailsBuildJs("DETAILS");
|
|
||||||
config.setDynamicSeverity(true);
|
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
|
||||||
|
|
||||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(detailsJs);
|
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
|
||||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
|
||||||
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
|
||||||
|
|
||||||
node = new TbCreateAlarmNode();
|
|
||||||
node.init(ctx, nodeConfiguration);
|
|
||||||
|
|
||||||
metaData.putValue("key", "value");
|
|
||||||
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, originator, metaData, TbMsgDataType.JSON, rawJson, ruleChainId, ruleNodeId);
|
|
||||||
long ts = msg.getTs();
|
|
||||||
Alarm expectedAlarm = Alarm.builder()
|
|
||||||
.startTs(ts)
|
|
||||||
.endTs(ts)
|
|
||||||
.tenantId(tenantId)
|
|
||||||
.originator(originator)
|
|
||||||
.severity(AlarmSeverity.CRITICAL)
|
|
||||||
.propagateToTenant(true)
|
|
||||||
.type("SomeType" + i)
|
|
||||||
.details(null)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
when(detailsJs.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
|
||||||
when(alarmService.findLatestActiveByOriginatorAndType(tenantId, originator, "SomeType" + i)).thenReturn(null);
|
|
||||||
when(alarmService.createAlarm(any(AlarmCreateOrUpdateActiveRequest.class))).thenReturn(
|
|
||||||
AlarmApiCallResult.builder()
|
|
||||||
.successful(true)
|
|
||||||
.created(true)
|
|
||||||
.alarm(new AlarmInfo(expectedAlarm))
|
|
||||||
.build());
|
|
||||||
node.onMsg(ctx, msg);
|
|
||||||
|
|
||||||
verify(ctx, atMost(10)).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
|
||||||
successCaptor.getValue().run();
|
|
||||||
verify(ctx, atMost(10)).tellNext(any(), eq("Created"));
|
|
||||||
|
|
||||||
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
|
||||||
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
|
||||||
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
|
||||||
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
|
||||||
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
|
||||||
verify(ctx, atMost(10)).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
|
||||||
|
|
||||||
assertEquals(TbMsgType.ALARM, typeCaptor.getValue());
|
|
||||||
assertEquals(originator, originatorCaptor.getValue());
|
|
||||||
assertEquals("value", metadataCaptor.getValue().getValue("key"));
|
|
||||||
assertEquals(Boolean.TRUE.toString(), metadataCaptor.getValue().getValue(DataConstants.IS_NEW_ALARM));
|
|
||||||
assertNotSame(metaData, metadataCaptor.getValue());
|
|
||||||
|
|
||||||
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
|
||||||
assertEquals(expectedAlarm, actualAlarm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initWithCreateAlarmScript() {
|
|
||||||
try {
|
|
||||||
TbCreateAlarmNodeConfiguration config = new TbCreateAlarmNodeConfiguration();
|
|
||||||
config.setPropagate(true);
|
|
||||||
config.setSeverity(AlarmSeverity.CRITICAL.name());
|
|
||||||
config.setAlarmType("SomeType");
|
|
||||||
config.setScriptLang(ScriptLanguage.JS);
|
|
||||||
config.setAlarmDetailsBuildJs("DETAILS");
|
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
|
||||||
|
|
||||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(detailsJs);
|
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
|
||||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
|
||||||
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
|
||||||
|
|
||||||
node = new TbCreateAlarmNode();
|
|
||||||
node.init(ctx, nodeConfiguration);
|
|
||||||
} catch (TbNodeException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initWithClearAlarmScript() {
|
|
||||||
try {
|
|
||||||
TbClearAlarmNodeConfiguration config = new TbClearAlarmNodeConfiguration();
|
|
||||||
config.setAlarmType("SomeType");
|
|
||||||
config.setScriptLang(ScriptLanguage.JS);
|
|
||||||
config.setAlarmDetailsBuildJs("DETAILS");
|
|
||||||
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
|
||||||
|
|
||||||
when(ctx.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(detailsJs);
|
|
||||||
|
|
||||||
when(ctx.getTenantId()).thenReturn(tenantId);
|
|
||||||
when(ctx.getAlarmService()).thenReturn(alarmService);
|
|
||||||
when(ctx.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
|
||||||
|
|
||||||
node = new TbClearAlarmNode();
|
|
||||||
node.init(ctx, nodeConfiguration);
|
|
||||||
} catch (TbNodeException ex) {
|
|
||||||
throw new IllegalStateException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void verifyError(TbMsg msg, String message, Class expectedClass) {
|
|
||||||
ArgumentCaptor<Throwable> captor = ArgumentCaptor.forClass(Throwable.class);
|
|
||||||
verify(ctx).tellFailure(same(msg), captor.capture());
|
|
||||||
|
|
||||||
Throwable value = captor.getValue();
|
|
||||||
assertEquals(expectedClass, value.getClass());
|
|
||||||
assertEquals(message, value.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@ -0,0 +1,218 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2024 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.rule.engine.action;
|
||||||
|
|
||||||
|
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.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.common.util.ListeningExecutor;
|
||||||
|
import org.thingsboard.rule.engine.TestDbCallbackExecutor;
|
||||||
|
import org.thingsboard.rule.engine.api.RuleEngineAlarmService;
|
||||||
|
import org.thingsboard.rule.engine.api.ScriptEngine;
|
||||||
|
import org.thingsboard.rule.engine.api.TbContext;
|
||||||
|
import org.thingsboard.rule.engine.api.TbNodeConfiguration;
|
||||||
|
import org.thingsboard.rule.engine.api.TbNodeException;
|
||||||
|
import org.thingsboard.server.common.data.DataConstants;
|
||||||
|
import org.thingsboard.server.common.data.alarm.Alarm;
|
||||||
|
import org.thingsboard.server.common.data.alarm.AlarmApiCallResult;
|
||||||
|
import org.thingsboard.server.common.data.alarm.AlarmInfo;
|
||||||
|
import org.thingsboard.server.common.data.alarm.AlarmSeverity;
|
||||||
|
import org.thingsboard.server.common.data.id.AlarmId;
|
||||||
|
import org.thingsboard.server.common.data.id.DeviceId;
|
||||||
|
import org.thingsboard.server.common.data.id.EntityId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.msg.TbMsgType;
|
||||||
|
import org.thingsboard.server.common.data.script.ScriptLanguage;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsg;
|
||||||
|
import org.thingsboard.server.common.msg.TbMsgMetaData;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
|
import static org.mockito.ArgumentMatchers.nullable;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
class TbClearAlarmNodeTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
TbContext ctxMock;
|
||||||
|
@Mock
|
||||||
|
RuleEngineAlarmService alarmServiceMock;
|
||||||
|
@Mock
|
||||||
|
ScriptEngine alarmDetailsScriptMock;
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Runnable> successCaptor;
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<Consumer<Throwable>> failureCaptor;
|
||||||
|
|
||||||
|
TbClearAlarmNode node;
|
||||||
|
|
||||||
|
final TenantId tenantId = TenantId.fromUUID(Uuids.timeBased());
|
||||||
|
final EntityId msgOriginator = new DeviceId(Uuids.timeBased());
|
||||||
|
final EntityId alarmOriginator = new AlarmId(Uuids.timeBased());
|
||||||
|
TbMsgMetaData metadata;
|
||||||
|
|
||||||
|
ListeningExecutor dbExecutor;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void before() {
|
||||||
|
dbExecutor = new TestDbCallbackExecutor();
|
||||||
|
metadata = new TbMsgMetaData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void alarmCanBeCleared() {
|
||||||
|
initWithClearAlarmScript();
|
||||||
|
metadata.putValue("key", "value");
|
||||||
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, msgOriginator, metadata, "{\"temperature\": 50}");
|
||||||
|
|
||||||
|
long oldEndDate = System.currentTimeMillis();
|
||||||
|
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(msgOriginator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build();
|
||||||
|
|
||||||
|
Alarm expectedAlarm = Alarm.builder()
|
||||||
|
.tenantId(tenantId)
|
||||||
|
.originator(msgOriginator)
|
||||||
|
.cleared(true)
|
||||||
|
.severity(AlarmSeverity.WARNING)
|
||||||
|
.propagate(false)
|
||||||
|
.type("SomeType")
|
||||||
|
.details(null)
|
||||||
|
.endTs(oldEndDate)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
||||||
|
when(alarmServiceMock.findLatestActiveByOriginatorAndType(tenantId, msgOriginator, "SomeType")).thenReturn(activeAlarm);
|
||||||
|
when(alarmServiceMock.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class)))
|
||||||
|
.thenReturn(AlarmApiCallResult.builder()
|
||||||
|
.successful(true)
|
||||||
|
.cleared(true)
|
||||||
|
.alarm(new AlarmInfo(expectedAlarm))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
node.onMsg(ctxMock, msg);
|
||||||
|
|
||||||
|
verify(ctxMock).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
||||||
|
successCaptor.getValue().run();
|
||||||
|
verify(ctxMock).tellNext(any(), eq("Cleared"));
|
||||||
|
|
||||||
|
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
||||||
|
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
||||||
|
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
||||||
|
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
||||||
|
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
|
verify(ctxMock).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
||||||
|
|
||||||
|
assertThat(TbMsgType.ALARM).isEqualTo(typeCaptor.getValue());
|
||||||
|
assertThat(msgOriginator).isEqualTo(originatorCaptor.getValue());
|
||||||
|
assertThat("value").isEqualTo(metadataCaptor.getValue().getValue("key"));
|
||||||
|
assertThat(Boolean.TRUE.toString()).isEqualTo(metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM));
|
||||||
|
assertThat(metadata).isNotSameAs(metadataCaptor.getValue());
|
||||||
|
|
||||||
|
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
||||||
|
assertThat(actualAlarm).isEqualTo(expectedAlarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void alarmCanBeClearedWithAlarmOriginator() {
|
||||||
|
initWithClearAlarmScript();
|
||||||
|
metadata.putValue("key", "value");
|
||||||
|
TbMsg msg = TbMsg.newMsg(TbMsgType.POST_TELEMETRY_REQUEST, alarmOriginator, metadata, "{\"temperature\": 50}");
|
||||||
|
|
||||||
|
long oldEndDate = System.currentTimeMillis();
|
||||||
|
AlarmId id = new AlarmId(alarmOriginator.getId());
|
||||||
|
Alarm activeAlarm = Alarm.builder().type("SomeType").tenantId(tenantId).originator(msgOriginator).severity(AlarmSeverity.WARNING).endTs(oldEndDate).build();
|
||||||
|
activeAlarm.setId(id);
|
||||||
|
|
||||||
|
Alarm expectedAlarm = Alarm.builder()
|
||||||
|
.tenantId(tenantId)
|
||||||
|
.originator(msgOriginator)
|
||||||
|
.cleared(true)
|
||||||
|
.severity(AlarmSeverity.WARNING)
|
||||||
|
.propagate(false)
|
||||||
|
.type("SomeType")
|
||||||
|
.details(null)
|
||||||
|
.endTs(oldEndDate)
|
||||||
|
.build();
|
||||||
|
expectedAlarm.setId(id);
|
||||||
|
|
||||||
|
when(alarmDetailsScriptMock.executeJsonAsync(msg)).thenReturn(Futures.immediateFuture(null));
|
||||||
|
when(alarmServiceMock.findAlarmById(tenantId, id)).thenReturn(activeAlarm);
|
||||||
|
when(alarmServiceMock.clearAlarm(eq(activeAlarm.getTenantId()), eq(activeAlarm.getId()), anyLong(), nullable(JsonNode.class)))
|
||||||
|
.thenReturn(AlarmApiCallResult.builder()
|
||||||
|
.successful(true)
|
||||||
|
.cleared(true)
|
||||||
|
.alarm(new AlarmInfo(expectedAlarm))
|
||||||
|
.build());
|
||||||
|
|
||||||
|
node.onMsg(ctxMock, msg);
|
||||||
|
|
||||||
|
verify(ctxMock).enqueue(any(), successCaptor.capture(), failureCaptor.capture());
|
||||||
|
successCaptor.getValue().run();
|
||||||
|
verify(ctxMock).tellNext(any(), eq("Cleared"));
|
||||||
|
|
||||||
|
ArgumentCaptor<TbMsg> msgCaptor = ArgumentCaptor.forClass(TbMsg.class);
|
||||||
|
ArgumentCaptor<TbMsgType> typeCaptor = ArgumentCaptor.forClass(TbMsgType.class);
|
||||||
|
ArgumentCaptor<EntityId> originatorCaptor = ArgumentCaptor.forClass(EntityId.class);
|
||||||
|
ArgumentCaptor<TbMsgMetaData> metadataCaptor = ArgumentCaptor.forClass(TbMsgMetaData.class);
|
||||||
|
ArgumentCaptor<String> dataCaptor = ArgumentCaptor.forClass(String.class);
|
||||||
|
verify(ctxMock).transformMsg(msgCaptor.capture(), typeCaptor.capture(), originatorCaptor.capture(), metadataCaptor.capture(), dataCaptor.capture());
|
||||||
|
|
||||||
|
assertThat(TbMsgType.ALARM).isEqualTo(typeCaptor.getValue());
|
||||||
|
assertThat(alarmOriginator).isEqualTo(originatorCaptor.getValue());
|
||||||
|
assertThat("value").isEqualTo(metadataCaptor.getValue().getValue("key"));
|
||||||
|
assertThat(Boolean.TRUE.toString()).isEqualTo(metadataCaptor.getValue().getValue(DataConstants.IS_CLEARED_ALARM));
|
||||||
|
assertThat(metadata).isNotSameAs(metadataCaptor.getValue());
|
||||||
|
|
||||||
|
Alarm actualAlarm = JacksonUtil.fromBytes(dataCaptor.getValue().getBytes(), Alarm.class);
|
||||||
|
assertThat(actualAlarm).isEqualTo(expectedAlarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initWithClearAlarmScript() {
|
||||||
|
try {
|
||||||
|
TbClearAlarmNodeConfiguration config = new TbClearAlarmNodeConfiguration();
|
||||||
|
config.setAlarmType("SomeType");
|
||||||
|
config.setScriptLang(ScriptLanguage.JS);
|
||||||
|
config.setAlarmDetailsBuildJs("DETAILS");
|
||||||
|
TbNodeConfiguration nodeConfiguration = new TbNodeConfiguration(JacksonUtil.valueToTree(config));
|
||||||
|
|
||||||
|
when(ctxMock.createScriptEngine(ScriptLanguage.JS, "DETAILS")).thenReturn(alarmDetailsScriptMock);
|
||||||
|
|
||||||
|
when(ctxMock.getTenantId()).thenReturn(tenantId);
|
||||||
|
when(ctxMock.getAlarmService()).thenReturn(alarmServiceMock);
|
||||||
|
when(ctxMock.getDbCallbackExecutor()).thenReturn(dbExecutor);
|
||||||
|
|
||||||
|
node = new TbClearAlarmNode();
|
||||||
|
node.init(ctxMock, nodeConfiguration);
|
||||||
|
} catch (TbNodeException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user