feat:【IoT 物联网】场景联动规则匹配器单元测试

This commit is contained in:
puhui999 2025-08-18 15:21:09 +08:00
parent fb35807ebf
commit 7661c7165c
13 changed files with 2575 additions and 260 deletions

View File

@ -2,18 +2,11 @@ package cn.iocoder.yudao.module.iot.service.rule.scene;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.text.CharPool;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO;
@ -23,7 +16,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
@ -36,12 +28,9 @@ import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS;
@ -353,65 +342,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService {
}
}
// TODO @puhui999下面还需要么
/**
* 判断触发器的条件参数是否匹配
*
* @param message 设备消息
* @param condition 触发条件
* @param sceneRule 规则场景用于日志无其它作用
* @param trigger 触发器用于日志无其它作用
* @return 是否匹配
*/
@SuppressWarnings({"unchecked", "DataFlowIssue"})
private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition,
IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) {
// 1.1 校验操作符是否合法
IotSceneRuleConditionOperatorEnum operator =
IotSceneRuleConditionOperatorEnum.operatorOf(condition.getOperator());
if (operator == null) {
log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]",
sceneRule.getId(), trigger, condition.getOperator());
return false;
}
// 1.2 校验消息是否包含对应的值
String messageValue = MapUtil.getStr((Map<String, Object>) message.getData(), condition.getIdentifier());
if (messageValue == null) {
return false;
}
// 2.1 构建 Spring 表达式的变量
Map<String, Object> springExpressionVariables = new HashMap<>();
try {
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue);
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, condition.getParam());
List<String> parameterValues = StrUtil.splitTrim(condition.getParam(), CharPool.COMMA);
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues);
// 特殊解决数字的比较因为 Spring 是基于它的 compareTo 方法对数字的比较存在问题
if (ObjectUtils.equalsAny(operator, IotSceneRuleConditionOperatorEnum.BETWEEN,
IotSceneRuleConditionOperatorEnum.NOT_BETWEEN,
IotSceneRuleConditionOperatorEnum.GREATER_THAN,
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS,
IotSceneRuleConditionOperatorEnum.LESS_THAN,
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS)
&& NumberUtil.isNumber(messageValue)
&& NumberUtils.isAllNumber(parameterValues)) {
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE,
NumberUtil.parseDouble(messageValue));
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE,
NumberUtil.parseDouble(condition.getParam()));
springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST,
convertList(parameterValues, NumberUtil::parseDouble));
}
// 2.2 计算 Spring 表达式
return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables);
} catch (Exception e) {
log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]",
message, sceneRule.getId(), trigger, operator, springExpressionVariables, e);
return false;
}
}
/**
* 执行规则场景的动作
*

View File

@ -1,190 +0,0 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.*;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
/**
* IoT 场景规则触发器匹配器测试类
*
* @author HUIHUI
*/
public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest {
// TODO @puhui999public 都加下哈
private IotSceneRuleMatcherManager matcherManager;
@BeforeEach
void setUp() {
// 创建所有匹配器实例
List<IotSceneRuleMatcher> matchers = Arrays.asList(
new DeviceStateUpdateTriggerMatcher(),
new DevicePropertyPostTriggerMatcher(),
new DeviceEventPostTriggerMatcher(),
new DeviceServiceInvokeTriggerMatcher(),
new TimerTriggerMatcher()
);
// 初始化匹配器管理器
matcherManager = new IotSceneRuleMatcherManager(matchers);
}
@Test
void testDeviceStateUpdateTriggerMatcher() {
// 1. 准备测试数据
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-001")
.method("thing.state.update") // TODO @puhui999这里的枚举
.data(1) // 在线状态 TODO @puhui999这里的枚举
.build();
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
trigger.setOperator("="); // TODO @puhui999这里的枚举下面也是类似
trigger.setValue("1");
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertTrue(matched, "设备状态更新触发器应该匹配");
}
@Test
void testDevicePropertyPostTriggerMatcher() {
// 1. 准备测试数据
HashMap<String, Object> params = new HashMap<>();
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-002")
.method("thing.property.post")
.data(25.5) // 温度值
.params(params)
.build();
// 模拟标识符
params.put("identifier", "temperature");
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
trigger.setIdentifier("temperature");
trigger.setOperator(">");
trigger.setValue("20");
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertTrue(matched, "设备属性上报触发器应该匹配");
}
@Test
void testDeviceEventPostTriggerMatcher() {
// 1. 准备测试数据
HashMap<String, Object> params = new HashMap<>();
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-003")
.method("thing.event.post")
.data("alarm_data")
.params(params)
.build();
// 模拟标识符
params.put("identifier", "high_temperature_alarm");
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
trigger.setIdentifier("high_temperature_alarm");
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertTrue(matched, "设备事件上报触发器应该匹配");
}
@Test
void testDeviceServiceInvokeTriggerMatcher() {
// 1. 准备测试数据
HashMap<String, Object> params = new HashMap<>();
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-004")
.method("thing.service.invoke")
.msg("alarm_data")
.params(params)
.build();
// 模拟标识符
params.put("identifier", "restart_device");
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
trigger.setIdentifier("restart_device");
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertTrue(matched, "设备服务调用触发器应该匹配");
}
@Test
void testTimerTriggerMatcher() {
// 1. 准备测试数据
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-005")
.method("timer.trigger") // 定时触发器不依赖具体消息方法
.build();
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
trigger.setCronExpression("0 0 12 * * ?"); // 每天中午12点
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertTrue(matched, "定时触发器应该匹配");
}
@Test
void testInvalidTriggerType() {
// 1. 准备测试数据
IotDeviceMessage message = IotDeviceMessage.builder()
.requestId("test-006")
.method("unknown.method")
.build();
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(999); // 无效的触发器类型
// 2. 执行测试
boolean matched = matcherManager.isMatched(message, trigger);
// 3. 验证结果
assertFalse(matched, "无效的触发器类型应该不匹配");
}
@Test
void testGetSupportedTriggerTypes() {
// 1. 执行测试
var supportedTypes = matcherManager.getSupportedTriggerTypes();
// 2. 验证结果
assertNotNull(supportedTypes);
assertEquals(5, supportedTypes.size());
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE));
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST));
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST));
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE));
assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER));
}
}

