Commit 7661c7165c88c57039baee1d0db96a40de4cde31
1 parent
fb35807e
feat:【IoT 物联网】场景联动规则匹配器单元测试
Showing
13 changed files
with
2575 additions
and
260 deletions
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleServiceImpl.java
| @@ -2,18 +2,11 @@ package cn.iocoder.yudao.module.iot.service.rule.scene; | @@ -2,18 +2,11 @@ package cn.iocoder.yudao.module.iot.service.rule.scene; | ||
| 2 | 2 | ||
| 3 | import cn.hutool.core.collection.CollUtil; | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | import cn.hutool.core.collection.ListUtil; | 4 | import cn.hutool.core.collection.ListUtil; |
| 5 | -import cn.hutool.core.map.MapUtil; | ||
| 6 | -import cn.hutool.core.text.CharPool; | ||
| 7 | -import cn.hutool.core.util.NumberUtil; | ||
| 8 | import cn.hutool.core.util.ObjUtil; | 5 | import cn.hutool.core.util.ObjUtil; |
| 9 | -import cn.hutool.core.util.StrUtil; | ||
| 10 | import cn.hutool.extra.spring.SpringUtil; | 6 | import cn.hutool.extra.spring.SpringUtil; |
| 11 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | 7 | import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; |
| 12 | import cn.iocoder.yudao.framework.common.pojo.PageResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 13 | -import cn.iocoder.yudao.framework.common.util.number.NumberUtils; | ||
| 14 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.BeanUtils; |
| 15 | -import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | ||
| 16 | -import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils; | ||
| 17 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; | 10 | import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore; |
| 18 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; | 11 | import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils; |
| 19 | import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRulePageReqVO; | 12 | 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; | @@ -23,7 +16,6 @@ import cn.iocoder.yudao.module.iot.dal.dataobject.device.IotDeviceDO; | ||
| 23 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; | 16 | import cn.iocoder.yudao.module.iot.dal.dataobject.product.IotProductDO; |
| 24 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | 17 | import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; |
| 25 | import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper; | 18 | import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper; |
| 26 | -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 27 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | 19 | import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; |
| 28 | import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; | 20 | import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager; |
| 29 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; | 21 | import cn.iocoder.yudao.module.iot.service.device.IotDeviceService; |
| @@ -36,12 +28,9 @@ import org.springframework.stereotype.Service; | @@ -36,12 +28,9 @@ import org.springframework.stereotype.Service; | ||
| 36 | import org.springframework.validation.annotation.Validated; | 28 | import org.springframework.validation.annotation.Validated; |
| 37 | 29 | ||
| 38 | import java.util.Collection; | 30 | import java.util.Collection; |
| 39 | -import java.util.HashMap; | ||
| 40 | import java.util.List; | 31 | import java.util.List; |
| 41 | -import java.util.Map; | ||
| 42 | 32 | ||
| 43 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | 33 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 44 | -import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; | ||
| 45 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; | 34 | import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList; |
| 46 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS; | 35 | import static cn.iocoder.yudao.module.iot.enums.ErrorCodeConstants.RULE_SCENE_NOT_EXISTS; |
| 47 | 36 | ||
| @@ -353,65 +342,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { | @@ -353,65 +342,6 @@ public class IotSceneRuleServiceImpl implements IotSceneRuleService { | ||
| 353 | } | 342 | } |
| 354 | } | 343 | } |
| 355 | 344 | ||
| 356 | - // TODO @puhui999:下面还需要么? | ||
| 357 | - /** | ||
| 358 | - * 判断触发器的条件参数是否匹配 | ||
| 359 | - * | ||
| 360 | - * @param message 设备消息 | ||
| 361 | - * @param condition 触发条件 | ||
| 362 | - * @param sceneRule 规则场景(用于日志,无其它作用) | ||
| 363 | - * @param trigger 触发器(用于日志,无其它作用) | ||
| 364 | - * @return 是否匹配 | ||
| 365 | - */ | ||
| 366 | - @SuppressWarnings({"unchecked", "DataFlowIssue"}) | ||
| 367 | - private boolean isTriggerConditionParameterMatched(IotDeviceMessage message, IotSceneRuleDO.TriggerCondition condition, | ||
| 368 | - IotSceneRuleDO sceneRule, IotSceneRuleDO.Trigger trigger) { | ||
| 369 | - // 1.1 校验操作符是否合法 | ||
| 370 | - IotSceneRuleConditionOperatorEnum operator = | ||
| 371 | - IotSceneRuleConditionOperatorEnum.operatorOf(condition.getOperator()); | ||
| 372 | - if (operator == null) { | ||
| 373 | - log.error("[isTriggerConditionParameterMatched][规则场景编号({}) 的触发器({}) 存在错误的操作符({})]", | ||
| 374 | - sceneRule.getId(), trigger, condition.getOperator()); | ||
| 375 | - return false; | ||
| 376 | - } | ||
| 377 | - // 1.2 校验消息是否包含对应的值 | ||
| 378 | - String messageValue = MapUtil.getStr((Map<String, Object>) message.getData(), condition.getIdentifier()); | ||
| 379 | - if (messageValue == null) { | ||
| 380 | - return false; | ||
| 381 | - } | ||
| 382 | - | ||
| 383 | - // 2.1 构建 Spring 表达式的变量 | ||
| 384 | - Map<String, Object> springExpressionVariables = new HashMap<>(); | ||
| 385 | - try { | ||
| 386 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, messageValue); | ||
| 387 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, condition.getParam()); | ||
| 388 | - List<String> parameterValues = StrUtil.splitTrim(condition.getParam(), CharPool.COMMA); | ||
| 389 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, parameterValues); | ||
| 390 | - // 特殊:解决数字的比较。因为 Spring 是基于它的 compareTo 方法,对数字的比较存在问题! | ||
| 391 | - if (ObjectUtils.equalsAny(operator, IotSceneRuleConditionOperatorEnum.BETWEEN, | ||
| 392 | - IotSceneRuleConditionOperatorEnum.NOT_BETWEEN, | ||
| 393 | - IotSceneRuleConditionOperatorEnum.GREATER_THAN, | ||
| 394 | - IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS, | ||
| 395 | - IotSceneRuleConditionOperatorEnum.LESS_THAN, | ||
| 396 | - IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS) | ||
| 397 | - && NumberUtil.isNumber(messageValue) | ||
| 398 | - && NumberUtils.isAllNumber(parameterValues)) { | ||
| 399 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_SOURCE, | ||
| 400 | - NumberUtil.parseDouble(messageValue)); | ||
| 401 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE, | ||
| 402 | - NumberUtil.parseDouble(condition.getParam())); | ||
| 403 | - springExpressionVariables.put(IotSceneRuleConditionOperatorEnum.SPRING_EXPRESSION_VALUE_LIST, | ||
| 404 | - convertList(parameterValues, NumberUtil::parseDouble)); | ||
| 405 | - } | ||
| 406 | - // 2.2 计算 Spring 表达式 | ||
| 407 | - return (Boolean) SpringExpressionUtils.parseExpression(operator.getSpringExpression(), springExpressionVariables); | ||
| 408 | - } catch (Exception e) { | ||
| 409 | - log.error("[isTriggerConditionParameterMatched][消息({}) 规则场景编号({}) 的触发器({}) 的匹配表达式({}/{}) 计算异常]", | ||
| 410 | - message, sceneRule.getId(), trigger, operator, springExpressionVariables, e); | ||
| 411 | - return false; | ||
| 412 | - } | ||
| 413 | - } | ||
| 414 | - | ||
| 415 | /** | 345 | /** |
| 416 | * 执行规则场景的动作 | 346 | * 执行规则场景的动作 |
| 417 | * | 347 | * |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleTriggerMatcherTest.java deleted
| 1 | -package cn.iocoder.yudao.module.iot.service.rule.scene.matcher; | ||
| 2 | - | ||
| 3 | -import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | -import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | -import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 6 | -import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 7 | -import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger.*; | ||
| 8 | -import org.junit.jupiter.api.BeforeEach; | ||
| 9 | -import org.junit.jupiter.api.Test; | ||
| 10 | - | ||
| 11 | -import java.util.Arrays; | ||
| 12 | -import java.util.HashMap; | ||
| 13 | -import java.util.List; | ||
| 14 | - | ||
| 15 | -import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | - | ||
| 17 | -/** | ||
| 18 | - * IoT 场景规则触发器匹配器测试类 | ||
| 19 | - * | ||
| 20 | - * @author HUIHUI | ||
| 21 | - */ | ||
| 22 | -public class IotSceneRuleTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 23 | - | ||
| 24 | - // TODO @puhui999:public 都加下哈; | ||
| 25 | - | ||
| 26 | - private IotSceneRuleMatcherManager matcherManager; | ||
| 27 | - | ||
| 28 | - @BeforeEach | ||
| 29 | - void setUp() { | ||
| 30 | - // 创建所有匹配器实例 | ||
| 31 | - List<IotSceneRuleMatcher> matchers = Arrays.asList( | ||
| 32 | - new DeviceStateUpdateTriggerMatcher(), | ||
| 33 | - new DevicePropertyPostTriggerMatcher(), | ||
| 34 | - new DeviceEventPostTriggerMatcher(), | ||
| 35 | - new DeviceServiceInvokeTriggerMatcher(), | ||
| 36 | - new TimerTriggerMatcher() | ||
| 37 | - ); | ||
| 38 | - | ||
| 39 | - // 初始化匹配器管理器 | ||
| 40 | - matcherManager = new IotSceneRuleMatcherManager(matchers); | ||
| 41 | - } | ||
| 42 | - | ||
| 43 | - @Test | ||
| 44 | - void testDeviceStateUpdateTriggerMatcher() { | ||
| 45 | - // 1. 准备测试数据 | ||
| 46 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 47 | - .requestId("test-001") | ||
| 48 | - .method("thing.state.update") // TODO @puhui999:这里的枚举; | ||
| 49 | - .data(1) // 在线状态 TODO @puhui999:这里的枚举; | ||
| 50 | - .build(); | ||
| 51 | - | ||
| 52 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 53 | - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); | ||
| 54 | - trigger.setOperator("="); // TODO @puhui999:这里的枚举;下面也是类似; | ||
| 55 | - trigger.setValue("1"); | ||
| 56 | - | ||
| 57 | - // 2. 执行测试 | ||
| 58 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 59 | - | ||
| 60 | - // 3. 验证结果 | ||
| 61 | - assertTrue(matched, "设备状态更新触发器应该匹配"); | ||
| 62 | - } | ||
| 63 | - | ||
| 64 | - @Test | ||
| 65 | - void testDevicePropertyPostTriggerMatcher() { | ||
| 66 | - // 1. 准备测试数据 | ||
| 67 | - HashMap<String, Object> params = new HashMap<>(); | ||
| 68 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 69 | - .requestId("test-002") | ||
| 70 | - .method("thing.property.post") | ||
| 71 | - .data(25.5) // 温度值 | ||
| 72 | - .params(params) | ||
| 73 | - .build(); | ||
| 74 | - // 模拟标识符 | ||
| 75 | - params.put("identifier", "temperature"); | ||
| 76 | - | ||
| 77 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 78 | - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); | ||
| 79 | - trigger.setIdentifier("temperature"); | ||
| 80 | - trigger.setOperator(">"); | ||
| 81 | - trigger.setValue("20"); | ||
| 82 | - | ||
| 83 | - // 2. 执行测试 | ||
| 84 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 85 | - | ||
| 86 | - // 3. 验证结果 | ||
| 87 | - assertTrue(matched, "设备属性上报触发器应该匹配"); | ||
| 88 | - } | ||
| 89 | - | ||
| 90 | - @Test | ||
| 91 | - void testDeviceEventPostTriggerMatcher() { | ||
| 92 | - // 1. 准备测试数据 | ||
| 93 | - HashMap<String, Object> params = new HashMap<>(); | ||
| 94 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 95 | - .requestId("test-003") | ||
| 96 | - .method("thing.event.post") | ||
| 97 | - .data("alarm_data") | ||
| 98 | - .params(params) | ||
| 99 | - .build(); | ||
| 100 | - // 模拟标识符 | ||
| 101 | - params.put("identifier", "high_temperature_alarm"); | ||
| 102 | - | ||
| 103 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 104 | - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); | ||
| 105 | - trigger.setIdentifier("high_temperature_alarm"); | ||
| 106 | - | ||
| 107 | - // 2. 执行测试 | ||
| 108 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 109 | - | ||
| 110 | - // 3. 验证结果 | ||
| 111 | - assertTrue(matched, "设备事件上报触发器应该匹配"); | ||
| 112 | - } | ||
| 113 | - | ||
| 114 | - @Test | ||
| 115 | - void testDeviceServiceInvokeTriggerMatcher() { | ||
| 116 | - // 1. 准备测试数据 | ||
| 117 | - HashMap<String, Object> params = new HashMap<>(); | ||
| 118 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 119 | - .requestId("test-004") | ||
| 120 | - .method("thing.service.invoke") | ||
| 121 | - .msg("alarm_data") | ||
| 122 | - .params(params) | ||
| 123 | - .build(); | ||
| 124 | - // 模拟标识符 | ||
| 125 | - params.put("identifier", "restart_device"); | ||
| 126 | - | ||
| 127 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 128 | - trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); | ||
| 129 | - trigger.setIdentifier("restart_device"); | ||
| 130 | - | ||
| 131 | - // 2. 执行测试 | ||
| 132 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 133 | - | ||
| 134 | - // 3. 验证结果 | ||
| 135 | - assertTrue(matched, "设备服务调用触发器应该匹配"); | ||
| 136 | - } | ||
| 137 | - | ||
| 138 | - @Test | ||
| 139 | - void testTimerTriggerMatcher() { | ||
| 140 | - // 1. 准备测试数据 | ||
| 141 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 142 | - .requestId("test-005") | ||
| 143 | - .method("timer.trigger") // 定时触发器不依赖具体消息方法 | ||
| 144 | - .build(); | ||
| 145 | - | ||
| 146 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 147 | - trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); | ||
| 148 | - trigger.setCronExpression("0 0 12 * * ?"); // 每天中午12点 | ||
| 149 | - | ||
| 150 | - // 2. 执行测试 | ||
| 151 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 152 | - | ||
| 153 | - // 3. 验证结果 | ||
| 154 | - assertTrue(matched, "定时触发器应该匹配"); | ||
| 155 | - } | ||
| 156 | - | ||
| 157 | - @Test | ||
| 158 | - void testInvalidTriggerType() { | ||
| 159 | - // 1. 准备测试数据 | ||
| 160 | - IotDeviceMessage message = IotDeviceMessage.builder() | ||
| 161 | - .requestId("test-006") | ||
| 162 | - .method("unknown.method") | ||
| 163 | - .build(); | ||
| 164 | - | ||
| 165 | - IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 166 | - trigger.setType(999); // 无效的触发器类型 | ||
| 167 | - | ||
| 168 | - // 2. 执行测试 | ||
| 169 | - boolean matched = matcherManager.isMatched(message, trigger); | ||
| 170 | - | ||
| 171 | - // 3. 验证结果 | ||
| 172 | - assertFalse(matched, "无效的触发器类型应该不匹配"); | ||
| 173 | - } | ||
| 174 | - | ||
| 175 | - @Test | ||
| 176 | - void testGetSupportedTriggerTypes() { | ||
| 177 | - // 1. 执行测试 | ||
| 178 | - var supportedTypes = matcherManager.getSupportedTriggerTypes(); | ||
| 179 | - | ||
| 180 | - // 2. 验证结果 | ||
| 181 | - assertNotNull(supportedTypes); | ||
| 182 | - assertEquals(5, supportedTypes.size()); | ||
| 183 | - assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE)); | ||
| 184 | - assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST)); | ||
| 185 | - assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST)); | ||
| 186 | - assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE)); | ||
| 187 | - assertTrue(supportedTypes.contains(IotSceneRuleTriggerTypeEnum.TIMER)); | ||
| 188 | - } | ||
| 189 | - | ||
| 190 | -} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/CurrentTimeConditionMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 7 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; | ||
| 8 | +import org.junit.jupiter.api.BeforeEach; | ||
| 9 | +import org.junit.jupiter.api.Test; | ||
| 10 | + | ||
| 11 | +import java.time.LocalDateTime; | ||
| 12 | +import java.time.ZoneOffset; | ||
| 13 | + | ||
| 14 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 15 | + | ||
| 16 | +/** | ||
| 17 | + * {@link CurrentTimeConditionMatcher} 的单元测试类 | ||
| 18 | + * | ||
| 19 | + * @author HUIHUI | ||
| 20 | + */ | ||
| 21 | +public class CurrentTimeConditionMatcherTest extends BaseMockitoUnitTest { | ||
| 22 | + | ||
| 23 | + private CurrentTimeConditionMatcher matcher; | ||
| 24 | + | ||
| 25 | + @BeforeEach | ||
| 26 | + public void setUp() { | ||
| 27 | + matcher = new CurrentTimeConditionMatcher(); | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Test | ||
| 31 | + public void testGetSupportedConditionType() { | ||
| 32 | + // when & then | ||
| 33 | + assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, matcher.getSupportedConditionType()); | ||
| 34 | + } | ||
| 35 | + | ||
| 36 | + @Test | ||
| 37 | + public void testGetPriority() { | ||
| 38 | + // when & then | ||
| 39 | + assertEquals(40, matcher.getPriority()); | ||
| 40 | + } | ||
| 41 | + | ||
| 42 | + @Test | ||
| 43 | + public void testIsEnabled() { | ||
| 44 | + // when & then | ||
| 45 | + assertTrue(matcher.isEnabled()); | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + // ========== 时间戳条件测试 ========== | ||
| 49 | + | ||
| 50 | + @Test | ||
| 51 | + public void testIsMatched_DateTimeGreaterThan_Success() { | ||
| 52 | + // given | ||
| 53 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 54 | + long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 55 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 56 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), | ||
| 57 | + String.valueOf(pastTimestamp) | ||
| 58 | + ); | ||
| 59 | + | ||
| 60 | + // when | ||
| 61 | + boolean result = matcher.isMatched(message, condition); | ||
| 62 | + | ||
| 63 | + // then | ||
| 64 | + assertTrue(result); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + @Test | ||
| 68 | + public void testIsMatched_DateTimeGreaterThan_Failure() { | ||
| 69 | + // given | ||
| 70 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 71 | + long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 72 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 73 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), | ||
| 74 | + String.valueOf(futureTimestamp) | ||
| 75 | + ); | ||
| 76 | + | ||
| 77 | + // when | ||
| 78 | + boolean result = matcher.isMatched(message, condition); | ||
| 79 | + | ||
| 80 | + // then | ||
| 81 | + assertFalse(result); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + @Test | ||
| 85 | + public void testIsMatched_DateTimeLessThan_Success() { | ||
| 86 | + // given | ||
| 87 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 88 | + long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 89 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 90 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(), | ||
| 91 | + String.valueOf(futureTimestamp) | ||
| 92 | + ); | ||
| 93 | + | ||
| 94 | + // when | ||
| 95 | + boolean result = matcher.isMatched(message, condition); | ||
| 96 | + | ||
| 97 | + // then | ||
| 98 | + assertTrue(result); | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + @Test | ||
| 102 | + public void testIsMatched_DateTimeBetween_Success() { | ||
| 103 | + // given | ||
| 104 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 105 | + long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 106 | + long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 107 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 108 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), | ||
| 109 | + startTimestamp + "," + endTimestamp | ||
| 110 | + ); | ||
| 111 | + | ||
| 112 | + // when | ||
| 113 | + boolean result = matcher.isMatched(message, condition); | ||
| 114 | + | ||
| 115 | + // then | ||
| 116 | + assertTrue(result); | ||
| 117 | + } | ||
| 118 | + | ||
| 119 | + @Test | ||
| 120 | + public void testIsMatched_DateTimeBetween_Failure() { | ||
| 121 | + // given | ||
| 122 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 123 | + long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8")); | ||
| 124 | + long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of("+8")); | ||
| 125 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 126 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(), | ||
| 127 | + startTimestamp + "," + endTimestamp | ||
| 128 | + ); | ||
| 129 | + | ||
| 130 | + // when | ||
| 131 | + boolean result = matcher.isMatched(message, condition); | ||
| 132 | + | ||
| 133 | + // then | ||
| 134 | + assertFalse(result); | ||
| 135 | + } | ||
| 136 | + | ||
| 137 | + // ========== 当日时间条件测试 ========== | ||
| 138 | + | ||
| 139 | + @Test | ||
| 140 | + public void testIsMatched_TimeGreaterThan_EarlyMorning() { | ||
| 141 | + // given | ||
| 142 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 143 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 144 | + IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), | ||
| 145 | + "06:00:00" // 早上6点 | ||
| 146 | + ); | ||
| 147 | + | ||
| 148 | + // when | ||
| 149 | + boolean result = matcher.isMatched(message, condition); | ||
| 150 | + | ||
| 151 | + // then | ||
| 152 | + // 结果取决于当前时间,如果当前时间大于6点则为true | ||
| 153 | + assertNotNull(result); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Test | ||
| 157 | + public void testIsMatched_TimeLessThan_LateNight() { | ||
| 158 | + // given | ||
| 159 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 160 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 161 | + IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(), | ||
| 162 | + "23:59:59" // 晚上11点59分59秒 | ||
| 163 | + ); | ||
| 164 | + | ||
| 165 | + // when | ||
| 166 | + boolean result = matcher.isMatched(message, condition); | ||
| 167 | + | ||
| 168 | + // then | ||
| 169 | + // 大部分情况下应该为true,除非在午夜前1秒运行测试 | ||
| 170 | + assertNotNull(result); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + @Test | ||
| 174 | + public void testIsMatched_TimeBetween_AllDay() { | ||
| 175 | + // given | ||
| 176 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 177 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 178 | + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), | ||
| 179 | + "00:00:00,23:59:59" // 全天 | ||
| 180 | + ); | ||
| 181 | + | ||
| 182 | + // when | ||
| 183 | + boolean result = matcher.isMatched(message, condition); | ||
| 184 | + | ||
| 185 | + // then | ||
| 186 | + assertTrue(result); // 全天范围应该总是匹配 | ||
| 187 | + } | ||
| 188 | + | ||
| 189 | + @Test | ||
| 190 | + public void testIsMatched_TimeBetween_WorkingHours() { | ||
| 191 | + // given | ||
| 192 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 193 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 194 | + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), | ||
| 195 | + "09:00:00,17:00:00" // 工作时间 | ||
| 196 | + ); | ||
| 197 | + | ||
| 198 | + // when | ||
| 199 | + boolean result = matcher.isMatched(message, condition); | ||
| 200 | + | ||
| 201 | + // then | ||
| 202 | + // 结果取决于当前时间是否在工作时间内 | ||
| 203 | + assertNotNull(result); | ||
| 204 | + } | ||
| 205 | + | ||
| 206 | + // ========== 异常情况测试 ========== | ||
| 207 | + | ||
| 208 | + @Test | ||
| 209 | + public void testIsMatched_NullCondition() { | ||
| 210 | + // given | ||
| 211 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 212 | + | ||
| 213 | + // when | ||
| 214 | + boolean result = matcher.isMatched(message, null); | ||
| 215 | + | ||
| 216 | + // then | ||
| 217 | + assertFalse(result); | ||
| 218 | + } | ||
| 219 | + | ||
| 220 | + @Test | ||
| 221 | + public void testIsMatched_NullConditionType() { | ||
| 222 | + // given | ||
| 223 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 224 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 225 | + condition.setType(null); | ||
| 226 | + | ||
| 227 | + // when | ||
| 228 | + boolean result = matcher.isMatched(message, condition); | ||
| 229 | + | ||
| 230 | + // then | ||
| 231 | + assertFalse(result); | ||
| 232 | + } | ||
| 233 | + | ||
| 234 | + @Test | ||
| 235 | + public void testIsMatched_InvalidOperator() { | ||
| 236 | + // given | ||
| 237 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 238 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 239 | + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); | ||
| 240 | + condition.setOperator("invalid_operator"); | ||
| 241 | + condition.setParam("12:00:00"); | ||
| 242 | + | ||
| 243 | + // when | ||
| 244 | + boolean result = matcher.isMatched(message, condition); | ||
| 245 | + | ||
| 246 | + // then | ||
| 247 | + assertFalse(result); | ||
| 248 | + } | ||
| 249 | + | ||
| 250 | + @Test | ||
| 251 | + public void testIsMatched_InvalidTimeFormat() { | ||
| 252 | + // given | ||
| 253 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 254 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 255 | + IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(), | ||
| 256 | + "invalid-time-format" | ||
| 257 | + ); | ||
| 258 | + | ||
| 259 | + // when | ||
| 260 | + boolean result = matcher.isMatched(message, condition); | ||
| 261 | + | ||
| 262 | + // then | ||
| 263 | + assertFalse(result); | ||
| 264 | + } | ||
| 265 | + | ||
| 266 | + @Test | ||
| 267 | + public void testIsMatched_InvalidTimestampFormat() { | ||
| 268 | + // given | ||
| 269 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 270 | + IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition( | ||
| 271 | + IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(), | ||
| 272 | + "invalid-timestamp" | ||
| 273 | + ); | ||
| 274 | + | ||
| 275 | + // when | ||
| 276 | + boolean result = matcher.isMatched(message, condition); | ||
| 277 | + | ||
| 278 | + // then | ||
| 279 | + assertFalse(result); | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + @Test | ||
| 283 | + public void testIsMatched_InvalidBetweenFormat() { | ||
| 284 | + // given | ||
| 285 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 286 | + IotSceneRuleDO.TriggerCondition condition = createTimeCondition( | ||
| 287 | + IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(), | ||
| 288 | + "09:00:00" // 缺少结束时间 | ||
| 289 | + ); | ||
| 290 | + | ||
| 291 | + // when | ||
| 292 | + boolean result = matcher.isMatched(message, condition); | ||
| 293 | + | ||
| 294 | + // then | ||
| 295 | + assertFalse(result); | ||
| 296 | + } | ||
| 297 | + | ||
| 298 | + // ========== 辅助方法 ========== | ||
| 299 | + | ||
| 300 | + /** | ||
| 301 | + * 创建日期时间条件 | ||
| 302 | + */ | ||
| 303 | + private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) { | ||
| 304 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 305 | + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); | ||
| 306 | + condition.setOperator(operator); | ||
| 307 | + condition.setParam(param); | ||
| 308 | + return condition; | ||
| 309 | + } | ||
| 310 | + | ||
| 311 | + /** | ||
| 312 | + * 创建当日时间条件 | ||
| 313 | + */ | ||
| 314 | + private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) { | ||
| 315 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 316 | + condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType()); | ||
| 317 | + condition.setOperator(operator); | ||
| 318 | + condition.setParam(param); | ||
| 319 | + return condition; | ||
| 320 | + } | ||
| 321 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DevicePropertyConditionMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.map.MapUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; | ||
| 9 | +import org.junit.jupiter.api.BeforeEach; | ||
| 10 | +import org.junit.jupiter.api.Test; | ||
| 11 | + | ||
| 12 | +import java.util.HashMap; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * {@link DevicePropertyConditionMatcher} 的单元测试类 | ||
| 19 | + * | ||
| 20 | + * @author HUIHUI | ||
| 21 | + */ | ||
| 22 | +public class DevicePropertyConditionMatcherTest extends BaseMockitoUnitTest { | ||
| 23 | + | ||
| 24 | + private DevicePropertyConditionMatcher matcher; | ||
| 25 | + | ||
| 26 | + @BeforeEach | ||
| 27 | + public void setUp() { | ||
| 28 | + matcher = new DevicePropertyConditionMatcher(); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Test | ||
| 32 | + public void testGetSupportedConditionType() { | ||
| 33 | + // when & then | ||
| 34 | + assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, matcher.getSupportedConditionType()); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Test | ||
| 38 | + public void testGetPriority() { | ||
| 39 | + // when & then | ||
| 40 | + assertEquals(20, matcher.getPriority()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Test | ||
| 44 | + public void testIsEnabled() { | ||
| 45 | + // when & then | ||
| 46 | + assertTrue(matcher.isEnabled()); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Test | ||
| 50 | + public void testIsMatched_Success_TemperatureEquals() { | ||
| 51 | + // given | ||
| 52 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 53 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 54 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 55 | + "temperature", | ||
| 56 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 57 | + "25.5" | ||
| 58 | + ); | ||
| 59 | + | ||
| 60 | + // when | ||
| 61 | + boolean result = matcher.isMatched(message, condition); | ||
| 62 | + | ||
| 63 | + // then | ||
| 64 | + assertTrue(result); | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + @Test | ||
| 68 | + public void testIsMatched_Success_HumidityGreaterThan() { | ||
| 69 | + // given | ||
| 70 | + Map<String, Object> properties = MapUtil.of("humidity", 75); | ||
| 71 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 72 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 73 | + "humidity", | ||
| 74 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 75 | + "70" | ||
| 76 | + ); | ||
| 77 | + | ||
| 78 | + // when | ||
| 79 | + boolean result = matcher.isMatched(message, condition); | ||
| 80 | + | ||
| 81 | + // then | ||
| 82 | + assertTrue(result); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + @Test | ||
| 86 | + public void testIsMatched_Success_PressureLessThan() { | ||
| 87 | + // given | ||
| 88 | + Map<String, Object> properties = MapUtil.of("pressure", 1010.5); | ||
| 89 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 90 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 91 | + "pressure", | ||
| 92 | + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), | ||
| 93 | + "1020" | ||
| 94 | + ); | ||
| 95 | + | ||
| 96 | + // when | ||
| 97 | + boolean result = matcher.isMatched(message, condition); | ||
| 98 | + | ||
| 99 | + // then | ||
| 100 | + assertTrue(result); | ||
| 101 | + } | ||
| 102 | + | ||
| 103 | + @Test | ||
| 104 | + public void testIsMatched_Success_StatusNotEquals() { | ||
| 105 | + // given | ||
| 106 | + Map<String, Object> properties = MapUtil.of("status", "active"); | ||
| 107 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 108 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 109 | + "status", | ||
| 110 | + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), | ||
| 111 | + "inactive" | ||
| 112 | + ); | ||
| 113 | + | ||
| 114 | + // when | ||
| 115 | + boolean result = matcher.isMatched(message, condition); | ||
| 116 | + | ||
| 117 | + // then | ||
| 118 | + assertTrue(result); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Test | ||
| 122 | + public void testIsMatched_Failure_PropertyMismatch() { | ||
| 123 | + // given | ||
| 124 | + Map<String, Object> properties = MapUtil.of("temperature", 15.0); | ||
| 125 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 126 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 127 | + "temperature", | ||
| 128 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 129 | + "20" | ||
| 130 | + ); | ||
| 131 | + | ||
| 132 | + // when | ||
| 133 | + boolean result = matcher.isMatched(message, condition); | ||
| 134 | + | ||
| 135 | + // then | ||
| 136 | + assertFalse(result); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + @Test | ||
| 140 | + public void testIsMatched_Failure_PropertyNotFound() { | ||
| 141 | + // given | ||
| 142 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 143 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 144 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 145 | + "humidity", // 不存在的属性 | ||
| 146 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 147 | + "50" | ||
| 148 | + ); | ||
| 149 | + | ||
| 150 | + // when | ||
| 151 | + boolean result = matcher.isMatched(message, condition); | ||
| 152 | + | ||
| 153 | + // then | ||
| 154 | + assertFalse(result); | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + @Test | ||
| 158 | + public void testIsMatched_Failure_NullCondition() { | ||
| 159 | + // given | ||
| 160 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 161 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 162 | + | ||
| 163 | + // when | ||
| 164 | + boolean result = matcher.isMatched(message, null); | ||
| 165 | + | ||
| 166 | + // then | ||
| 167 | + assertFalse(result); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + @Test | ||
| 171 | + public void testIsMatched_Failure_NullConditionType() { | ||
| 172 | + // given | ||
| 173 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 174 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 175 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 176 | + condition.setType(null); | ||
| 177 | + | ||
| 178 | + // when | ||
| 179 | + boolean result = matcher.isMatched(message, condition); | ||
| 180 | + | ||
| 181 | + // then | ||
| 182 | + assertFalse(result); | ||
| 183 | + } | ||
| 184 | + | ||
| 185 | + @Test | ||
| 186 | + public void testIsMatched_Failure_MissingIdentifier() { | ||
| 187 | + // given | ||
| 188 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 189 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 190 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 191 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); | ||
| 192 | + condition.setIdentifier(null); // 缺少标识符 | ||
| 193 | + condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); | ||
| 194 | + condition.setParam("20"); | ||
| 195 | + | ||
| 196 | + // when | ||
| 197 | + boolean result = matcher.isMatched(message, condition); | ||
| 198 | + | ||
| 199 | + // then | ||
| 200 | + assertFalse(result); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + @Test | ||
| 204 | + public void testIsMatched_Failure_MissingOperator() { | ||
| 205 | + // given | ||
| 206 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 207 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 208 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 209 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); | ||
| 210 | + condition.setIdentifier("temperature"); | ||
| 211 | + condition.setOperator(null); // 缺少操作符 | ||
| 212 | + condition.setParam("20"); | ||
| 213 | + | ||
| 214 | + // when | ||
| 215 | + boolean result = matcher.isMatched(message, condition); | ||
| 216 | + | ||
| 217 | + // then | ||
| 218 | + assertFalse(result); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + @Test | ||
| 222 | + public void testIsMatched_Failure_MissingParam() { | ||
| 223 | + // given | ||
| 224 | + Map<String, Object> properties = MapUtil.of("temperature", 25.5); | ||
| 225 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 226 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 227 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); | ||
| 228 | + condition.setIdentifier("temperature"); | ||
| 229 | + condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); | ||
| 230 | + condition.setParam(null); // 缺少参数 | ||
| 231 | + | ||
| 232 | + // when | ||
| 233 | + boolean result = matcher.isMatched(message, condition); | ||
| 234 | + | ||
| 235 | + // then | ||
| 236 | + assertFalse(result); | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + @Test | ||
| 240 | + public void testIsMatched_Failure_NullMessage() { | ||
| 241 | + // given | ||
| 242 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 243 | + "temperature", | ||
| 244 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 245 | + "20" | ||
| 246 | + ); | ||
| 247 | + | ||
| 248 | + // when | ||
| 249 | + boolean result = matcher.isMatched(null, condition); | ||
| 250 | + | ||
| 251 | + // then | ||
| 252 | + assertFalse(result); | ||
| 253 | + } | ||
| 254 | + | ||
| 255 | + @Test | ||
| 256 | + public void testIsMatched_Failure_NullDeviceProperties() { | ||
| 257 | + // given | ||
| 258 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 259 | + message.setParams(null); | ||
| 260 | + | ||
| 261 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 262 | + "temperature", | ||
| 263 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 264 | + "20" | ||
| 265 | + ); | ||
| 266 | + | ||
| 267 | + // when | ||
| 268 | + boolean result = matcher.isMatched(message, condition); | ||
| 269 | + | ||
| 270 | + // then | ||
| 271 | + assertFalse(result); | ||
| 272 | + } | ||
| 273 | + | ||
| 274 | + @Test | ||
| 275 | + public void testIsMatched_Success_GreaterThanOrEquals() { | ||
| 276 | + // given | ||
| 277 | + Map<String, Object> properties = MapUtil.of("voltage", 12.0); | ||
| 278 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 279 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 280 | + "voltage", | ||
| 281 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), | ||
| 282 | + "12.0" | ||
| 283 | + ); | ||
| 284 | + | ||
| 285 | + // when | ||
| 286 | + boolean result = matcher.isMatched(message, condition); | ||
| 287 | + | ||
| 288 | + // then | ||
| 289 | + assertTrue(result); | ||
| 290 | + } | ||
| 291 | + | ||
| 292 | + @Test | ||
| 293 | + public void testIsMatched_Success_LessThanOrEquals() { | ||
| 294 | + // given | ||
| 295 | + Map<String, Object> properties = MapUtil.of("current", 2.5); | ||
| 296 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 297 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 298 | + "current", | ||
| 299 | + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), | ||
| 300 | + "3.0" | ||
| 301 | + ); | ||
| 302 | + | ||
| 303 | + // when | ||
| 304 | + boolean result = matcher.isMatched(message, condition); | ||
| 305 | + | ||
| 306 | + // then | ||
| 307 | + assertTrue(result); | ||
| 308 | + } | ||
| 309 | + | ||
| 310 | + @Test | ||
| 311 | + public void testIsMatched_Success_StringProperty() { | ||
| 312 | + // given | ||
| 313 | + Map<String, Object> properties = MapUtil.of("mode", "auto"); | ||
| 314 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 315 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 316 | + "mode", | ||
| 317 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 318 | + "auto" | ||
| 319 | + ); | ||
| 320 | + | ||
| 321 | + // when | ||
| 322 | + boolean result = matcher.isMatched(message, condition); | ||
| 323 | + | ||
| 324 | + // then | ||
| 325 | + assertTrue(result); | ||
| 326 | + } | ||
| 327 | + | ||
| 328 | + @Test | ||
| 329 | + public void testIsMatched_Success_BooleanProperty() { | ||
| 330 | + // given | ||
| 331 | + Map<String, Object> properties = MapUtil.of("enabled", true); | ||
| 332 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 333 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 334 | + "enabled", | ||
| 335 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 336 | + "true" | ||
| 337 | + ); | ||
| 338 | + | ||
| 339 | + // when | ||
| 340 | + boolean result = matcher.isMatched(message, condition); | ||
| 341 | + | ||
| 342 | + // then | ||
| 343 | + assertTrue(result); | ||
| 344 | + } | ||
| 345 | + | ||
| 346 | + @Test | ||
| 347 | + public void testIsMatched_Success_MultipleProperties() { | ||
| 348 | + // given | ||
| 349 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 350 | + .put("temperature", 25.5) | ||
| 351 | + .put("humidity", 60) | ||
| 352 | + .put("status", "active") | ||
| 353 | + .put("enabled", true) | ||
| 354 | + .build(); | ||
| 355 | + IotDeviceMessage message = createDeviceMessage(properties); | ||
| 356 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 357 | + "humidity", | ||
| 358 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 359 | + "60" | ||
| 360 | + ); | ||
| 361 | + | ||
| 362 | + // when | ||
| 363 | + boolean result = matcher.isMatched(message, condition); | ||
| 364 | + | ||
| 365 | + // then | ||
| 366 | + assertTrue(result); | ||
| 367 | + } | ||
| 368 | + | ||
| 369 | + // ========== 辅助方法 ========== | ||
| 370 | + | ||
| 371 | + /** | ||
| 372 | + * 创建设备消息 | ||
| 373 | + */ | ||
| 374 | + private IotDeviceMessage createDeviceMessage(Map<String, Object> properties) { | ||
| 375 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 376 | + message.setParams(properties); | ||
| 377 | + return message; | ||
| 378 | + } | ||
| 379 | + | ||
| 380 | + /** | ||
| 381 | + * 创建有效的条件 | ||
| 382 | + */ | ||
| 383 | + private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) { | ||
| 384 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 385 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType()); | ||
| 386 | + condition.setIdentifier(identifier); | ||
| 387 | + condition.setOperator(operator); | ||
| 388 | + condition.setParam(param); | ||
| 389 | + return condition; | ||
| 390 | + } | ||
| 391 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/DeviceStateConditionMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 6 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 7 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum; | ||
| 9 | +import org.junit.jupiter.api.BeforeEach; | ||
| 10 | +import org.junit.jupiter.api.Test; | ||
| 11 | + | ||
| 12 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * {@link DeviceStateConditionMatcher} 的单元测试类 | ||
| 16 | + * | ||
| 17 | + * @author HUIHUI | ||
| 18 | + */ | ||
| 19 | +public class DeviceStateConditionMatcherTest extends BaseMockitoUnitTest { | ||
| 20 | + | ||
| 21 | + private DeviceStateConditionMatcher matcher; | ||
| 22 | + | ||
| 23 | + @BeforeEach | ||
| 24 | + public void setUp() { | ||
| 25 | + matcher = new DeviceStateConditionMatcher(); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Test | ||
| 29 | + public void testGetSupportedConditionType() { | ||
| 30 | + // when & then | ||
| 31 | + assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, matcher.getSupportedConditionType()); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Test | ||
| 35 | + public void testGetPriority() { | ||
| 36 | + // when & then | ||
| 37 | + assertEquals(30, matcher.getPriority()); | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Test | ||
| 41 | + public void testIsEnabled() { | ||
| 42 | + // when & then | ||
| 43 | + assertTrue(matcher.isEnabled()); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + @Test | ||
| 47 | + public void testIsMatched_Success_OnlineState() { | ||
| 48 | + // given | ||
| 49 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 50 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 51 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 52 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 53 | + ); | ||
| 54 | + | ||
| 55 | + // when | ||
| 56 | + boolean result = matcher.isMatched(message, condition); | ||
| 57 | + | ||
| 58 | + // then | ||
| 59 | + assertTrue(result); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + @Test | ||
| 63 | + public void testIsMatched_Success_OfflineState() { | ||
| 64 | + // given | ||
| 65 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.OFFLINE.getState()); | ||
| 66 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 67 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 68 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 69 | + ); | ||
| 70 | + | ||
| 71 | + // when | ||
| 72 | + boolean result = matcher.isMatched(message, condition); | ||
| 73 | + | ||
| 74 | + // then | ||
| 75 | + assertTrue(result); | ||
| 76 | + } | ||
| 77 | + | ||
| 78 | + @Test | ||
| 79 | + public void testIsMatched_Success_InactiveState() { | ||
| 80 | + // given | ||
| 81 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.INACTIVE.getState()); | ||
| 82 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 83 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 84 | + IotDeviceStateEnum.INACTIVE.getState().toString() | ||
| 85 | + ); | ||
| 86 | + | ||
| 87 | + // when | ||
| 88 | + boolean result = matcher.isMatched(message, condition); | ||
| 89 | + | ||
| 90 | + // then | ||
| 91 | + assertTrue(result); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | + @Test | ||
| 95 | + public void testIsMatched_Failure_StateMismatch() { | ||
| 96 | + // given | ||
| 97 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 98 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 99 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 100 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 101 | + ); | ||
| 102 | + | ||
| 103 | + // when | ||
| 104 | + boolean result = matcher.isMatched(message, condition); | ||
| 105 | + | ||
| 106 | + // then | ||
| 107 | + assertFalse(result); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + @Test | ||
| 111 | + public void testIsMatched_Success_NotEqualsOperator() { | ||
| 112 | + // given | ||
| 113 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 114 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 115 | + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), | ||
| 116 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 117 | + ); | ||
| 118 | + | ||
| 119 | + // when | ||
| 120 | + boolean result = matcher.isMatched(message, condition); | ||
| 121 | + | ||
| 122 | + // then | ||
| 123 | + assertTrue(result); | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + @Test | ||
| 127 | + public void testIsMatched_Success_GreaterThanOperator() { | ||
| 128 | + // given | ||
| 129 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.OFFLINE.getState()); // 2 | ||
| 130 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 131 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 132 | + IotDeviceStateEnum.ONLINE.getState().toString() // 1 | ||
| 133 | + ); | ||
| 134 | + | ||
| 135 | + // when | ||
| 136 | + boolean result = matcher.isMatched(message, condition); | ||
| 137 | + | ||
| 138 | + // then | ||
| 139 | + assertTrue(result); | ||
| 140 | + } | ||
| 141 | + | ||
| 142 | + @Test | ||
| 143 | + public void testIsMatched_Success_LessThanOperator() { | ||
| 144 | + // given | ||
| 145 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.INACTIVE.getState()); // 0 | ||
| 146 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 147 | + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), | ||
| 148 | + IotDeviceStateEnum.ONLINE.getState().toString() // 1 | ||
| 149 | + ); | ||
| 150 | + | ||
| 151 | + // when | ||
| 152 | + boolean result = matcher.isMatched(message, condition); | ||
| 153 | + | ||
| 154 | + // then | ||
| 155 | + assertTrue(result); | ||
| 156 | + } | ||
| 157 | + | ||
| 158 | + @Test | ||
| 159 | + public void testIsMatched_Failure_NullCondition() { | ||
| 160 | + // given | ||
| 161 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 162 | + | ||
| 163 | + // when | ||
| 164 | + boolean result = matcher.isMatched(message, null); | ||
| 165 | + | ||
| 166 | + // then | ||
| 167 | + assertFalse(result); | ||
| 168 | + } | ||
| 169 | + | ||
| 170 | + @Test | ||
| 171 | + public void testIsMatched_Failure_NullConditionType() { | ||
| 172 | + // given | ||
| 173 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 174 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 175 | + condition.setType(null); | ||
| 176 | + | ||
| 177 | + // when | ||
| 178 | + boolean result = matcher.isMatched(message, condition); | ||
| 179 | + | ||
| 180 | + // then | ||
| 181 | + assertFalse(result); | ||
| 182 | + } | ||
| 183 | + | ||
| 184 | + @Test | ||
| 185 | + public void testIsMatched_Failure_MissingOperator() { | ||
| 186 | + // given | ||
| 187 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 188 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 189 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); | ||
| 190 | + condition.setOperator(null); | ||
| 191 | + condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); | ||
| 192 | + | ||
| 193 | + // when | ||
| 194 | + boolean result = matcher.isMatched(message, condition); | ||
| 195 | + | ||
| 196 | + // then | ||
| 197 | + assertFalse(result); | ||
| 198 | + } | ||
| 199 | + | ||
| 200 | + @Test | ||
| 201 | + public void testIsMatched_Failure_MissingParam() { | ||
| 202 | + // given | ||
| 203 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 204 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 205 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); | ||
| 206 | + condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); | ||
| 207 | + condition.setParam(null); | ||
| 208 | + | ||
| 209 | + // when | ||
| 210 | + boolean result = matcher.isMatched(message, condition); | ||
| 211 | + | ||
| 212 | + // then | ||
| 213 | + assertFalse(result); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + @Test | ||
| 217 | + public void testIsMatched_Failure_NullMessage() { | ||
| 218 | + // given | ||
| 219 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 220 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 221 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 222 | + ); | ||
| 223 | + | ||
| 224 | + // when | ||
| 225 | + boolean result = matcher.isMatched(null, condition); | ||
| 226 | + | ||
| 227 | + // then | ||
| 228 | + assertFalse(result); | ||
| 229 | + } | ||
| 230 | + | ||
| 231 | + @Test | ||
| 232 | + public void testIsMatched_Failure_NullDeviceState() { | ||
| 233 | + // given | ||
| 234 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 235 | + message.setParams(null); | ||
| 236 | + | ||
| 237 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 238 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 239 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 240 | + ); | ||
| 241 | + | ||
| 242 | + // when | ||
| 243 | + boolean result = matcher.isMatched(message, condition); | ||
| 244 | + | ||
| 245 | + // then | ||
| 246 | + assertFalse(result); | ||
| 247 | + } | ||
| 248 | + | ||
| 249 | + @Test | ||
| 250 | + public void testIsMatched_Success_GreaterThanOrEqualsOperator() { | ||
| 251 | + // given | ||
| 252 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); // 1 | ||
| 253 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 254 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(), | ||
| 255 | + IotDeviceStateEnum.ONLINE.getState().toString() // 1 | ||
| 256 | + ); | ||
| 257 | + | ||
| 258 | + // when | ||
| 259 | + boolean result = matcher.isMatched(message, condition); | ||
| 260 | + | ||
| 261 | + // then | ||
| 262 | + assertTrue(result); | ||
| 263 | + } | ||
| 264 | + | ||
| 265 | + @Test | ||
| 266 | + public void testIsMatched_Success_LessThanOrEqualsOperator() { | ||
| 267 | + // given | ||
| 268 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); // 1 | ||
| 269 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 270 | + IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(), | ||
| 271 | + IotDeviceStateEnum.OFFLINE.getState().toString() // 2 | ||
| 272 | + ); | ||
| 273 | + | ||
| 274 | + // when | ||
| 275 | + boolean result = matcher.isMatched(message, condition); | ||
| 276 | + | ||
| 277 | + // then | ||
| 278 | + assertTrue(result); | ||
| 279 | + } | ||
| 280 | + | ||
| 281 | + @Test | ||
| 282 | + public void testIsMatched_Failure_InvalidOperator() { | ||
| 283 | + // given | ||
| 284 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 285 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 286 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); | ||
| 287 | + condition.setOperator("invalid_operator"); | ||
| 288 | + condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString()); | ||
| 289 | + | ||
| 290 | + // when | ||
| 291 | + boolean result = matcher.isMatched(message, condition); | ||
| 292 | + | ||
| 293 | + // then | ||
| 294 | + assertFalse(result); | ||
| 295 | + } | ||
| 296 | + | ||
| 297 | + @Test | ||
| 298 | + public void testIsMatched_Failure_InvalidParamFormat() { | ||
| 299 | + // given | ||
| 300 | + IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 301 | + IotSceneRuleDO.TriggerCondition condition = createValidCondition( | ||
| 302 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 303 | + "invalid_state_value" | ||
| 304 | + ); | ||
| 305 | + | ||
| 306 | + // when | ||
| 307 | + boolean result = matcher.isMatched(message, condition); | ||
| 308 | + | ||
| 309 | + // then | ||
| 310 | + assertFalse(result); | ||
| 311 | + } | ||
| 312 | + | ||
| 313 | + // ========== 辅助方法 ========== | ||
| 314 | + | ||
| 315 | + /** | ||
| 316 | + * 创建设备消息 | ||
| 317 | + */ | ||
| 318 | + private IotDeviceMessage createDeviceMessage(Integer deviceState) { | ||
| 319 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 320 | + message.setParams(deviceState); | ||
| 321 | + return message; | ||
| 322 | + } | ||
| 323 | + | ||
| 324 | + /** | ||
| 325 | + * 创建有效的条件 | ||
| 326 | + */ | ||
| 327 | + private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) { | ||
| 328 | + IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition(); | ||
| 329 | + condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType()); | ||
| 330 | + condition.setOperator(operator); | ||
| 331 | + condition.setParam(param); | ||
| 332 | + return condition; | ||
| 333 | + } | ||
| 334 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceEventPostTriggerMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.map.MapUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 9 | +import org.junit.jupiter.api.BeforeEach; | ||
| 10 | +import org.junit.jupiter.api.Test; | ||
| 11 | + | ||
| 12 | +import java.util.HashMap; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * {@link DeviceEventPostTriggerMatcher} 的单元测试类 | ||
| 19 | + * | ||
| 20 | + * @author HUIHUI | ||
| 21 | + */ | ||
| 22 | +public class DeviceEventPostTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 23 | + | ||
| 24 | + private DeviceEventPostTriggerMatcher matcher; | ||
| 25 | + | ||
| 26 | + @BeforeEach | ||
| 27 | + public void setUp() { | ||
| 28 | + matcher = new DeviceEventPostTriggerMatcher(); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Test | ||
| 32 | + public void testGetSupportedTriggerType() { | ||
| 33 | + // when & then | ||
| 34 | + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, matcher.getSupportedTriggerType()); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Test | ||
| 38 | + public void testGetPriority() { | ||
| 39 | + // when & then | ||
| 40 | + assertEquals(30, matcher.getPriority()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Test | ||
| 44 | + public void testIsEnabled() { | ||
| 45 | + // when & then | ||
| 46 | + assertTrue(matcher.isEnabled()); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Test | ||
| 50 | + public void testIsMatched_Success_AlarmEvent() { | ||
| 51 | + // given | ||
| 52 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 53 | + .put("identifier", "alarm") | ||
| 54 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 55 | + .put("level", "high") | ||
| 56 | + .put("message", "Temperature too high") | ||
| 57 | + .build()) | ||
| 58 | + .build(); | ||
| 59 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 60 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); | ||
| 61 | + | ||
| 62 | + // when | ||
| 63 | + boolean result = matcher.isMatched(message, trigger); | ||
| 64 | + | ||
| 65 | + // then | ||
| 66 | + assertTrue(result); | ||
| 67 | + } | ||
| 68 | + | ||
| 69 | + @Test | ||
| 70 | + public void testIsMatched_Success_ErrorEvent() { | ||
| 71 | + // given | ||
| 72 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 73 | + .put("identifier", "error") | ||
| 74 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 75 | + .put("code", 500) | ||
| 76 | + .put("description", "System error") | ||
| 77 | + .build()) | ||
| 78 | + .build(); | ||
| 79 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 80 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("error"); | ||
| 81 | + | ||
| 82 | + // when | ||
| 83 | + boolean result = matcher.isMatched(message, trigger); | ||
| 84 | + | ||
| 85 | + // then | ||
| 86 | + assertTrue(result); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @Test | ||
| 90 | + public void testIsMatched_Success_InfoEvent() { | ||
| 91 | + // given | ||
| 92 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 93 | + .put("identifier", "info") | ||
| 94 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 95 | + .put("status", "normal") | ||
| 96 | + .put("timestamp", System.currentTimeMillis()) | ||
| 97 | + .build()) | ||
| 98 | + .build(); | ||
| 99 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 100 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("info"); | ||
| 101 | + | ||
| 102 | + // when | ||
| 103 | + boolean result = matcher.isMatched(message, trigger); | ||
| 104 | + | ||
| 105 | + // then | ||
| 106 | + assertTrue(result); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Test | ||
| 110 | + public void testIsMatched_Failure_EventIdentifierMismatch() { | ||
| 111 | + // given | ||
| 112 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 113 | + .put("identifier", "alarm") | ||
| 114 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 115 | + .put("level", "high") | ||
| 116 | + .build()) | ||
| 117 | + .build(); | ||
| 118 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 119 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("error"); // 不匹配的事件标识符 | ||
| 120 | + | ||
| 121 | + // when | ||
| 122 | + boolean result = matcher.isMatched(message, trigger); | ||
| 123 | + | ||
| 124 | + // then | ||
| 125 | + assertFalse(result); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Test | ||
| 129 | + public void testIsMatched_Failure_WrongMessageMethod() { | ||
| 130 | + // given | ||
| 131 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 132 | + .put("identifier", "alarm") | ||
| 133 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 134 | + .put("level", "high") | ||
| 135 | + .build()) | ||
| 136 | + .build(); | ||
| 137 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 138 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 | ||
| 139 | + message.setParams(eventParams); | ||
| 140 | + | ||
| 141 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); | ||
| 142 | + | ||
| 143 | + // when | ||
| 144 | + boolean result = matcher.isMatched(message, trigger); | ||
| 145 | + | ||
| 146 | + // then | ||
| 147 | + assertFalse(result); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @Test | ||
| 151 | + public void testIsMatched_Failure_MissingIdentifier() { | ||
| 152 | + // given | ||
| 153 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 154 | + .put("identifier", "alarm") | ||
| 155 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 156 | + .put("level", "high") | ||
| 157 | + .build()) | ||
| 158 | + .build(); | ||
| 159 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 160 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 161 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); | ||
| 162 | + trigger.setIdentifier(null); // 缺少标识符 | ||
| 163 | + | ||
| 164 | + // when | ||
| 165 | + boolean result = matcher.isMatched(message, trigger); | ||
| 166 | + | ||
| 167 | + // then | ||
| 168 | + assertFalse(result); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Test | ||
| 172 | + public void testIsMatched_Failure_NullMessageParams() { | ||
| 173 | + // given | ||
| 174 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 175 | + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); | ||
| 176 | + message.setParams(null); | ||
| 177 | + | ||
| 178 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); | ||
| 179 | + | ||
| 180 | + // when | ||
| 181 | + boolean result = matcher.isMatched(message, trigger); | ||
| 182 | + | ||
| 183 | + // then | ||
| 184 | + assertFalse(result); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + @Test | ||
| 188 | + public void testIsMatched_Failure_InvalidMessageParams() { | ||
| 189 | + // given | ||
| 190 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 191 | + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); | ||
| 192 | + message.setParams("invalid-params"); // 不是 Map 类型 | ||
| 193 | + | ||
| 194 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); | ||
| 195 | + | ||
| 196 | + // when | ||
| 197 | + boolean result = matcher.isMatched(message, trigger); | ||
| 198 | + | ||
| 199 | + // then | ||
| 200 | + assertFalse(result); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + @Test | ||
| 204 | + public void testIsMatched_Failure_MissingEventIdentifierInParams() { | ||
| 205 | + // given | ||
| 206 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 207 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 208 | + .put("level", "high") | ||
| 209 | + .build()) // 缺少 identifier 字段 | ||
| 210 | + .build(); | ||
| 211 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 212 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); | ||
| 213 | + | ||
| 214 | + // when | ||
| 215 | + boolean result = matcher.isMatched(message, trigger); | ||
| 216 | + | ||
| 217 | + // then | ||
| 218 | + assertFalse(result); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + @Test | ||
| 222 | + public void testIsMatched_Failure_NullTrigger() { | ||
| 223 | + // given | ||
| 224 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 225 | + .put("identifier", "alarm") | ||
| 226 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 227 | + .put("level", "high") | ||
| 228 | + .build()) | ||
| 229 | + .build(); | ||
| 230 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 231 | + | ||
| 232 | + // when | ||
| 233 | + boolean result = matcher.isMatched(message, null); | ||
| 234 | + | ||
| 235 | + // then | ||
| 236 | + assertFalse(result); | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + @Test | ||
| 240 | + public void testIsMatched_Failure_NullTriggerType() { | ||
| 241 | + // given | ||
| 242 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 243 | + .put("identifier", "alarm") | ||
| 244 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 245 | + .put("level", "high") | ||
| 246 | + .build()) | ||
| 247 | + .build(); | ||
| 248 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 249 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 250 | + trigger.setType(null); | ||
| 251 | + trigger.setIdentifier("alarm"); | ||
| 252 | + | ||
| 253 | + // when | ||
| 254 | + boolean result = matcher.isMatched(message, trigger); | ||
| 255 | + | ||
| 256 | + // then | ||
| 257 | + assertFalse(result); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + @Test | ||
| 261 | + public void testIsMatched_Success_ComplexEventValue() { | ||
| 262 | + // given | ||
| 263 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 264 | + .put("identifier", "maintenance") | ||
| 265 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 266 | + .put("type", "scheduled") | ||
| 267 | + .put("duration", 120) | ||
| 268 | + .put("components", new String[]{"motor", "sensor"}) | ||
| 269 | + .put("priority", "medium") | ||
| 270 | + .build()) | ||
| 271 | + .build(); | ||
| 272 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 273 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("maintenance"); | ||
| 274 | + | ||
| 275 | + // when | ||
| 276 | + boolean result = matcher.isMatched(message, trigger); | ||
| 277 | + | ||
| 278 | + // then | ||
| 279 | + assertTrue(result); | ||
| 280 | + } | ||
| 281 | + | ||
| 282 | + @Test | ||
| 283 | + public void testIsMatched_Success_EmptyEventValue() { | ||
| 284 | + // given | ||
| 285 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 286 | + .put("identifier", "heartbeat") | ||
| 287 | + .put("value", MapUtil.of()) // 空的事件值 | ||
| 288 | + .build(); | ||
| 289 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 290 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("heartbeat"); | ||
| 291 | + | ||
| 292 | + // when | ||
| 293 | + boolean result = matcher.isMatched(message, trigger); | ||
| 294 | + | ||
| 295 | + // then | ||
| 296 | + assertTrue(result); | ||
| 297 | + } | ||
| 298 | + | ||
| 299 | + @Test | ||
| 300 | + public void testIsMatched_Success_CaseInsensitiveIdentifier() { | ||
| 301 | + // given | ||
| 302 | + Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 303 | + .put("identifier", "ALARM") // 大写 | ||
| 304 | + .put("value", MapUtil.builder(new HashMap<String, Object>()) | ||
| 305 | + .put("level", "high") | ||
| 306 | + .build()) | ||
| 307 | + .build(); | ||
| 308 | + IotDeviceMessage message = createEventPostMessage(eventParams); | ||
| 309 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("alarm"); // 小写 | ||
| 310 | + | ||
| 311 | + // when | ||
| 312 | + boolean result = matcher.isMatched(message, trigger); | ||
| 313 | + | ||
| 314 | + // then | ||
| 315 | + // 根据实际实现,这里可能需要调整期望结果 | ||
| 316 | + // 如果实现是大小写敏感的,则应该为 false | ||
| 317 | + assertFalse(result); | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + // ========== 辅助方法 ========== | ||
| 321 | + | ||
| 322 | + /** | ||
| 323 | + * 创建事件上报消息 | ||
| 324 | + */ | ||
| 325 | + private IotDeviceMessage createEventPostMessage(Map<String, Object> eventParams) { | ||
| 326 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 327 | + message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod()); | ||
| 328 | + message.setParams(eventParams); | ||
| 329 | + return message; | ||
| 330 | + } | ||
| 331 | + | ||
| 332 | + /** | ||
| 333 | + * 创建有效的触发器 | ||
| 334 | + */ | ||
| 335 | + private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { | ||
| 336 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 337 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType()); | ||
| 338 | + trigger.setIdentifier(identifier); | ||
| 339 | + return trigger; | ||
| 340 | + } | ||
| 341 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DevicePropertyPostTriggerMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.map.MapUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 9 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 10 | +import org.junit.jupiter.api.BeforeEach; | ||
| 11 | +import org.junit.jupiter.api.Test; | ||
| 12 | + | ||
| 13 | +import java.util.HashMap; | ||
| 14 | +import java.util.Map; | ||
| 15 | + | ||
| 16 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 17 | + | ||
| 18 | +/** | ||
| 19 | + * {@link DevicePropertyPostTriggerMatcher} 的单元测试类 | ||
| 20 | + * | ||
| 21 | + * @author HUIHUI | ||
| 22 | + */ | ||
| 23 | +public class DevicePropertyPostTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 24 | + | ||
| 25 | + private DevicePropertyPostTriggerMatcher matcher; | ||
| 26 | + | ||
| 27 | + @BeforeEach | ||
| 28 | + public void setUp() { | ||
| 29 | + matcher = new DevicePropertyPostTriggerMatcher(); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Test | ||
| 33 | + public void testGetSupportedTriggerType() { | ||
| 34 | + // when & then | ||
| 35 | + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, matcher.getSupportedTriggerType()); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Test | ||
| 39 | + public void testGetPriority() { | ||
| 40 | + // when & then | ||
| 41 | + assertEquals(20, matcher.getPriority()); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Test | ||
| 45 | + public void testIsEnabled() { | ||
| 46 | + // when & then | ||
| 47 | + assertTrue(matcher.isEnabled()); | ||
| 48 | + } | ||
| 49 | + | ||
| 50 | + @Test | ||
| 51 | + public void testIsMatched_Success_TemperatureProperty() { | ||
| 52 | + // given | ||
| 53 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 54 | + .put("temperature", 25.5) | ||
| 55 | + .build(); | ||
| 56 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 57 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 58 | + "temperature", | ||
| 59 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 60 | + "20" | ||
| 61 | + ); | ||
| 62 | + | ||
| 63 | + // when | ||
| 64 | + boolean result = matcher.isMatched(message, trigger); | ||
| 65 | + | ||
| 66 | + // then | ||
| 67 | + assertTrue(result); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Test | ||
| 71 | + public void testIsMatched_Success_HumidityProperty() { | ||
| 72 | + // given | ||
| 73 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 74 | + .put("humidity", 60) | ||
| 75 | + .build(); | ||
| 76 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 77 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 78 | + "humidity", | ||
| 79 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 80 | + "60" | ||
| 81 | + ); | ||
| 82 | + | ||
| 83 | + // when | ||
| 84 | + boolean result = matcher.isMatched(message, trigger); | ||
| 85 | + | ||
| 86 | + // then | ||
| 87 | + assertTrue(result); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + @Test | ||
| 91 | + public void testIsMatched_Failure_PropertyMismatch() { | ||
| 92 | + // given | ||
| 93 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 94 | + .put("temperature", 15.0) | ||
| 95 | + .build(); | ||
| 96 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 97 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 98 | + "temperature", | ||
| 99 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 100 | + "20" | ||
| 101 | + ); | ||
| 102 | + | ||
| 103 | + // when | ||
| 104 | + boolean result = matcher.isMatched(message, trigger); | ||
| 105 | + | ||
| 106 | + // then | ||
| 107 | + assertFalse(result); | ||
| 108 | + } | ||
| 109 | + | ||
| 110 | + @Test | ||
| 111 | + public void testIsMatched_Failure_PropertyNotFound() { | ||
| 112 | + // given | ||
| 113 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 114 | + .put("temperature", 25.5) | ||
| 115 | + .build(); | ||
| 116 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 117 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 118 | + "humidity", // 不存在的属性 | ||
| 119 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 120 | + "50" | ||
| 121 | + ); | ||
| 122 | + | ||
| 123 | + // when | ||
| 124 | + boolean result = matcher.isMatched(message, trigger); | ||
| 125 | + | ||
| 126 | + // then | ||
| 127 | + assertFalse(result); | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + @Test | ||
| 131 | + public void testIsMatched_Failure_WrongMessageMethod() { | ||
| 132 | + // given | ||
| 133 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 134 | + .put("temperature", 25.5) | ||
| 135 | + .build(); | ||
| 136 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 137 | + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); | ||
| 138 | + message.setParams(properties); | ||
| 139 | + | ||
| 140 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 141 | + "temperature", | ||
| 142 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 143 | + "20" | ||
| 144 | + ); | ||
| 145 | + | ||
| 146 | + // when | ||
| 147 | + boolean result = matcher.isMatched(message, trigger); | ||
| 148 | + | ||
| 149 | + // then | ||
| 150 | + assertFalse(result); | ||
| 151 | + } | ||
| 152 | + | ||
| 153 | + @Test | ||
| 154 | + public void testIsMatched_Failure_MissingIdentifier() { | ||
| 155 | + // given | ||
| 156 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 157 | + .put("temperature", 25.5) | ||
| 158 | + .build(); | ||
| 159 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 160 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 161 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); | ||
| 162 | + trigger.setIdentifier(null); // 缺少标识符 | ||
| 163 | + trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator()); | ||
| 164 | + trigger.setValue("20"); | ||
| 165 | + | ||
| 166 | + // when | ||
| 167 | + boolean result = matcher.isMatched(message, trigger); | ||
| 168 | + | ||
| 169 | + // then | ||
| 170 | + assertFalse(result); | ||
| 171 | + } | ||
| 172 | + | ||
| 173 | + @Test | ||
| 174 | + public void testIsMatched_Failure_NullMessageParams() { | ||
| 175 | + // given | ||
| 176 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 177 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); | ||
| 178 | + message.setParams(null); | ||
| 179 | + | ||
| 180 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 181 | + "temperature", | ||
| 182 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 183 | + "20" | ||
| 184 | + ); | ||
| 185 | + | ||
| 186 | + // when | ||
| 187 | + boolean result = matcher.isMatched(message, trigger); | ||
| 188 | + | ||
| 189 | + // then | ||
| 190 | + assertFalse(result); | ||
| 191 | + } | ||
| 192 | + | ||
| 193 | + @Test | ||
| 194 | + public void testIsMatched_Failure_InvalidMessageParams() { | ||
| 195 | + // given | ||
| 196 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 197 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); | ||
| 198 | + message.setParams("invalid-params"); // 不是 Map 类型 | ||
| 199 | + | ||
| 200 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 201 | + "temperature", | ||
| 202 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 203 | + "20" | ||
| 204 | + ); | ||
| 205 | + | ||
| 206 | + // when | ||
| 207 | + boolean result = matcher.isMatched(message, trigger); | ||
| 208 | + | ||
| 209 | + // then | ||
| 210 | + assertFalse(result); | ||
| 211 | + } | ||
| 212 | + | ||
| 213 | + @Test | ||
| 214 | + public void testIsMatched_Success_LessThanOperator() { | ||
| 215 | + // given | ||
| 216 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 217 | + .put("temperature", 15.0) | ||
| 218 | + .build(); | ||
| 219 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 220 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 221 | + "temperature", | ||
| 222 | + IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(), | ||
| 223 | + "20" | ||
| 224 | + ); | ||
| 225 | + | ||
| 226 | + // when | ||
| 227 | + boolean result = matcher.isMatched(message, trigger); | ||
| 228 | + | ||
| 229 | + // then | ||
| 230 | + assertTrue(result); | ||
| 231 | + } | ||
| 232 | + | ||
| 233 | + @Test | ||
| 234 | + public void testIsMatched_Success_NotEqualsOperator() { | ||
| 235 | + // given | ||
| 236 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 237 | + .put("status", "active") | ||
| 238 | + .build(); | ||
| 239 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 240 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 241 | + "status", | ||
| 242 | + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), | ||
| 243 | + "inactive" | ||
| 244 | + ); | ||
| 245 | + | ||
| 246 | + // when | ||
| 247 | + boolean result = matcher.isMatched(message, trigger); | ||
| 248 | + | ||
| 249 | + // then | ||
| 250 | + assertTrue(result); | ||
| 251 | + } | ||
| 252 | + | ||
| 253 | + @Test | ||
| 254 | + public void testIsMatched_Success_MultipleProperties() { | ||
| 255 | + // given | ||
| 256 | + Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>()) | ||
| 257 | + .put("temperature", 25.5) | ||
| 258 | + .put("humidity", 60) | ||
| 259 | + .put("status", "active") | ||
| 260 | + .build(); | ||
| 261 | + IotDeviceMessage message = createPropertyPostMessage(properties); | ||
| 262 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 263 | + "humidity", | ||
| 264 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 265 | + "60" | ||
| 266 | + ); | ||
| 267 | + | ||
| 268 | + // when | ||
| 269 | + boolean result = matcher.isMatched(message, trigger); | ||
| 270 | + | ||
| 271 | + // then | ||
| 272 | + assertTrue(result); | ||
| 273 | + } | ||
| 274 | + | ||
| 275 | + // ========== 辅助方法 ========== | ||
| 276 | + | ||
| 277 | + /** | ||
| 278 | + * 创建属性上报消息 | ||
| 279 | + */ | ||
| 280 | + private IotDeviceMessage createPropertyPostMessage(Map<String, Object> properties) { | ||
| 281 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 282 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); | ||
| 283 | + message.setParams(properties); | ||
| 284 | + return message; | ||
| 285 | + } | ||
| 286 | + | ||
| 287 | + /** | ||
| 288 | + * 创建有效的触发器 | ||
| 289 | + */ | ||
| 290 | + private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) { | ||
| 291 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 292 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType()); | ||
| 293 | + trigger.setIdentifier(identifier); | ||
| 294 | + trigger.setOperator(operator); | ||
| 295 | + trigger.setValue(value); | ||
| 296 | + return trigger; | ||
| 297 | + } | ||
| 298 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceServiceInvokeTriggerMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.map.MapUtil; | ||
| 4 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 9 | +import org.junit.jupiter.api.BeforeEach; | ||
| 10 | +import org.junit.jupiter.api.Test; | ||
| 11 | + | ||
| 12 | +import java.util.HashMap; | ||
| 13 | +import java.util.Map; | ||
| 14 | + | ||
| 15 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 16 | + | ||
| 17 | +/** | ||
| 18 | + * {@link DeviceServiceInvokeTriggerMatcher} 的单元测试类 | ||
| 19 | + * | ||
| 20 | + * @author HUIHUI | ||
| 21 | + */ | ||
| 22 | +public class DeviceServiceInvokeTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 23 | + | ||
| 24 | + private DeviceServiceInvokeTriggerMatcher matcher; | ||
| 25 | + | ||
| 26 | + @BeforeEach | ||
| 27 | + public void setUp() { | ||
| 28 | + matcher = new DeviceServiceInvokeTriggerMatcher(); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + @Test | ||
| 32 | + public void testGetSupportedTriggerType() { | ||
| 33 | + // when & then | ||
| 34 | + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, matcher.getSupportedTriggerType()); | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + @Test | ||
| 38 | + public void testGetPriority() { | ||
| 39 | + // when & then | ||
| 40 | + assertEquals(40, matcher.getPriority()); | ||
| 41 | + } | ||
| 42 | + | ||
| 43 | + @Test | ||
| 44 | + public void testIsEnabled() { | ||
| 45 | + // when & then | ||
| 46 | + assertTrue(matcher.isEnabled()); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + @Test | ||
| 50 | + public void testIsMatched_Success_RestartService() { | ||
| 51 | + // given | ||
| 52 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 53 | + .put("identifier", "restart") | ||
| 54 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 55 | + .put("mode", "soft") | ||
| 56 | + .build()) | ||
| 57 | + .build(); | ||
| 58 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 59 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); | ||
| 60 | + | ||
| 61 | + // when | ||
| 62 | + boolean result = matcher.isMatched(message, trigger); | ||
| 63 | + | ||
| 64 | + // then | ||
| 65 | + assertTrue(result); | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + @Test | ||
| 69 | + public void testIsMatched_Success_ConfigService() { | ||
| 70 | + // given | ||
| 71 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 72 | + .put("identifier", "config") | ||
| 73 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 74 | + .put("interval", 30) | ||
| 75 | + .put("enabled", true) | ||
| 76 | + .put("threshold", 75.5) | ||
| 77 | + .build()) | ||
| 78 | + .build(); | ||
| 79 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 80 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("config"); | ||
| 81 | + | ||
| 82 | + // when | ||
| 83 | + boolean result = matcher.isMatched(message, trigger); | ||
| 84 | + | ||
| 85 | + // then | ||
| 86 | + assertTrue(result); | ||
| 87 | + } | ||
| 88 | + | ||
| 89 | + @Test | ||
| 90 | + public void testIsMatched_Success_UpdateService() { | ||
| 91 | + // given | ||
| 92 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 93 | + .put("identifier", "update") | ||
| 94 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 95 | + .put("version", "1.2.3") | ||
| 96 | + .put("url", "http://example.com/firmware.bin") | ||
| 97 | + .build()) | ||
| 98 | + .build(); | ||
| 99 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 100 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("update"); | ||
| 101 | + | ||
| 102 | + // when | ||
| 103 | + boolean result = matcher.isMatched(message, trigger); | ||
| 104 | + | ||
| 105 | + // then | ||
| 106 | + assertTrue(result); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Test | ||
| 110 | + public void testIsMatched_Failure_ServiceIdentifierMismatch() { | ||
| 111 | + // given | ||
| 112 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 113 | + .put("identifier", "restart") | ||
| 114 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 115 | + .put("mode", "soft") | ||
| 116 | + .build()) | ||
| 117 | + .build(); | ||
| 118 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 119 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("config"); // 不匹配的服务标识符 | ||
| 120 | + | ||
| 121 | + // when | ||
| 122 | + boolean result = matcher.isMatched(message, trigger); | ||
| 123 | + | ||
| 124 | + // then | ||
| 125 | + assertFalse(result); | ||
| 126 | + } | ||
| 127 | + | ||
| 128 | + @Test | ||
| 129 | + public void testIsMatched_Failure_WrongMessageMethod() { | ||
| 130 | + // given | ||
| 131 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 132 | + .put("identifier", "restart") | ||
| 133 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 134 | + .put("mode", "soft") | ||
| 135 | + .build()) | ||
| 136 | + .build(); | ||
| 137 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 138 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法 | ||
| 139 | + message.setParams(serviceParams); | ||
| 140 | + | ||
| 141 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); | ||
| 142 | + | ||
| 143 | + // when | ||
| 144 | + boolean result = matcher.isMatched(message, trigger); | ||
| 145 | + | ||
| 146 | + // then | ||
| 147 | + assertFalse(result); | ||
| 148 | + } | ||
| 149 | + | ||
| 150 | + @Test | ||
| 151 | + public void testIsMatched_Failure_MissingIdentifier() { | ||
| 152 | + // given | ||
| 153 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 154 | + .put("identifier", "restart") | ||
| 155 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 156 | + .put("mode", "soft") | ||
| 157 | + .build()) | ||
| 158 | + .build(); | ||
| 159 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 160 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 161 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); | ||
| 162 | + trigger.setIdentifier(null); // 缺少标识符 | ||
| 163 | + | ||
| 164 | + // when | ||
| 165 | + boolean result = matcher.isMatched(message, trigger); | ||
| 166 | + | ||
| 167 | + // then | ||
| 168 | + assertFalse(result); | ||
| 169 | + } | ||
| 170 | + | ||
| 171 | + @Test | ||
| 172 | + public void testIsMatched_Failure_NullMessageParams() { | ||
| 173 | + // given | ||
| 174 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 175 | + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); | ||
| 176 | + message.setParams(null); | ||
| 177 | + | ||
| 178 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); | ||
| 179 | + | ||
| 180 | + // when | ||
| 181 | + boolean result = matcher.isMatched(message, trigger); | ||
| 182 | + | ||
| 183 | + // then | ||
| 184 | + assertFalse(result); | ||
| 185 | + } | ||
| 186 | + | ||
| 187 | + @Test | ||
| 188 | + public void testIsMatched_Failure_InvalidMessageParams() { | ||
| 189 | + // given | ||
| 190 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 191 | + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); | ||
| 192 | + message.setParams("invalid-params"); // 不是 Map 类型 | ||
| 193 | + | ||
| 194 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); | ||
| 195 | + | ||
| 196 | + // when | ||
| 197 | + boolean result = matcher.isMatched(message, trigger); | ||
| 198 | + | ||
| 199 | + // then | ||
| 200 | + assertFalse(result); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + @Test | ||
| 204 | + public void testIsMatched_Failure_MissingServiceIdentifierInParams() { | ||
| 205 | + // given | ||
| 206 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 207 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 208 | + .put("mode", "soft") | ||
| 209 | + .build()) // 缺少 identifier 字段 | ||
| 210 | + .build(); | ||
| 211 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 212 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); | ||
| 213 | + | ||
| 214 | + // when | ||
| 215 | + boolean result = matcher.isMatched(message, trigger); | ||
| 216 | + | ||
| 217 | + // then | ||
| 218 | + assertFalse(result); | ||
| 219 | + } | ||
| 220 | + | ||
| 221 | + @Test | ||
| 222 | + public void testIsMatched_Failure_NullTrigger() { | ||
| 223 | + // given | ||
| 224 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 225 | + .put("identifier", "restart") | ||
| 226 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 227 | + .put("mode", "soft") | ||
| 228 | + .build()) | ||
| 229 | + .build(); | ||
| 230 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 231 | + | ||
| 232 | + // when | ||
| 233 | + boolean result = matcher.isMatched(message, null); | ||
| 234 | + | ||
| 235 | + // then | ||
| 236 | + assertFalse(result); | ||
| 237 | + } | ||
| 238 | + | ||
| 239 | + @Test | ||
| 240 | + public void testIsMatched_Failure_NullTriggerType() { | ||
| 241 | + // given | ||
| 242 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 243 | + .put("identifier", "restart") | ||
| 244 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 245 | + .put("mode", "soft") | ||
| 246 | + .build()) | ||
| 247 | + .build(); | ||
| 248 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 249 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 250 | + trigger.setType(null); | ||
| 251 | + trigger.setIdentifier("restart"); | ||
| 252 | + | ||
| 253 | + // when | ||
| 254 | + boolean result = matcher.isMatched(message, trigger); | ||
| 255 | + | ||
| 256 | + // then | ||
| 257 | + assertFalse(result); | ||
| 258 | + } | ||
| 259 | + | ||
| 260 | + @Test | ||
| 261 | + public void testIsMatched_Success_EmptyInputData() { | ||
| 262 | + // given | ||
| 263 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 264 | + .put("identifier", "ping") | ||
| 265 | + .put("inputData", MapUtil.of()) // 空的输入数据 | ||
| 266 | + .build(); | ||
| 267 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 268 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("ping"); | ||
| 269 | + | ||
| 270 | + // when | ||
| 271 | + boolean result = matcher.isMatched(message, trigger); | ||
| 272 | + | ||
| 273 | + // then | ||
| 274 | + assertTrue(result); | ||
| 275 | + } | ||
| 276 | + | ||
| 277 | + @Test | ||
| 278 | + public void testIsMatched_Success_NoInputData() { | ||
| 279 | + // given | ||
| 280 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 281 | + .put("identifier", "status") | ||
| 282 | + // 没有 inputData 字段 | ||
| 283 | + .build(); | ||
| 284 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 285 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("status"); | ||
| 286 | + | ||
| 287 | + // when | ||
| 288 | + boolean result = matcher.isMatched(message, trigger); | ||
| 289 | + | ||
| 290 | + // then | ||
| 291 | + assertTrue(result); | ||
| 292 | + } | ||
| 293 | + | ||
| 294 | + @Test | ||
| 295 | + public void testIsMatched_Success_ComplexInputData() { | ||
| 296 | + // given | ||
| 297 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 298 | + .put("identifier", "calibrate") | ||
| 299 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 300 | + .put("sensors", new String[]{"temperature", "humidity", "pressure"}) | ||
| 301 | + .put("precision", 0.01) | ||
| 302 | + .put("duration", 300) | ||
| 303 | + .put("autoSave", true) | ||
| 304 | + .put("config", MapUtil.builder(new HashMap<String, Object>()) | ||
| 305 | + .put("mode", "auto") | ||
| 306 | + .put("level", "high") | ||
| 307 | + .build()) | ||
| 308 | + .build()) | ||
| 309 | + .build(); | ||
| 310 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 311 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("calibrate"); | ||
| 312 | + | ||
| 313 | + // when | ||
| 314 | + boolean result = matcher.isMatched(message, trigger); | ||
| 315 | + | ||
| 316 | + // then | ||
| 317 | + assertTrue(result); | ||
| 318 | + } | ||
| 319 | + | ||
| 320 | + @Test | ||
| 321 | + public void testIsMatched_Success_CaseInsensitiveIdentifier() { | ||
| 322 | + // given | ||
| 323 | + Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>()) | ||
| 324 | + .put("identifier", "RESTART") // 大写 | ||
| 325 | + .put("inputData", MapUtil.builder(new HashMap<String, Object>()) | ||
| 326 | + .put("mode", "soft") | ||
| 327 | + .build()) | ||
| 328 | + .build(); | ||
| 329 | + IotDeviceMessage message = createServiceInvokeMessage(serviceParams); | ||
| 330 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("restart"); // 小写 | ||
| 331 | + | ||
| 332 | + // when | ||
| 333 | + boolean result = matcher.isMatched(message, trigger); | ||
| 334 | + | ||
| 335 | + // then | ||
| 336 | + // 根据实际实现,这里可能需要调整期望结果 | ||
| 337 | + // 如果实现是大小写敏感的,则应该为 false | ||
| 338 | + assertFalse(result); | ||
| 339 | + } | ||
| 340 | + | ||
| 341 | + // ========== 辅助方法 ========== | ||
| 342 | + | ||
| 343 | + /** | ||
| 344 | + * 创建服务调用消息 | ||
| 345 | + */ | ||
| 346 | + private IotDeviceMessage createServiceInvokeMessage(Map<String, Object> serviceParams) { | ||
| 347 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 348 | + message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod()); | ||
| 349 | + message.setParams(serviceParams); | ||
| 350 | + return message; | ||
| 351 | + } | ||
| 352 | + | ||
| 353 | + /** | ||
| 354 | + * 创建有效的触发器 | ||
| 355 | + */ | ||
| 356 | + private IotSceneRuleDO.Trigger createValidTrigger(String identifier) { | ||
| 357 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 358 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType()); | ||
| 359 | + trigger.setIdentifier(identifier); | ||
| 360 | + return trigger; | ||
| 361 | + } | ||
| 362 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/DeviceStateUpdateTriggerMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum; | ||
| 5 | +import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum; | ||
| 6 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 7 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 8 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum; | ||
| 9 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 10 | +import org.junit.jupiter.api.BeforeEach; | ||
| 11 | +import org.junit.jupiter.api.Test; | ||
| 12 | + | ||
| 13 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * {@link DeviceStateUpdateTriggerMatcher} 的单元测试类 | ||
| 17 | + * | ||
| 18 | + * @author HUIHUI | ||
| 19 | + */ | ||
| 20 | +public class DeviceStateUpdateTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 21 | + | ||
| 22 | + private DeviceStateUpdateTriggerMatcher matcher; | ||
| 23 | + | ||
| 24 | + @BeforeEach | ||
| 25 | + public void setUp() { | ||
| 26 | + matcher = new DeviceStateUpdateTriggerMatcher(); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Test | ||
| 30 | + public void testGetSupportedTriggerType() { | ||
| 31 | + // when & then | ||
| 32 | + assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, matcher.getSupportedTriggerType()); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Test | ||
| 36 | + public void testGetPriority() { | ||
| 37 | + // when & then | ||
| 38 | + assertEquals(10, matcher.getPriority()); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + @Test | ||
| 42 | + public void testIsEnabled() { | ||
| 43 | + // when & then | ||
| 44 | + assertTrue(matcher.isEnabled()); | ||
| 45 | + } | ||
| 46 | + | ||
| 47 | + @Test | ||
| 48 | + public void testIsMatched_Success_OnlineState() { | ||
| 49 | + // given | ||
| 50 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 51 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 52 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 53 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 54 | + ); | ||
| 55 | + | ||
| 56 | + // when | ||
| 57 | + boolean result = matcher.isMatched(message, trigger); | ||
| 58 | + | ||
| 59 | + // then | ||
| 60 | + assertTrue(result); | ||
| 61 | + } | ||
| 62 | + | ||
| 63 | + @Test | ||
| 64 | + public void testIsMatched_Success_OfflineState() { | ||
| 65 | + // given | ||
| 66 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState()); | ||
| 67 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 68 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 69 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 70 | + ); | ||
| 71 | + | ||
| 72 | + // when | ||
| 73 | + boolean result = matcher.isMatched(message, trigger); | ||
| 74 | + | ||
| 75 | + // then | ||
| 76 | + assertTrue(result); | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + @Test | ||
| 80 | + public void testIsMatched_Failure_StateMismatch() { | ||
| 81 | + // given | ||
| 82 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 83 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 84 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 85 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 86 | + ); | ||
| 87 | + | ||
| 88 | + // when | ||
| 89 | + boolean result = matcher.isMatched(message, trigger); | ||
| 90 | + | ||
| 91 | + // then | ||
| 92 | + assertFalse(result); | ||
| 93 | + } | ||
| 94 | + | ||
| 95 | + @Test | ||
| 96 | + public void testIsMatched_Failure_NullTrigger() { | ||
| 97 | + // given | ||
| 98 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 99 | + | ||
| 100 | + // when | ||
| 101 | + boolean result = matcher.isMatched(message, null); | ||
| 102 | + | ||
| 103 | + // then | ||
| 104 | + assertFalse(result); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + @Test | ||
| 108 | + public void testIsMatched_Failure_NullTriggerType() { | ||
| 109 | + // given | ||
| 110 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 111 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 112 | + trigger.setType(null); | ||
| 113 | + | ||
| 114 | + // when | ||
| 115 | + boolean result = matcher.isMatched(message, trigger); | ||
| 116 | + | ||
| 117 | + // then | ||
| 118 | + assertFalse(result); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + @Test | ||
| 122 | + public void testIsMatched_Failure_WrongMessageMethod() { | ||
| 123 | + // given | ||
| 124 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 125 | + message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); | ||
| 126 | + message.setParams(IotDeviceStateEnum.ONLINE.getState()); | ||
| 127 | + | ||
| 128 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 129 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 130 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 131 | + ); | ||
| 132 | + | ||
| 133 | + // when | ||
| 134 | + boolean result = matcher.isMatched(message, trigger); | ||
| 135 | + | ||
| 136 | + // then | ||
| 137 | + assertFalse(result); | ||
| 138 | + } | ||
| 139 | + | ||
| 140 | + @Test | ||
| 141 | + public void testIsMatched_Failure_MissingOperator() { | ||
| 142 | + // given | ||
| 143 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 144 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 145 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); | ||
| 146 | + trigger.setOperator(null); | ||
| 147 | + trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString()); | ||
| 148 | + | ||
| 149 | + // when | ||
| 150 | + boolean result = matcher.isMatched(message, trigger); | ||
| 151 | + | ||
| 152 | + // then | ||
| 153 | + assertFalse(result); | ||
| 154 | + } | ||
| 155 | + | ||
| 156 | + @Test | ||
| 157 | + public void testIsMatched_Failure_MissingValue() { | ||
| 158 | + // given | ||
| 159 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 160 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 161 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); | ||
| 162 | + trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator()); | ||
| 163 | + trigger.setValue(null); | ||
| 164 | + | ||
| 165 | + // when | ||
| 166 | + boolean result = matcher.isMatched(message, trigger); | ||
| 167 | + | ||
| 168 | + // then | ||
| 169 | + assertFalse(result); | ||
| 170 | + } | ||
| 171 | + | ||
| 172 | + @Test | ||
| 173 | + public void testIsMatched_Failure_NullMessageParams() { | ||
| 174 | + // given | ||
| 175 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 176 | + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); | ||
| 177 | + message.setParams(null); | ||
| 178 | + | ||
| 179 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 180 | + IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(), | ||
| 181 | + IotDeviceStateEnum.ONLINE.getState().toString() | ||
| 182 | + ); | ||
| 183 | + | ||
| 184 | + // when | ||
| 185 | + boolean result = matcher.isMatched(message, trigger); | ||
| 186 | + | ||
| 187 | + // then | ||
| 188 | + assertFalse(result); | ||
| 189 | + } | ||
| 190 | + | ||
| 191 | + @Test | ||
| 192 | + public void testIsMatched_Success_GreaterThanOperator() { | ||
| 193 | + // given | ||
| 194 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 195 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 196 | + IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(), | ||
| 197 | + IotDeviceStateEnum.INACTIVE.getState().toString() | ||
| 198 | + ); | ||
| 199 | + | ||
| 200 | + // when | ||
| 201 | + boolean result = matcher.isMatched(message, trigger); | ||
| 202 | + | ||
| 203 | + // then | ||
| 204 | + assertTrue(result); | ||
| 205 | + } | ||
| 206 | + | ||
| 207 | + @Test | ||
| 208 | + public void testIsMatched_Success_NotEqualsOperator() { | ||
| 209 | + // given | ||
| 210 | + IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState()); | ||
| 211 | + IotSceneRuleDO.Trigger trigger = createValidTrigger( | ||
| 212 | + IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(), | ||
| 213 | + IotDeviceStateEnum.OFFLINE.getState().toString() | ||
| 214 | + ); | ||
| 215 | + | ||
| 216 | + // when | ||
| 217 | + boolean result = matcher.isMatched(message, trigger); | ||
| 218 | + | ||
| 219 | + // then | ||
| 220 | + assertTrue(result); | ||
| 221 | + } | ||
| 222 | + | ||
| 223 | + // ========== 辅助方法 ========== | ||
| 224 | + | ||
| 225 | + /** | ||
| 226 | + * 创建设备状态更新消息 | ||
| 227 | + */ | ||
| 228 | + private IotDeviceMessage createStateUpdateMessage(Integer state) { | ||
| 229 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 230 | + message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod()); | ||
| 231 | + message.setParams(state); | ||
| 232 | + return message; | ||
| 233 | + } | ||
| 234 | + | ||
| 235 | + /** | ||
| 236 | + * 创建有效的触发器 | ||
| 237 | + */ | ||
| 238 | + private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) { | ||
| 239 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 240 | + trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType()); | ||
| 241 | + trigger.setOperator(operator); | ||
| 242 | + trigger.setValue(value); | ||
| 243 | + return trigger; | ||
| 244 | + } | ||
| 245 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/TimerTriggerMatcherTest.java
0 → 100644
| 1 | +package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest; | ||
| 4 | +import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage; | ||
| 5 | +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO; | ||
| 6 | +import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum; | ||
| 7 | +import org.junit.jupiter.api.BeforeEach; | ||
| 8 | +import org.junit.jupiter.api.Test; | ||
| 9 | + | ||
| 10 | +import static org.junit.jupiter.api.Assertions.*; | ||
| 11 | + | ||
| 12 | +/** | ||
| 13 | + * {@link TimerTriggerMatcher} 的单元测试类 | ||
| 14 | + * | ||
| 15 | + * @author HUIHUI | ||
| 16 | + */ | ||
| 17 | +public class TimerTriggerMatcherTest extends BaseMockitoUnitTest { | ||
| 18 | + | ||
| 19 | + private TimerTriggerMatcher matcher; | ||
| 20 | + | ||
| 21 | + @BeforeEach | ||
| 22 | + public void setUp() { | ||
| 23 | + matcher = new TimerTriggerMatcher(); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + @Test | ||
| 27 | + public void testGetSupportedTriggerType() { | ||
| 28 | + // when & then | ||
| 29 | + assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, matcher.getSupportedTriggerType()); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + @Test | ||
| 33 | + public void testGetPriority() { | ||
| 34 | + // when & then | ||
| 35 | + assertEquals(50, matcher.getPriority()); | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + @Test | ||
| 39 | + public void testIsEnabled() { | ||
| 40 | + // when & then | ||
| 41 | + assertTrue(matcher.isEnabled()); | ||
| 42 | + } | ||
| 43 | + | ||
| 44 | + @Test | ||
| 45 | + public void testIsMatched_Success_ValidCronExpression() { | ||
| 46 | + // given | ||
| 47 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 48 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * * ?"); // 每天中午12点 | ||
| 49 | + | ||
| 50 | + // when | ||
| 51 | + boolean result = matcher.isMatched(message, trigger); | ||
| 52 | + | ||
| 53 | + // then | ||
| 54 | + assertTrue(result); | ||
| 55 | + } | ||
| 56 | + | ||
| 57 | + @Test | ||
| 58 | + public void testIsMatched_Success_EveryMinuteCron() { | ||
| 59 | + // given | ||
| 60 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 61 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 * * * * ?"); // 每分钟 | ||
| 62 | + | ||
| 63 | + // when | ||
| 64 | + boolean result = matcher.isMatched(message, trigger); | ||
| 65 | + | ||
| 66 | + // then | ||
| 67 | + assertTrue(result); | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + @Test | ||
| 71 | + public void testIsMatched_Success_WeekdaysCron() { | ||
| 72 | + // given | ||
| 73 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 74 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9 ? * MON-FRI"); // 工作日上午9点 | ||
| 75 | + | ||
| 76 | + // when | ||
| 77 | + boolean result = matcher.isMatched(message, trigger); | ||
| 78 | + | ||
| 79 | + // then | ||
| 80 | + assertTrue(result); | ||
| 81 | + } | ||
| 82 | + | ||
| 83 | + @Test | ||
| 84 | + public void testIsMatched_Failure_InvalidCronExpression() { | ||
| 85 | + // given | ||
| 86 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 87 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("invalid-cron-expression"); | ||
| 88 | + | ||
| 89 | + // when | ||
| 90 | + boolean result = matcher.isMatched(message, trigger); | ||
| 91 | + | ||
| 92 | + // then | ||
| 93 | + assertFalse(result); | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + @Test | ||
| 97 | + public void testIsMatched_Failure_EmptyCronExpression() { | ||
| 98 | + // given | ||
| 99 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 100 | + IotSceneRuleDO.Trigger trigger = createValidTrigger(""); | ||
| 101 | + | ||
| 102 | + // when | ||
| 103 | + boolean result = matcher.isMatched(message, trigger); | ||
| 104 | + | ||
| 105 | + // then | ||
| 106 | + assertFalse(result); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + @Test | ||
| 110 | + public void testIsMatched_Failure_NullCronExpression() { | ||
| 111 | + // given | ||
| 112 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 113 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 114 | + trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); | ||
| 115 | + trigger.setCronExpression(null); | ||
| 116 | + | ||
| 117 | + // when | ||
| 118 | + boolean result = matcher.isMatched(message, trigger); | ||
| 119 | + | ||
| 120 | + // then | ||
| 121 | + assertFalse(result); | ||
| 122 | + } | ||
| 123 | + | ||
| 124 | + @Test | ||
| 125 | + public void testIsMatched_Failure_NullTrigger() { | ||
| 126 | + // given | ||
| 127 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 128 | + | ||
| 129 | + // when | ||
| 130 | + boolean result = matcher.isMatched(message, null); | ||
| 131 | + | ||
| 132 | + // then | ||
| 133 | + assertFalse(result); | ||
| 134 | + } | ||
| 135 | + | ||
| 136 | + @Test | ||
| 137 | + public void testIsMatched_Failure_NullTriggerType() { | ||
| 138 | + // given | ||
| 139 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 140 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 141 | + trigger.setType(null); | ||
| 142 | + trigger.setCronExpression("0 0 12 * * ?"); | ||
| 143 | + | ||
| 144 | + // when | ||
| 145 | + boolean result = matcher.isMatched(message, trigger); | ||
| 146 | + | ||
| 147 | + // then | ||
| 148 | + assertFalse(result); | ||
| 149 | + } | ||
| 150 | + | ||
| 151 | + @Test | ||
| 152 | + public void testIsMatched_Success_ComplexCronExpression() { | ||
| 153 | + // given | ||
| 154 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 155 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 15 10 ? * 6#3"); // 每月第三个星期五上午10:15 | ||
| 156 | + | ||
| 157 | + // when | ||
| 158 | + boolean result = matcher.isMatched(message, trigger); | ||
| 159 | + | ||
| 160 | + // then | ||
| 161 | + assertTrue(result); | ||
| 162 | + } | ||
| 163 | + | ||
| 164 | + @Test | ||
| 165 | + public void testIsMatched_Failure_IncorrectCronFormat() { | ||
| 166 | + // given | ||
| 167 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 168 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * *"); // 缺少字段 | ||
| 169 | + | ||
| 170 | + // when | ||
| 171 | + boolean result = matcher.isMatched(message, trigger); | ||
| 172 | + | ||
| 173 | + // then | ||
| 174 | + assertFalse(result); | ||
| 175 | + } | ||
| 176 | + | ||
| 177 | + @Test | ||
| 178 | + public void testIsMatched_Success_SpecificDateCron() { | ||
| 179 | + // given | ||
| 180 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 181 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 0 1 1 ? 2025"); // 2025年1月1日午夜 | ||
| 182 | + | ||
| 183 | + // when | ||
| 184 | + boolean result = matcher.isMatched(message, trigger); | ||
| 185 | + | ||
| 186 | + // then | ||
| 187 | + assertTrue(result); | ||
| 188 | + } | ||
| 189 | + | ||
| 190 | + @Test | ||
| 191 | + public void testIsMatched_Success_EverySecondCron() { | ||
| 192 | + // given | ||
| 193 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 194 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("* * * * * ?"); // 每秒 | ||
| 195 | + | ||
| 196 | + // when | ||
| 197 | + boolean result = matcher.isMatched(message, trigger); | ||
| 198 | + | ||
| 199 | + // then | ||
| 200 | + assertTrue(result); | ||
| 201 | + } | ||
| 202 | + | ||
| 203 | + @Test | ||
| 204 | + public void testIsMatched_Failure_InvalidCharactersCron() { | ||
| 205 | + // given | ||
| 206 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 207 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 12 * * @ #"); // 包含无效字符 | ||
| 208 | + | ||
| 209 | + // when | ||
| 210 | + boolean result = matcher.isMatched(message, trigger); | ||
| 211 | + | ||
| 212 | + // then | ||
| 213 | + assertFalse(result); | ||
| 214 | + } | ||
| 215 | + | ||
| 216 | + @Test | ||
| 217 | + public void testIsMatched_Success_RangeCron() { | ||
| 218 | + // given | ||
| 219 | + IotDeviceMessage message = new IotDeviceMessage(); | ||
| 220 | + IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9-17 * * MON-FRI"); // 工作日9-17点 | ||
| 221 | + | ||
| 222 | + // when | ||
| 223 | + boolean result = matcher.isMatched(message, trigger); | ||
| 224 | + | ||
| 225 | + // then | ||
| 226 | + assertTrue(result); | ||
| 227 | + } | ||
| 228 | + | ||
| 229 | + // ========== 辅助方法 ========== | ||
| 230 | + | ||
| 231 | + /** | ||
| 232 | + * 创建有效的定时触发器 | ||
| 233 | + */ | ||
| 234 | + private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) { | ||
| 235 | + IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger(); | ||
| 236 | + trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType()); | ||
| 237 | + trigger.setCronExpression(cronExpression); | ||
| 238 | + return trigger; | ||
| 239 | + } | ||
| 240 | +} |
yudao-module-iot/yudao-module-iot-biz/src/test/resources/application-unit-test.yaml
| @@ -20,6 +20,15 @@ mybatis-plus: | @@ -20,6 +20,15 @@ mybatis-plus: | ||
| 20 | lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 | 20 | lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试 |
| 21 | type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject | 21 | type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject |
| 22 | 22 | ||
| 23 | +# 日志配置 | ||
| 24 | +logging: | ||
| 25 | + level: | ||
| 26 | + cn.iocoder.yudao.module.iot.service.rule.scene.matcher: DEBUG | ||
| 27 | + cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager: INFO | ||
| 28 | + cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition: DEBUG | ||
| 29 | + cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger: DEBUG | ||
| 30 | + root: WARN | ||
| 31 | + | ||
| 23 | --- #################### 定时任务相关配置 #################### | 32 | --- #################### 定时任务相关配置 #################### |
| 24 | 33 | ||
| 25 | --- #################### 配置中心相关配置 #################### | 34 | --- #################### 配置中心相关配置 #################### |
yudao-module-iot/yudao-module-iot-biz/src/test/resources/logback.xml
| 1 | <configuration> | 1 | <configuration> |
| 2 | <!-- 引用 Spring Boot 的 logback 基础配置 --> | 2 | <!-- 引用 Spring Boot 的 logback 基础配置 --> |
| 3 | <include resource="org/springframework/boot/logging/logback/defaults.xml"/> | 3 | <include resource="org/springframework/boot/logging/logback/defaults.xml"/> |
| 4 | + | ||
| 5 | + <!-- 控制台输出 --> | ||
| 6 | + <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> | ||
| 7 | + <encoder> | ||
| 8 | + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> | ||
| 9 | + <charset>UTF-8</charset> | ||
| 10 | + </encoder> | ||
| 11 | + </appender> | ||
| 12 | + | ||
| 13 | + <!-- 设置特定包的日志级别 --> | ||
| 14 | + <logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher" level="DEBUG" additivity="false"> | ||
| 15 | + <appender-ref ref="CONSOLE"/> | ||
| 16 | + </logger> | ||
| 17 | + | ||
| 18 | + <!-- 设置 IotSceneRuleMatcherManager 的日志级别 --> | ||
| 19 | + <logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager" level="INFO" | ||
| 20 | + additivity="false"> | ||
| 21 | + <appender-ref ref="CONSOLE"/> | ||
| 22 | + </logger> | ||
| 23 | + | ||
| 24 | + <!-- 设置所有匹配器的日志级别 --> | ||
| 25 | + <logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition" level="DEBUG" additivity="false"> | ||
| 26 | + <appender-ref ref="CONSOLE"/> | ||
| 27 | + </logger> | ||
| 28 | + | ||
| 29 | + <logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger" level="DEBUG" additivity="false"> | ||
| 30 | + <appender-ref ref="CONSOLE"/> | ||
| 31 | + </logger> | ||
| 32 | + | ||
| 33 | + <!-- 根日志级别 --> | ||
| 34 | + <root level="WARN"> | ||
| 35 | + <appender-ref ref="CONSOLE"/> | ||
| 36 | + </root> | ||
| 4 | </configuration> | 37 | </configuration> |
yudao-server/src/main/resources/application-local.yaml
| @@ -185,6 +185,7 @@ logging: | @@ -185,6 +185,7 @@ logging: | ||
| 185 | cn.iocoder.yudao.module.erp.dal.mysql: debug | 185 | cn.iocoder.yudao.module.erp.dal.mysql: debug |
| 186 | cn.iocoder.yudao.module.iot.dal.mysql: debug | 186 | cn.iocoder.yudao.module.iot.dal.mysql: debug |
| 187 | cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG | 187 | cn.iocoder.yudao.module.iot.dal.tdengine: DEBUG |
| 188 | + cn.iocoder.yudao.module.iot.service.rule: debug | ||
| 188 | cn.iocoder.yudao.module.ai.dal.mysql: debug | 189 | cn.iocoder.yudao.module.ai.dal.mysql: debug |
| 189 | org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 | 190 | org.springframework.context.support.PostProcessorRegistrationDelegate: ERROR # TODO 芋艿:先禁用,Spring Boot 3.X 存在部分错误的 WARN 提示 |
| 190 | 191 |