View File

@ -0,0 +1,321 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link CurrentTimeConditionMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class CurrentTimeConditionMatcherTest extends BaseMockitoUnitTest {
private CurrentTimeConditionMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new CurrentTimeConditionMatcher();
}
@Test
public void testGetSupportedConditionType() {
// when & then
assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, matcher.getSupportedConditionType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(40, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
// ========== 时间戳条件测试 ==========
@Test
public void testIsMatched_DateTimeGreaterThan_Success() {
// given
IotDeviceMessage message = new IotDeviceMessage();
long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8"));
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
String.valueOf(pastTimestamp)
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_DateTimeGreaterThan_Failure() {
// given
IotDeviceMessage message = new IotDeviceMessage();
long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
String.valueOf(futureTimestamp)
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_DateTimeLessThan_Success() {
// given
IotDeviceMessage message = new IotDeviceMessage();
long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(),
String.valueOf(futureTimestamp)
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_DateTimeBetween_Success() {
// given
IotDeviceMessage message = new IotDeviceMessage();
long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8"));
long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),
startTimestamp + "," + endTimestamp
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_DateTimeBetween_Failure() {
// given
IotDeviceMessage message = new IotDeviceMessage();
long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of("+8"));
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),
startTimestamp + "," + endTimestamp
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
// ========== 当日时间条件测试 ==========
@Test
public void testIsMatched_TimeGreaterThan_EarlyMorning() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),
"06:00:00" // 早上6点
);
// when
boolean result = matcher.isMatched(message, condition);
// then
// 结果取决于当前时间如果当前时间大于6点则为true
assertNotNull(result);
}
@Test
public void testIsMatched_TimeLessThan_LateNight() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(),
"23:59:59" // 晚上11点59分59秒
);
// when
boolean result = matcher.isMatched(message, condition);
// then
// 大部分情况下应该为true除非在午夜前1秒运行测试
assertNotNull(result);
}
@Test
public void testIsMatched_TimeBetween_AllDay() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
"00:00:00,23:59:59" // 全天
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result); // 全天范围应该总是匹配
}
@Test
public void testIsMatched_TimeBetween_WorkingHours() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
"09:00:00,17:00:00" // 工作时间
);
// when
boolean result = matcher.isMatched(message, condition);
// then
// 结果取决于当前时间是否在工作时间内
assertNotNull(result);
}
// ========== 异常情况测试 ==========
@Test
public void testIsMatched_NullCondition() {
// given
IotDeviceMessage message = new IotDeviceMessage();
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_NullConditionType() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(null);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_InvalidOperator() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
condition.setOperator("invalid_operator");
condition.setParam("12:00:00");
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_InvalidTimeFormat() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),
"invalid-time-format"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_InvalidTimestampFormat() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
"invalid-timestamp"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_InvalidBetweenFormat() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
"09:00:00" // 缺少结束时间
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
// ========== 辅助方法 ==========
/**
* 创建日期时间条件
*/
private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) {
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
condition.setOperator(operator);
condition.setParam(param);
return condition;
}
/**
* 创建当日时间条件
*/
private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) {
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
condition.setOperator(operator);
condition.setParam(param);
return condition;
}
}

View File

@ -0,0 +1,391 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DevicePropertyConditionMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DevicePropertyConditionMatcherTest extends BaseMockitoUnitTest {
private DevicePropertyConditionMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DevicePropertyConditionMatcher();
}
@Test
public void testGetSupportedConditionType() {
// when & then
assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, matcher.getSupportedConditionType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(20, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_TemperatureEquals() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"temperature",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"25.5"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_HumidityGreaterThan() {
// given
Map<String, Object> properties = MapUtil.of("humidity", 75);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"humidity",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"70"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_PressureLessThan() {
// given
Map<String, Object> properties = MapUtil.of("pressure", 1010.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"pressure",
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
"1020"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_StatusNotEquals() {
// given
Map<String, Object> properties = MapUtil.of("status", "active");
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"status",
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
"inactive"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_PropertyMismatch() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 15.0);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_PropertyNotFound() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"humidity", // 不存在的属性
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"50"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullCondition() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullConditionType() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(null);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingIdentifier() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
condition.setIdentifier(null); // 缺少标识符
condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
condition.setParam("20");
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingOperator() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
condition.setIdentifier("temperature");
condition.setOperator(null); // 缺少操作符
condition.setParam("20");
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingParam() {
// given
Map<String, Object> properties = MapUtil.of("temperature", 25.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
condition.setIdentifier("temperature");
condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
condition.setParam(null); // 缺少参数
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessage() {
// given
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(null, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullDeviceProperties() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setParams(null);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_GreaterThanOrEquals() {
// given
Map<String, Object> properties = MapUtil.of("voltage", 12.0);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"voltage",
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
"12.0"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_LessThanOrEquals() {
// given
Map<String, Object> properties = MapUtil.of("current", 2.5);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"current",
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
"3.0"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_StringProperty() {
// given
Map<String, Object> properties = MapUtil.of("mode", "auto");
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"mode",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"auto"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_BooleanProperty() {
// given
Map<String, Object> properties = MapUtil.of("enabled", true);
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"enabled",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"true"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_MultipleProperties() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.put("humidity", 60)
.put("status", "active")
.put("enabled", true)
.build();
IotDeviceMessage message = createDeviceMessage(properties);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
"humidity",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"60"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
// ========== 辅助方法 ==========
/**
* 创建设备消息
*/
private IotDeviceMessage createDeviceMessage(Map<String, Object> properties) {
IotDeviceMessage message = new IotDeviceMessage();
message.setParams(properties);
return message;
}
/**
* 创建有效的条件
*/
private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) {
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
condition.setIdentifier(identifier);
condition.setOperator(operator);
condition.setParam(param);
return condition;
}
}

View File

@ -0,0 +1,334 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DeviceStateConditionMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DeviceStateConditionMatcherTest extends BaseMockitoUnitTest {
private DeviceStateConditionMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DeviceStateConditionMatcher();
}
@Test
public void testGetSupportedConditionType() {
// when & then
assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, matcher.getSupportedConditionType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(30, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_OnlineState() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_OfflineState() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.OFFLINE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_InactiveState() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.INACTIVE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.INACTIVE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_StateMismatch() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_NotEqualsOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_GreaterThanOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.OFFLINE.getState()); // 2
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString() // 1
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_LessThanOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.INACTIVE.getState()); // 0
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString() // 1
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_NullCondition() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullConditionType() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(null);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
condition.setOperator(null);
condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingParam() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());
condition.setParam(null);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessage() {
// given
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(null, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullDeviceState() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setParams(null);
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_GreaterThanOrEqualsOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); // 1
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString() // 1
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_LessThanOrEqualsOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); // 1
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString() // 2
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_InvalidOperator() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
condition.setOperator("invalid_operator");
condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_InvalidParamFormat() {
// given
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"invalid_state_value"
);
// when
boolean result = matcher.isMatched(message, condition);
// then
assertFalse(result);
}
// ========== 辅助方法 ==========
/**
* 创建设备消息
*/
private IotDeviceMessage createDeviceMessage(Integer deviceState) {
IotDeviceMessage message = new IotDeviceMessage();
message.setParams(deviceState);
return message;
}
/**
* 创建有效的条件
*/
private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) {
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
condition.setOperator(operator);
condition.setParam(param);
return condition;
}
}

View File

@ -0,0 +1,341 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DeviceEventPostTriggerMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DeviceEventPostTriggerMatcherTest extends BaseMockitoUnitTest {
private DeviceEventPostTriggerMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DeviceEventPostTriggerMatcher();
}
@Test
public void testGetSupportedTriggerType() {
// when & then
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, matcher.getSupportedTriggerType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(30, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_AlarmEvent() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.put("message", "Temperature too high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_ErrorEvent() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "error")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("code", 500)
.put("description", "System error")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("error");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_InfoEvent() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "info")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("status", "normal")
.put("timestamp", System.currentTimeMillis())
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("info");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_EventIdentifierMismatch() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("error"); // 不匹配的事件标识符
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_WrongMessageMethod() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法
message.setParams(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingIdentifier() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
trigger.setIdentifier(null); // 缺少标识符
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
message.setParams(null);
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_InvalidMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
message.setParams("invalid-params"); // 不是 Map 类型
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingEventIdentifierInParams() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build()) // 缺少 identifier 字段
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTrigger() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTriggerType() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "alarm")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(null);
trigger.setIdentifier("alarm");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_ComplexEventValue() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "maintenance")
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("type", "scheduled")
.put("duration", 120)
.put("components", new String[]{"motor", "sensor"})
.put("priority", "medium")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("maintenance");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_EmptyEventValue() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "heartbeat")
.put("value", MapUtil.of()) // 空的事件值
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("heartbeat");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_CaseInsensitiveIdentifier() {
// given
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "ALARM") // 大写
.put("value", MapUtil.builder(new HashMap<String, Object>())
.put("level", "high")
.build())
.build();
IotDeviceMessage message = createEventPostMessage(eventParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); // 小写
// when
boolean result = matcher.isMatched(message, trigger);
// then
// 根据实际实现这里可能需要调整期望结果
// 如果实现是大小写敏感的则应该为 false
assertFalse(result);
}
// ========== 辅助方法 ==========
/**
* 创建事件上报消息
*/
private IotDeviceMessage createEventPostMessage(Map<String, Object> eventParams) {
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
message.setParams(eventParams);
return message;
}
/**
* 创建有效的触发器
*/
private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
trigger.setIdentifier(identifier);
return trigger;
}
}

View File

@ -0,0 +1,298 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DevicePropertyPostTriggerMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DevicePropertyPostTriggerMatcherTest extends BaseMockitoUnitTest {
private DevicePropertyPostTriggerMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DevicePropertyPostTriggerMatcher();
}
@Test
public void testGetSupportedTriggerType() {
// when & then
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, matcher.getSupportedTriggerType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(20, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_TemperatureProperty() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_HumidityProperty() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("humidity", 60)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"humidity",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"60"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_PropertyMismatch() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 15.0)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_PropertyNotFound() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"humidity", // 不存在的属性
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"50"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_WrongMessageMethod() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.build();
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
message.setParams(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingIdentifier() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
trigger.setIdentifier(null); // 缺少标识符
trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
trigger.setValue("20");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
message.setParams(null);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_InvalidMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
message.setParams("invalid-params"); // 不是 Map 类型
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_LessThanOperator() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 15.0)
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"temperature",
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
"20"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_NotEqualsOperator() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("status", "active")
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"status",
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
"inactive"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_MultipleProperties() {
// given
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
.put("temperature", 25.5)
.put("humidity", 60)
.put("status", "active")
.build();
IotDeviceMessage message = createPropertyPostMessage(properties);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
"humidity",
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
"60"
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
// ========== 辅助方法 ==========
/**
* 创建属性上报消息
*/
private IotDeviceMessage createPropertyPostMessage(Map<String, Object> properties) {
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
message.setParams(properties);
return message;
}
/**
* 创建有效的触发器
*/
private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) {
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
trigger.setIdentifier(identifier);
trigger.setOperator(operator);
trigger.setValue(value);
return trigger;
}
}

View File

@ -0,0 +1,362 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DeviceServiceInvokeTriggerMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DeviceServiceInvokeTriggerMatcherTest extends BaseMockitoUnitTest {
private DeviceServiceInvokeTriggerMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DeviceServiceInvokeTriggerMatcher();
}
@Test
public void testGetSupportedTriggerType() {
// when & then
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, matcher.getSupportedTriggerType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(40, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_RestartService() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_ConfigService() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "config")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("interval", 30)
.put("enabled", true)
.put("threshold", 75.5)
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("config");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_UpdateService() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "update")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("version", "1.2.3")
.put("url", "http://example.com/firmware.bin")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("update");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_ServiceIdentifierMismatch() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("config"); // 不匹配的服务标识符
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_WrongMessageMethod() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法
message.setParams(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingIdentifier() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
trigger.setIdentifier(null); // 缺少标识符
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
message.setParams(null);
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_InvalidMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
message.setParams("invalid-params"); // 不是 Map 类型
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingServiceIdentifierInParams() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build()) // 缺少 identifier 字段
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTrigger() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTriggerType() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "restart")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(null);
trigger.setIdentifier("restart");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_EmptyInputData() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "ping")
.put("inputData", MapUtil.of()) // 空的输入数据
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("ping");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_NoInputData() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "status")
// 没有 inputData 字段
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("status");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_ComplexInputData() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "calibrate")
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("sensors", new String[]{"temperature", "humidity", "pressure"})
.put("precision", 0.01)
.put("duration", 300)
.put("autoSave", true)
.put("config", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "auto")
.put("level", "high")
.build())
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("calibrate");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_CaseInsensitiveIdentifier() {
// given
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
.put("identifier", "RESTART") // 大写
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
.put("mode", "soft")
.build())
.build();
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); // 小写
// when
boolean result = matcher.isMatched(message, trigger);
// then
// 根据实际实现这里可能需要调整期望结果
// 如果实现是大小写敏感的则应该为 false
assertFalse(result);
}
// ========== 辅助方法 ==========
/**
* 创建服务调用消息
*/
private IotDeviceMessage createServiceInvokeMessage(Map<String, Object> serviceParams) {
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
message.setParams(serviceParams);
return message;
}
/**
* 创建有效的触发器
*/
private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
trigger.setIdentifier(identifier);
return trigger;
}
}

View File

@ -0,0 +1,245 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link DeviceStateUpdateTriggerMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class DeviceStateUpdateTriggerMatcherTest extends BaseMockitoUnitTest {
private DeviceStateUpdateTriggerMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new DeviceStateUpdateTriggerMatcher();
}
@Test
public void testGetSupportedTriggerType() {
// when & then
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, matcher.getSupportedTriggerType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(10, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_OnlineState() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_OfflineState() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_StateMismatch() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTrigger() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTriggerType() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(null);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_WrongMessageMethod() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
message.setParams(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingOperator() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
trigger.setOperator(null);
trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString());
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_MissingValue() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());
trigger.setValue(null);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullMessageParams() {
// given
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
message.setParams(null);
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
IotDeviceStateEnum.ONLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_GreaterThanOperator() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
IotDeviceStateEnum.INACTIVE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_NotEqualsOperator() {
// given
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
IotSceneRuleDO.Trigger trigger = createValidTrigger(
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
IotDeviceStateEnum.OFFLINE.getState().toString()
);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
// ========== 辅助方法 ==========
/**
* 创建设备状态更新消息
*/
private IotDeviceMessage createStateUpdateMessage(Integer state) {
IotDeviceMessage message = new IotDeviceMessage();
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
message.setParams(state);
return message;
}
/**
* 创建有效的触发器
*/
private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) {
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
trigger.setOperator(operator);
trigger.setValue(value);
return trigger;
}
}

View File

@ -0,0 +1,240 @@
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
/**
* {@link TimerTriggerMatcher} 的单元测试类
*
* @author HUIHUI
*/
public class TimerTriggerMatcherTest extends BaseMockitoUnitTest {
private TimerTriggerMatcher matcher;
@BeforeEach
public void setUp() {
matcher = new TimerTriggerMatcher();
}
@Test
public void testGetSupportedTriggerType() {
// when & then
assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, matcher.getSupportedTriggerType());
}
@Test
public void testGetPriority() {
// when & then
assertEquals(50, matcher.getPriority());
}
@Test
public void testIsEnabled() {
// when & then
assertTrue(matcher.isEnabled());
}
@Test
public void testIsMatched_Success_ValidCronExpression() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * * ?"); // 每天中午12点
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_EveryMinuteCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 * * * * ?"); // 每分钟
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_WeekdaysCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9 ? * MON-FRI"); // 工作日上午9点
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_InvalidCronExpression() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("invalid-cron-expression");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_EmptyCronExpression() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullCronExpression() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
trigger.setCronExpression(null);
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTrigger() {
// given
IotDeviceMessage message = new IotDeviceMessage();
// when
boolean result = matcher.isMatched(message, null);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Failure_NullTriggerType() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(null);
trigger.setCronExpression("0 0 12 * * ?");
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_ComplexCronExpression() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 15 10 ? * 6#3"); // 每月第三个星期五上午10:15
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_IncorrectCronFormat() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * *"); // 缺少字段
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_SpecificDateCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 0 1 1 ? 2025"); // 2025年1月1日午夜
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Success_EverySecondCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("* * * * * ?"); // 每秒
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
@Test
public void testIsMatched_Failure_InvalidCharactersCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * * @ #"); // 包含无效字符
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertFalse(result);
}
@Test
public void testIsMatched_Success_RangeCron() {
// given
IotDeviceMessage message = new IotDeviceMessage();
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9-17 * * MON-FRI"); // 工作日9-17点
// when
boolean result = matcher.isMatched(message, trigger);
// then
assertTrue(result);
}
// ========== 辅助方法 ==========
/**
* 创建有效的定时触发器
*/
private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) {
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
trigger.setCronExpression(cronExpression);
return trigger;
}
}

View File

@ -20,6 +20,15 @@ mybatis-plus:
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
# 日志配置
logging:
level:
cn.iocoder.yudao.module.iot.service.rule.scene.matcher: DEBUG
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager: INFO
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition: DEBUG
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger: DEBUG
root: WARN
--- #################### 定时任务相关配置 ####################
--- #################### 配置中心相关配置 ####################

View File

@ -1,4 +1,37 @@
<configuration>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
<!-- 控制台输出 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 设置特定包的日志级别 -->
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<!-- 设置 IotSceneRuleMatcherManager 的日志级别 -->
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager" level="INFO"
additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<!-- 设置所有匹配器的日志级别 -->
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger" level="DEBUG" additivity="false">
<appender-ref ref="CONSOLE"/>
</logger>
<!-- 根日志级别 -->
<root level="WARN">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@ -185,6 +185,7 @@ logging:
cn.iocoder.yudao.module.erp.dal.mysql: debug
cn.iocoder.yudao.module.iot.dal.mysql: debug
cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG
cn.iocoder.yudao.module.iot.service.rule: debug
cn.iocoder.yudao.module.ai.dal.mysql: debug
org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿先禁用Spring Boot 3.X 存在部分错误的 WARN 提示