第一次提交
Some checks failed
Java CI with Maven / build (11) (push) Has been cancelled
Java CI with Maven / build (17) (push) Has been cancelled
Java CI with Maven / build (8) (push) Has been cancelled
yudao-ui-admin CI / build (14.x) (push) Has been cancelled
yudao-ui-admin CI / build (16.x) (push) Has been cancelled
Some checks failed
Java CI with Maven / build (11) (push) Has been cancelled
Java CI with Maven / build (17) (push) Has been cancelled
Java CI with Maven / build (8) (push) Has been cancelled
yudao-ui-admin CI / build (14.x) (push) Has been cancelled
yudao-ui-admin CI / build (16.x) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,154 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.action.databridge;
|
||||
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotDataSinkDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.*;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.data.action.*;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* {@link IotDataRuleAction} 实现类的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@Disabled // 默认禁用,需要手动启用测试
|
||||
@Slf4j
|
||||
public class IotDataBridgeExecuteTest extends BaseMockitoUnitTest {
|
||||
|
||||
private IotDeviceMessage message;
|
||||
|
||||
@Mock
|
||||
private RestTemplate restTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private IotHttpDataSinkAction httpDataBridgeExecute;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
// TODO @芋艿:@puhui999:需要调整下;
|
||||
// 创建共享的测试消息
|
||||
//message = IotDeviceMessage.builder().messageId("TEST-001").reportTime(LocalDateTime.now())
|
||||
// .productKey("testProduct").deviceName("testDevice")
|
||||
// .type("property").identifier("temperature").data("{\"value\": 60}")
|
||||
// .build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKafkaMQDataBridge() throws Exception {
|
||||
// 1. 创建执行器实例
|
||||
IotKafkaDataRuleAction action = new IotKafkaDataRuleAction();
|
||||
|
||||
// 2. 创建配置
|
||||
IotDataSinkKafkaConfig config = new IotDataSinkKafkaConfig()
|
||||
.setBootstrapServers("127.0.0.1:9092")
|
||||
.setTopic("test-topic")
|
||||
.setSsl(false)
|
||||
.setUsername(null)
|
||||
.setPassword(null);
|
||||
|
||||
// 3. 执行测试并验证缓存
|
||||
executeAndVerifyCache(action, config, "KafkaMQ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRabbitMQDataBridge() throws Exception {
|
||||
// 1. 创建执行器实例
|
||||
IotRabbitMQDataRuleAction action = new IotRabbitMQDataRuleAction();
|
||||
|
||||
// 2. 创建配置
|
||||
IotDataSinkRabbitMQConfig config = new IotDataSinkRabbitMQConfig()
|
||||
.setHost("localhost")
|
||||
.setPort(5672)
|
||||
.setVirtualHost("/")
|
||||
.setUsername("admin")
|
||||
.setPassword("123456")
|
||||
.setExchange("test-exchange")
|
||||
.setRoutingKey("test-key")
|
||||
.setQueue("test-queue");
|
||||
|
||||
// 3. 执行测试并验证缓存
|
||||
executeAndVerifyCache(action, config, "RabbitMQ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRedisDataBridge() throws Exception {
|
||||
// 1. 创建执行器实例
|
||||
IotRedisRuleAction action = new IotRedisRuleAction();
|
||||
|
||||
// 2. 创建配置 - 测试 Stream 数据结构
|
||||
IotDataSinkRedisConfig config = new IotDataSinkRedisConfig();
|
||||
config.setHost("127.0.0.1");
|
||||
config.setPort(6379);
|
||||
config.setDatabase(0);
|
||||
config.setPassword("123456");
|
||||
config.setTopic("test-stream");
|
||||
config.setDataStructure(1); // Stream 类型
|
||||
|
||||
// 3. 执行测试并验证缓存
|
||||
executeAndVerifyCache(action, config, "Redis");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRocketMQDataBridge() throws Exception {
|
||||
// 1. 创建执行器实例
|
||||
IotRocketMQDataRuleAction action = new IotRocketMQDataRuleAction();
|
||||
|
||||
// 2. 创建配置
|
||||
IotDataSinkRocketMQConfig config = new IotDataSinkRocketMQConfig()
|
||||
.setNameServer("127.0.0.1:9876")
|
||||
.setGroup("test-group")
|
||||
.setTopic("test-topic")
|
||||
.setTags("test-tag");
|
||||
|
||||
// 3. 执行测试并验证缓存
|
||||
executeAndVerifyCache(action, config, "RocketMQ");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpDataBridge() throws Exception {
|
||||
// 1. 配置 RestTemplate mock 返回成功响应
|
||||
when(restTemplate.exchange(anyString(), any(HttpMethod.class), any(), any(Class.class)))
|
||||
.thenReturn(new ResponseEntity<>("Success", HttpStatus.OK));
|
||||
|
||||
// 2. 创建配置
|
||||
IotDataSinkHttpConfig config = new IotDataSinkHttpConfig()
|
||||
.setUrl("https://doc.iocoder.cn/").setMethod(HttpMethod.GET.name());
|
||||
|
||||
// 3. 执行测试
|
||||
log.info("[testHttpDataBridge][执行HTTP数据桥接测试]");
|
||||
httpDataBridgeExecute.execute(message, new IotDataSinkDO()
|
||||
.setType(httpDataBridgeExecute.getType()).setConfig(config));
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行测试并验证缓存的通用方法
|
||||
*
|
||||
* @param action 执行器实例
|
||||
* @param config 配置对象
|
||||
* @param type MQ 类型
|
||||
* @throws Exception 如果执行过程中发生异常
|
||||
*/
|
||||
private void executeAndVerifyCache(IotDataRuleAction action, IotAbstractDataSinkConfig config, String type)
|
||||
throws Exception {
|
||||
log.info("[test{}DataBridge][第一次执行,应该会创建新的 producer]", type);
|
||||
action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));
|
||||
|
||||
log.info("[test{}DataBridge][第二次执行,应该会复用缓存的 producer]", type);
|
||||
action.execute(message, new IotDataSinkDO().setType(action.getType()).setConfig(config));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,162 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.data.action;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.data.action.tcp.IotTcpClient;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link IotTcpDataRuleAction} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
class IotTcpDataRuleActionTest {
|
||||
|
||||
private IotTcpDataRuleAction tcpDataRuleAction;
|
||||
|
||||
@Mock
|
||||
private IotTcpClient mockTcpClient;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
tcpDataRuleAction = new IotTcpDataRuleAction();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetType() {
|
||||
// 准备参数
|
||||
Integer expectedType = 2; // 数据接收类型枚举中 TCP 类型的值
|
||||
|
||||
// 调用方法
|
||||
Integer actualType = tcpDataRuleAction.getType();
|
||||
|
||||
// 断言结果
|
||||
assertEquals(expectedType, actualType);
|
||||
}
|
||||
|
||||
// TODO @puhui999:_ 后面是小写哈,单测的命名规则。
|
||||
@Test
|
||||
public void testInitProducer_Success() throws Exception {
|
||||
// 准备参数
|
||||
IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();
|
||||
config.setHost("localhost");
|
||||
config.setPort(8080);
|
||||
config.setDataFormat("JSON");
|
||||
config.setSsl(false);
|
||||
|
||||
// 调用方法 & 断言结果
|
||||
// 此测试需要实际的 TCP 连接,在单元测试中可能不可用
|
||||
// 目前我们只验证配置是否有效
|
||||
assertNotNull(config.getHost());
|
||||
assertTrue(config.getPort() > 0 && config.getPort() <= 65535);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitProducer_InvalidHost() {
|
||||
// 准备参数
|
||||
IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();
|
||||
config.setHost("");
|
||||
config.setPort(8080);
|
||||
|
||||
// 调用方法 & 断言结果
|
||||
IotTcpDataRuleAction action = new IotTcpDataRuleAction();
|
||||
|
||||
// 测试验证逻辑(通常在 initProducer 方法中)
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
if (config.getHost() == null || config.getHost().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("TCP 服务器地址不能为空");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitProducer_InvalidPort() {
|
||||
// 准备参数
|
||||
IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();
|
||||
config.setHost("localhost");
|
||||
config.setPort(-1);
|
||||
|
||||
// 调用方法 & 断言结果
|
||||
assertThrows(IllegalArgumentException.class, () -> {
|
||||
if (config.getPort() == null || config.getPort() <= 0 || config.getPort() > 65535) {
|
||||
throw new IllegalArgumentException("TCP 服务器端口无效");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCloseProducer() throws Exception {
|
||||
// 准备参数
|
||||
IotTcpClient client = mock(IotTcpClient.class);
|
||||
|
||||
// 调用方法
|
||||
tcpDataRuleAction.closeProducer(client);
|
||||
|
||||
// 断言结果
|
||||
verify(client, times(1)).close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecute_WithValidConfig() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = IotDeviceMessage.requestOf("thing.property.report",
|
||||
"{\"temperature\": 25.5, \"humidity\": 60}");
|
||||
|
||||
IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();
|
||||
config.setHost("localhost");
|
||||
config.setPort(8080);
|
||||
config.setDataFormat("JSON");
|
||||
|
||||
// 调用方法 & 断言结果
|
||||
// 通常这需要实际的 TCP 连接
|
||||
// 在单元测试中,我们只验证输入参数
|
||||
assertNotNull(message);
|
||||
assertNotNull(config);
|
||||
assertNotNull(config.getHost());
|
||||
assertTrue(config.getPort() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testConfig_DefaultValues() {
|
||||
// 准备参数
|
||||
IotDataSinkTcpConfig config = new IotDataSinkTcpConfig();
|
||||
|
||||
// 调用方法 & 断言结果
|
||||
// 验证默认值
|
||||
assertEquals("JSON", config.getDataFormat());
|
||||
assertEquals(5000, config.getConnectTimeoutMs());
|
||||
assertEquals(10000, config.getReadTimeoutMs());
|
||||
assertEquals(false, config.getSsl());
|
||||
assertEquals(30000L, config.getHeartbeatIntervalMs());
|
||||
assertEquals(5000L, config.getReconnectIntervalMs());
|
||||
assertEquals(3, config.getMaxReconnectAttempts());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageSerialization() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = IotDeviceMessage.builder()
|
||||
.deviceId(123L)
|
||||
.method("thing.property.report")
|
||||
.params("{\"temperature\": 25.5}")
|
||||
.build();
|
||||
|
||||
// 调用方法
|
||||
String json = JsonUtils.toJsonString(message);
|
||||
|
||||
// 断言结果
|
||||
assertNotNull(json);
|
||||
assertTrue(json.contains("\"deviceId\":123"));
|
||||
assertTrue(json.contains("\"method\":\"thing.property.report\""));
|
||||
assertTrue(json.contains("\"temperature\":25.5"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
|
||||
import cn.iocoder.yudao.module.iot.controller.admin.rule.vo.scene.IotSceneRuleSaveReqVO;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.dal.mysql.rule.IotSceneRuleMapper;
|
||||
import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
|
||||
import cn.iocoder.yudao.module.iot.service.device.IotDeviceService;
|
||||
import cn.iocoder.yudao.module.iot.service.product.IotProductService;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.action.IotSceneRuleAction;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomPojo;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link IotSceneRuleServiceImpl} 的简化单元测试类
|
||||
* 使用 Mockito 进行纯单元测试,不依赖 Spring 容器
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
public class IotSceneRuleServiceSimpleTest extends BaseMockitoUnitTest {
|
||||
|
||||
@InjectMocks
|
||||
private IotSceneRuleServiceImpl sceneRuleService;
|
||||
|
||||
@Mock
|
||||
private IotSceneRuleMapper sceneRuleMapper;
|
||||
|
||||
@Mock
|
||||
private List<IotSceneRuleAction> sceneRuleActions;
|
||||
|
||||
@Mock
|
||||
private IotSchedulerManager schedulerManager;
|
||||
|
||||
@Mock
|
||||
private IotProductService productService;
|
||||
|
||||
@Mock
|
||||
private IotDeviceService deviceService;
|
||||
|
||||
@Test
|
||||
public void testCreateScene_Rule_success() {
|
||||
// 准备参数
|
||||
IotSceneRuleSaveReqVO createReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {
|
||||
o.setId(null);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
|
||||
o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));
|
||||
});
|
||||
|
||||
// Mock 行为
|
||||
Long expectedId = randomLongId();
|
||||
when(sceneRuleMapper.insert(any(IotSceneRuleDO.class))).thenAnswer(invocation -> {
|
||||
IotSceneRuleDO sceneRule = invocation.getArgument(0);
|
||||
sceneRule.setId(expectedId);
|
||||
return 1;
|
||||
});
|
||||
|
||||
// 调用
|
||||
Long sceneRuleId = sceneRuleService.createSceneRule(createReqVO);
|
||||
|
||||
// 断言
|
||||
assertEquals(expectedId, sceneRuleId);
|
||||
verify(sceneRuleMapper, times(1)).insert(any(IotSceneRuleDO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateScene_Rule_success() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
IotSceneRuleSaveReqVO updateReqVO = randomPojo(IotSceneRuleSaveReqVO.class, o -> {
|
||||
o.setId(id);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
o.setTriggers(Collections.singletonList(randomPojo(IotSceneRuleDO.Trigger.class)));
|
||||
o.setActions(Collections.singletonList(randomPojo(IotSceneRuleDO.Action.class)));
|
||||
});
|
||||
|
||||
// Mock 行为
|
||||
IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
|
||||
when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
|
||||
|
||||
// 调用
|
||||
assertDoesNotThrow(() -> sceneRuleService.updateSceneRule(updateReqVO));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteSceneRule_success() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// Mock 行为
|
||||
IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
|
||||
when(sceneRuleMapper.deleteById(id)).thenReturn(1);
|
||||
|
||||
// 调用
|
||||
assertDoesNotThrow(() -> sceneRuleService.deleteSceneRule(id));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
verify(sceneRuleMapper, times(1)).deleteById(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSceneRule() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
IotSceneRuleDO expectedSceneRule = randomPojo(IotSceneRuleDO.class, o -> o.setId(id));
|
||||
|
||||
// Mock 行为
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(expectedSceneRule);
|
||||
|
||||
// 调用
|
||||
IotSceneRuleDO result = sceneRuleService.getSceneRule(id);
|
||||
|
||||
// 断言
|
||||
assertEquals(expectedSceneRule, result);
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdateSceneRuleStatus_success() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
Integer status = CommonStatusEnum.DISABLE.getStatus();
|
||||
|
||||
// Mock 行为
|
||||
IotSceneRuleDO existingSceneRule = randomPojo(IotSceneRuleDO.class, o -> {
|
||||
o.setId(id);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(existingSceneRule);
|
||||
when(sceneRuleMapper.updateById(any(IotSceneRuleDO.class))).thenReturn(1);
|
||||
|
||||
// 调用
|
||||
assertDoesNotThrow(() -> sceneRuleService.updateSceneRuleStatus(id, status));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
verify(sceneRuleMapper, times(1)).updateById(any(IotSceneRuleDO.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteSceneRuleByTimer_success() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// Mock 行为
|
||||
IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {
|
||||
o.setId(id);
|
||||
o.setStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
});
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);
|
||||
|
||||
// 调用
|
||||
assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteSceneRuleByTimer_notExists() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// Mock 行为
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(null);
|
||||
|
||||
// 调用 - 不存在的场景规则应该不会抛异常,只是记录日志
|
||||
assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExecuteSceneRuleByTimer_disabled() {
|
||||
// 准备参数
|
||||
Long id = randomLongId();
|
||||
|
||||
// Mock 行为
|
||||
IotSceneRuleDO sceneRule = randomPojo(IotSceneRuleDO.class, o -> {
|
||||
o.setId(id);
|
||||
o.setStatus(CommonStatusEnum.DISABLE.getStatus());
|
||||
});
|
||||
when(sceneRuleMapper.selectById(id)).thenReturn(sceneRule);
|
||||
|
||||
// 调用 - 禁用的场景规则应该不会执行,只是记录日志
|
||||
assertDoesNotThrow(() -> sceneRuleService.executeSceneRuleByTimer(id));
|
||||
|
||||
// 验证
|
||||
verify(sceneRuleMapper, times(1)).selectById(id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher;
|
||||
|
||||
import cn.hutool.extra.spring.SpringUtil;
|
||||
import cn.iocoder.yudao.framework.common.util.spring.SpringExpressionUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
|
||||
|
||||
/**
|
||||
* Matcher 测试基类
|
||||
* 提供通用的 Spring 测试配置
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@SpringJUnitConfig
|
||||
public abstract class IotBaseConditionMatcherTest {
|
||||
|
||||
/**
|
||||
* 注入一下 SpringUtil,解析 EL 表达式时需要
|
||||
* {@link SpringExpressionUtils#parseExpression}
|
||||
*/
|
||||
@Configuration
|
||||
static class TestConfig {
|
||||
|
||||
@Bean
|
||||
public SpringUtil springUtil() {
|
||||
return new SpringUtil();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotCurrentTimeConditionMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotCurrentTimeConditionMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotCurrentTimeConditionMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotCurrentTimeConditionMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedConditionType() {
|
||||
// 调用
|
||||
IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleConditionTypeEnum.CURRENT_TIME, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority() {
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(40, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled() {
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
// ========== 时间戳条件测试 ==========
|
||||
|
||||
@Test
|
||||
public void testMatches_DateTimeGreaterThan_success() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
long pastTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
|
||||
String.valueOf(pastTimestamp)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_DateTimeGreaterThan_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
|
||||
String.valueOf(futureTimestamp)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_DateTimeLessThan_success() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
long futureTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_LESS_THAN.getOperator(),
|
||||
String.valueOf(futureTimestamp)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_DateTimeBetween_success() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
long startTimestamp = LocalDateTime.now().minusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
long endTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),
|
||||
startTimestamp + "," + endTimestamp
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_DateTimeBetween_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
long startTimestamp = LocalDateTime.now().plusHours(1).toEpochSecond(ZoneOffset.of("+8"));
|
||||
long endTimestamp = LocalDateTime.now().plusHours(2).toEpochSecond(ZoneOffset.of("+8"));
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_BETWEEN.getOperator(),
|
||||
startTimestamp + "," + endTimestamp
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// ========== 当日时间条件测试 ==========
|
||||
|
||||
@Test
|
||||
public void testMatches_TimeGreaterThan_earlyMorning() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),
|
||||
"06:00:00" // 早上6点
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
// 结果取决于当前时间,如果当前时间大于6点则为true
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_TimeLessThan_lateNight() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_LESS_THAN.getOperator(),
|
||||
"23:59:59" // 晚上11点59分59秒
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
// 大部分情况下应该为true,除非在午夜前1秒运行测试
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_TimeBetween_allDay() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
|
||||
"00:00:00,23:59:59" // 全天
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result); // 全天范围应该总是匹配
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_TimeBetween_workingHours() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
|
||||
"09:00:00,17:00:00" // 工作时间
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
// 结果取决于当前时间是否在工作时间内
|
||||
assertNotNull(result);
|
||||
}
|
||||
|
||||
// ========== 异常情况测试 ==========
|
||||
|
||||
@Test
|
||||
public void testMatches_nullCondition() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullConditionType() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidOperator() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
|
||||
condition.setOperator(randomString()); // 随机无效操作符
|
||||
condition.setParam("12:00:00");
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidTimeFormat() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_GREATER_THAN.getOperator(),
|
||||
randomString() // 随机无效时间格式
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidTimestampFormat() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createDateTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.DATE_TIME_GREATER_THAN.getOperator(),
|
||||
randomString() // 随机无效时间戳格式
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidBetweenFormat() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.TriggerCondition condition = createTimeCondition(
|
||||
IotSceneRuleConditionOperatorEnum.TIME_BETWEEN.getOperator(),
|
||||
"09:00:00" // 缺少结束时间
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建设备消息
|
||||
*/
|
||||
private IotDeviceMessage createDeviceMessage() {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建日期时间条件
|
||||
*/
|
||||
private IotSceneRuleDO.TriggerCondition createDateTimeCondition(String operator, String param) {
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
|
||||
condition.setOperator(operator);
|
||||
condition.setParam(param);
|
||||
return condition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建当日时间条件
|
||||
*/
|
||||
private IotSceneRuleDO.TriggerCondition createTimeCondition(String operator, String param) {
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.CURRENT_TIME.getType());
|
||||
condition.setOperator(operator);
|
||||
condition.setParam(param);
|
||||
return condition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,492 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDevicePropertyConditionMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDevicePropertyConditionMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDevicePropertyConditionMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDevicePropertyConditionMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedConditionType() {
|
||||
// 调用
|
||||
IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority() {
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(25, result); // 修正:实际返回值是 25
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_temperatureEquals_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "temperature";
|
||||
Double propertyValue = 25.5;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
String.valueOf(propertyValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_humidityGreaterThan_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "humidity";
|
||||
Integer propertyValue = 75;
|
||||
Integer compareValue = 70;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_pressureLessThan_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "pressure";
|
||||
Double propertyValue = 1010.5;
|
||||
Integer compareValue = 1020;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_statusNotEquals_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "status";
|
||||
String propertyValue = "active";
|
||||
String compareValue = "inactive";
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
||||
compareValue
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_propertyMismatch_fail() {
|
||||
// 准备参数:创建属性上报消息,值不满足条件
|
||||
String propertyIdentifier = "temperature";
|
||||
Double propertyValue = 15.0;
|
||||
Integer compareValue = 20;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_identifierMismatch_fail() {
|
||||
// 准备参数:标识符不匹配
|
||||
String messageIdentifier = "temperature";
|
||||
String conditionIdentifier = "humidity";
|
||||
Double propertyValue = 25.5;
|
||||
IotDeviceMessage message = createPropertyPostMessage(messageIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
conditionIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
String.valueOf(propertyValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullCondition_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullConditionType_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingIdentifier_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||
condition.setIdentifier(null); // 缺少标识符
|
||||
condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
|
||||
condition.setParam("20");
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingOperator_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||
condition.setIdentifier("temperature");
|
||||
condition.setOperator(null); // 缺少操作符
|
||||
condition.setParam("20");
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingParam_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createPropertyPostMessage("temperature", 25.5);
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||
condition.setIdentifier("temperature");
|
||||
condition.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
|
||||
condition.setParam(null); // 缺少参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessage_fail() {
|
||||
// 准备参数
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
"temperature",
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
"20"
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(null, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullDeviceProperties_fail() {
|
||||
// 准备参数:消息的 params 为 null
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
"temperature",
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
"20"
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_propertiesStructure_success() {
|
||||
// 测试使用 properties 结构的消息(真实的属性上报场景)
|
||||
String identifier = "temperature";
|
||||
Double propertyValue = 25.5;
|
||||
IotDeviceMessage message = createPropertyPostMessageWithProperties(identifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
identifier,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
"20"
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言:修复后的实现应该能正确从 properties 中提取属性值
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_simpleValueMessage_success() {
|
||||
// 测试简单值消息(params 直接是属性值)
|
||||
Double propertyValue = 25.5;
|
||||
IotDeviceMessage message = createSimpleValueMessage(propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
"any", // 对于简单值消息,标识符匹配会被跳过
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
"20"
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言:修复后的实现应该能处理简单值消息
|
||||
// 但由于标识符匹配失败,结果为 false
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_valueFieldStructure_success() {
|
||||
// 测试使用 value 字段的消息结构
|
||||
String identifier = "temperature";
|
||||
Double propertyValue = 25.5;
|
||||
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod("thing.event.post");
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("identifier", identifier);
|
||||
params.put("value", propertyValue);
|
||||
message.setParams(params);
|
||||
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
identifier,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
"20"
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言:修复后的实现应该能从 value 字段提取属性值
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_voltageGreaterThanOrEquals_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "voltage";
|
||||
Double propertyValue = 12.0;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
|
||||
String.valueOf(propertyValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_currentLessThanOrEquals_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "current";
|
||||
Double propertyValue = 2.5;
|
||||
Double compareValue = 3.0;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_stringProperty_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "mode";
|
||||
String propertyValue = "auto";
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
propertyValue
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_booleanProperty_success() {
|
||||
// 准备参数:创建属性上报消息
|
||||
String propertyIdentifier = "enabled";
|
||||
Boolean propertyValue = true;
|
||||
IotDeviceMessage message = createPropertyPostMessage(propertyIdentifier, propertyValue);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
propertyIdentifier,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
String.valueOf(propertyValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建设备消息用于测试
|
||||
*
|
||||
* 支持的消息格式:
|
||||
* 1. 直接属性值:params 直接是属性值(适用于简单消息)
|
||||
* 2. 标识符+值:params 包含 identifier 和对应的属性值
|
||||
* 3. properties 结构:params.properties[identifier] = value
|
||||
* 4. data 结构:params.data[identifier] = value
|
||||
* 5. value 字段:params.value = value
|
||||
*/
|
||||
private IotDeviceMessage createPropertyPostMessage(String identifier, Object value) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod("thing.event.post"); // 使用事件上报方法
|
||||
|
||||
// 创建符合修复后逻辑的 params 结构
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("identifier", identifier);
|
||||
// 直接将属性值放在标识符对应的字段中
|
||||
params.put(identifier, value);
|
||||
message.setParams(params);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建使用 properties 结构的消息(模拟真实的属性上报消息)
|
||||
*/
|
||||
private IotDeviceMessage createPropertyPostMessageWithProperties(String identifier, Object value) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod("thing.property.post"); // 属性上报方法
|
||||
|
||||
Map<String, Object> properties = new HashMap<>();
|
||||
properties.put(identifier, value);
|
||||
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
params.put("properties", properties);
|
||||
message.setParams(params);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建简单值消息(params 直接是属性值)
|
||||
*/
|
||||
private IotDeviceMessage createSimpleValueMessage(Object value) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod("thing.property.post");
|
||||
// 直接将属性值作为 params
|
||||
message.setParams(value);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的条件
|
||||
*/
|
||||
private IotSceneRuleDO.TriggerCondition createValidCondition(String identifier, String operator, String param) {
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_PROPERTY.getType());
|
||||
condition.setIdentifier(identifier);
|
||||
condition.setOperator(operator);
|
||||
condition.setParam(param);
|
||||
return condition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDeviceStateConditionMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDeviceStateConditionMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDeviceStateConditionMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDeviceStateConditionMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedConditionType() {
|
||||
// 调用
|
||||
IotSceneRuleConditionTypeEnum result = matcher.getSupportedConditionType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleConditionTypeEnum.DEVICE_STATE, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority() {
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(30, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled() {
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_onlineState_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE;
|
||||
IotDeviceMessage message = createDeviceMessage(deviceState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
deviceState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_offlineState_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum deviceState = IotDeviceStateEnum.OFFLINE;
|
||||
IotDeviceMessage message = createDeviceMessage(deviceState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
deviceState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_inactiveState_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum deviceState = IotDeviceStateEnum.INACTIVE;
|
||||
IotDeviceMessage message = createDeviceMessage(deviceState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
deviceState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_stateMismatch_fail() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE;
|
||||
IotDeviceStateEnum expectedState = IotDeviceStateEnum.OFFLINE;
|
||||
IotDeviceMessage message = createDeviceMessage(actualState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
expectedState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_notEqualsOperator_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE;
|
||||
IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE;
|
||||
IotDeviceMessage message = createDeviceMessage(actualState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
||||
compareState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_greaterThanOperator_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum actualState = IotDeviceStateEnum.OFFLINE; // 状态值为 2
|
||||
IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1
|
||||
IotDeviceMessage message = createDeviceMessage(actualState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
compareState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_lessThanOperator_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum actualState = IotDeviceStateEnum.INACTIVE; // 状态值为 0
|
||||
IotDeviceStateEnum compareState = IotDeviceStateEnum.ONLINE; // 状态值为 1
|
||||
IotDeviceMessage message = createDeviceMessage(actualState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
|
||||
compareState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullCondition_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullConditionType_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingOperator_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
|
||||
condition.setOperator(null);
|
||||
condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingParam_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
|
||||
condition.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());
|
||||
condition.setParam(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessage_fail() {
|
||||
// 准备参数
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.ONLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(null, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullDeviceState_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.ONLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_greaterThanOrEqualsOperator_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum deviceState = IotDeviceStateEnum.ONLINE; // 状态值为 1
|
||||
IotDeviceMessage message = createDeviceMessage(deviceState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN_OR_EQUALS.getOperator(),
|
||||
deviceState.getState().toString() // 比较值也为 1
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_lessThanOrEqualsOperator_success() {
|
||||
// 准备参数
|
||||
IotDeviceStateEnum actualState = IotDeviceStateEnum.ONLINE; // 状态值为 1
|
||||
IotDeviceStateEnum compareState = IotDeviceStateEnum.OFFLINE; // 状态值为 2
|
||||
IotDeviceMessage message = createDeviceMessage(actualState.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.LESS_THAN_OR_EQUALS.getOperator(),
|
||||
compareState.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidOperator_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
|
||||
condition.setOperator(randomString()); // 随机无效操作符
|
||||
condition.setParam(IotDeviceStateEnum.ONLINE.getState().toString());
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidParamFormat_fail() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.TriggerCondition condition = createValidCondition(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
randomString() // 随机无效状态值
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, condition);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建设备消息
|
||||
*/
|
||||
private IotDeviceMessage createDeviceMessage(Integer deviceState) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setParams(deviceState);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的条件
|
||||
*/
|
||||
private IotSceneRuleDO.TriggerCondition createValidCondition(String operator, String param) {
|
||||
IotSceneRuleDO.TriggerCondition condition = new IotSceneRuleDO.TriggerCondition();
|
||||
condition.setType(IotSceneRuleConditionTypeEnum.DEVICE_STATE.getType());
|
||||
condition.setOperator(operator);
|
||||
condition.setParam(param);
|
||||
return condition;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,380 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomInt;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDeviceEventPostTriggerMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDeviceEventPostTriggerMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDeviceEventPostTriggerMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDeviceEventPostTriggerMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedTriggerType_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(30, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_alarmEventSuccess() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.put("message", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_errorEventSuccess() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("code", randomInt())
|
||||
.put("description", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_infoEventSuccess() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("status", randomString())
|
||||
.put("timestamp", System.currentTimeMillis())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_eventIdentifierMismatch() {
|
||||
// 准备参数
|
||||
String messageIdentifier = randomString();
|
||||
String triggerIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", messageIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_wrongMessageMethod() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法
|
||||
message.setParams(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerIdentifier() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
|
||||
trigger.setIdentifier(null); // 缺少标识符
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessageParams() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidMessageParams() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
|
||||
message.setParams(randomString()); // 不是 Map 类型
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingEventIdentifierInParams() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build()) // 缺少 identifier 字段
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTrigger() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerType() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(null);
|
||||
trigger.setIdentifier(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_complexEventValueSuccess() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("type", randomString())
|
||||
.put("duration", randomInt())
|
||||
.put("components", new String[]{randomString(), randomString()})
|
||||
.put("priority", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_emptyEventValueSuccess() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString();
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.ofEntries()) // 空的事件值
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(eventIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_caseSensitiveIdentifierMismatch() {
|
||||
// 准备参数
|
||||
String eventIdentifier = randomString().toUpperCase(); // 大写
|
||||
String triggerIdentifier = eventIdentifier.toLowerCase(); // 小写
|
||||
Map<String, Object> eventParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", eventIdentifier)
|
||||
.put("value", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createEventPostMessage(eventParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
// 根据实际实现,这里可能需要调整期望结果
|
||||
// 如果实现是大小写敏感的,则应该为 false
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建事件上报消息
|
||||
*/
|
||||
private IotDeviceMessage createEventPostMessage(Map<String, Object> eventParams) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.EVENT_POST.getMethod());
|
||||
message.setParams(eventParams);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的触发器
|
||||
*/
|
||||
private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_EVENT_POST.getType());
|
||||
trigger.setIdentifier(identifier);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.randomDouble;
|
||||
import static cn.hutool.core.util.RandomUtil.randomInt;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDevicePropertyPostTriggerMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDevicePropertyPostTriggerMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDevicePropertyPostTriggerMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDevicePropertyPostTriggerMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedTriggerType_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(20, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_numericPropertyGreaterThanSuccess() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Double propertyValue = 25.5;
|
||||
Integer compareValue = 20;
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, propertyValue)
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_integerPropertyEqualsSuccess() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Integer propertyValue = randomInt();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, propertyValue)
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
String.valueOf(propertyValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_propertyValueNotMeetCondition() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Double propertyValue = 15.0;
|
||||
Integer compareValue = 20;
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, propertyValue)
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_propertyNotFound() {
|
||||
// 准备参数
|
||||
String existingProperty = randomString();
|
||||
String missingProperty = randomString();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(existingProperty, randomDouble())
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
missingProperty, // 不存在的属性
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(randomInt())
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_wrongMessageMethod() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, randomDouble())
|
||||
.build();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
|
||||
message.setParams(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(randomInt())
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerIdentifier() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, randomDouble())
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
|
||||
trigger.setIdentifier(null); // 缺少标识符
|
||||
trigger.setOperator(IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator());
|
||||
trigger.setValue(String.valueOf(randomInt()));
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessageParams() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(randomInt())
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidMessageParams() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
|
||||
message.setParams(randomString()); // 不是 Map 类型
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
String.valueOf(randomInt())
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_lessThanOperatorSuccess() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
Double propertyValue = 15.0;
|
||||
Integer compareValue = 20;
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, propertyValue)
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.LESS_THAN.getOperator(),
|
||||
String.valueOf(compareValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_notEqualsOperatorSuccess() {
|
||||
// 准备参数
|
||||
String propertyName = randomString();
|
||||
String propertyValue = randomString();
|
||||
String compareValue = randomString();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(propertyName, propertyValue)
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
propertyName,
|
||||
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
||||
compareValue
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_multiplePropertiesTargetPropertySuccess() {
|
||||
// 准备参数
|
||||
String targetProperty = randomString();
|
||||
Integer targetValue = randomInt();
|
||||
Map<String, Object> properties = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put(randomString(), randomDouble())
|
||||
.put(targetProperty, targetValue)
|
||||
.put(randomString(), randomString())
|
||||
.build();
|
||||
IotDeviceMessage message = createPropertyPostMessage(properties);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
targetProperty,
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
String.valueOf(targetValue)
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建属性上报消息
|
||||
*/
|
||||
private IotDeviceMessage createPropertyPostMessage(Map<String, Object> properties) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
|
||||
message.setParams(properties);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的触发器
|
||||
*/
|
||||
private IotSceneRuleDO.Trigger createValidTrigger(String identifier, String operator, String value) {
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
|
||||
trigger.setIdentifier(identifier);
|
||||
trigger.setOperator(operator);
|
||||
trigger.setValue(value);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,402 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
||||
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.hutool.core.util.RandomUtil.*;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDeviceServiceInvokeTriggerMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDeviceServiceInvokeTriggerMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDeviceServiceInvokeTriggerMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDeviceServiceInvokeTriggerMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedTriggerType_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(40, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_serviceInvokeSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_configServiceSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("interval", randomInt())
|
||||
.put("enabled", randomBoolean())
|
||||
.put("threshold", randomDouble())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_updateServiceSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("version", randomString())
|
||||
.put("url", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_serviceIdentifierMismatch() {
|
||||
// 准备参数
|
||||
String messageIdentifier = randomString();
|
||||
String triggerIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", messageIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier); // 不匹配的服务标识符
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_wrongMessageMethod() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod()); // 错误的方法
|
||||
message.setParams(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerIdentifier() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
|
||||
trigger.setIdentifier(null); // 缺少标识符
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessageParams() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidMessageParams() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
|
||||
message.setParams(randomString()); // 不是 Map 类型
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_missingServiceIdentifierInParams() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build()) // 缺少 identifier 字段
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTrigger() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerType() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(null);
|
||||
trigger.setIdentifier(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_emptyInputDataSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.ofEntries()) // 空的输入数据
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_noInputDataSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
// 没有 inputData 字段
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_complexInputDataSuccess() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString();
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("sensors", new String[]{randomString(), randomString(), randomString()})
|
||||
.put("precision", randomDouble())
|
||||
.put("duration", randomInt())
|
||||
.put("autoSave", randomBoolean())
|
||||
.put("config", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.put("level", randomString())
|
||||
.build())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(serviceIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_caseSensitiveIdentifierMismatch() {
|
||||
// 准备参数
|
||||
String serviceIdentifier = randomString().toUpperCase(); // 大写
|
||||
String triggerIdentifier = serviceIdentifier.toLowerCase(); // 小写
|
||||
Map<String, Object> serviceParams = MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("identifier", serviceIdentifier)
|
||||
.put("inputData", MapUtil.builder(new HashMap<String, Object>())
|
||||
.put("mode", randomString())
|
||||
.build())
|
||||
.build();
|
||||
IotDeviceMessage message = createServiceInvokeMessage(serviceParams);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(triggerIdentifier);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
// 根据实际实现,这里可能需要调整期望结果
|
||||
// 如果实现是大小写敏感的,则应该为 false
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建服务调用消息
|
||||
*/
|
||||
private IotDeviceMessage createServiceInvokeMessage(Map<String, Object> serviceParams) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.SERVICE_INVOKE.getMethod());
|
||||
message.setParams(serviceParams);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的触发器
|
||||
*/
|
||||
private IotSceneRuleDO.Trigger createValidTrigger(String identifier) {
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_SERVICE_INVOKE.getType());
|
||||
trigger.setIdentifier(identifier);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,266 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.enums.IotDeviceStateEnum;
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleConditionOperatorEnum;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotDeviceStateUpdateTriggerMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotDeviceStateUpdateTriggerMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotDeviceStateUpdateTriggerMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotDeviceStateUpdateTriggerMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedTriggerType_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(10, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_onlineStateSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.ONLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_offlineStateSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.OFFLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.OFFLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_stateMismatch() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.OFFLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTrigger() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerType() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_wrongMessageMethod() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
|
||||
message.setParams(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.ONLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerOperator() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
|
||||
trigger.setOperator(null);
|
||||
trigger.setValue(IotDeviceStateEnum.ONLINE.getState().toString());
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerValue() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
|
||||
trigger.setOperator(IotSceneRuleConditionOperatorEnum.EQUALS.getOperator());
|
||||
trigger.setValue(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullMessageParams() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
|
||||
message.setParams(null);
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.ONLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_greaterThanOperatorSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.GREATER_THAN.getOperator(),
|
||||
IotDeviceStateEnum.INACTIVE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_notEqualsOperatorSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createStateUpdateMessage(IotDeviceStateEnum.ONLINE.getState());
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(
|
||||
IotSceneRuleConditionOperatorEnum.NOT_EQUALS.getOperator(),
|
||||
IotDeviceStateEnum.OFFLINE.getState().toString()
|
||||
);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建设备状态更新消息
|
||||
*/
|
||||
private IotDeviceMessage createStateUpdateMessage(Integer state) {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
message.setMethod(IotDeviceMessageMethodEnum.STATE_UPDATE.getMethod());
|
||||
message.setParams(state);
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的触发器
|
||||
*/
|
||||
private IotSceneRuleDO.Trigger createValidTrigger(String operator, String value) {
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_STATE_UPDATE.getType());
|
||||
trigger.setOperator(operator);
|
||||
trigger.setValue(value);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger;
|
||||
|
||||
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotBaseConditionMatcherTest;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomLongId;
|
||||
import static cn.iocoder.yudao.framework.test.core.util.RandomUtils.randomString;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/**
|
||||
* {@link IotTimerTriggerMatcher} 的单元测试
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
public class IotTimerTriggerMatcherTest extends IotBaseConditionMatcherTest {
|
||||
|
||||
private IotTimerTriggerMatcher matcher;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp() {
|
||||
matcher = new IotTimerTriggerMatcher();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportedTriggerType_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
IotSceneRuleTriggerTypeEnum result = matcher.getSupportedTriggerType();
|
||||
|
||||
// 断言
|
||||
assertEquals(IotSceneRuleTriggerTypeEnum.TIMER, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPriority_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
int result = matcher.getPriority();
|
||||
|
||||
// 断言
|
||||
assertEquals(50, result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsEnabled_success() {
|
||||
// 准备参数
|
||||
// 无需准备参数
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.isEnabled();
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_validCronExpressionSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 0 12 * * ?"; // 每天中午12点
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_everyMinuteCronSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 * * * * ?"; // 每分钟
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_weekdaysCronSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 0 9 ? * MON-FRI"; // 工作日上午9点
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidCronExpression() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = randomString(); // 随机无效的 cron 表达式
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_emptyCronExpression() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = ""; // 空的 cron 表达式
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullCronExpression() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
|
||||
trigger.setCronExpression(null);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTrigger() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, null);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_nullTriggerType() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(null);
|
||||
trigger.setCronExpression("0 0 12 * * ?");
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_complexCronExpressionSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 15 10 ? * 6#3"; // 每月第三个星期五上午10:15
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_incorrectCronFormat() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 0 12 * *"; // 缺少字段的 cron 表达式
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_specificDateCronSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 0 0 1 1 ? 2025"; // 2025年1月1日午夜
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_everySecondCronSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "* * * * * ?"; // 每秒执行
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_invalidCharactersCron() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
String cronExpression = "0 0 12 * * @ #"; // 包含无效字符的 cron 表达式
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger(cronExpression);
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertFalse(result);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMatches_rangeCronSuccess() {
|
||||
// 准备参数
|
||||
IotDeviceMessage message = createDeviceMessage();
|
||||
IotSceneRuleDO.Trigger trigger = createValidTrigger("0 0 9-17 * * MON-FRI"); // 工作日9-17点
|
||||
|
||||
// 调用
|
||||
boolean result = matcher.matches(message, trigger);
|
||||
|
||||
// 断言
|
||||
assertTrue(result);
|
||||
}
|
||||
|
||||
// ========== 辅助方法 ==========
|
||||
|
||||
/**
|
||||
* 创建设备消息
|
||||
*/
|
||||
private IotDeviceMessage createDeviceMessage() {
|
||||
IotDeviceMessage message = new IotDeviceMessage();
|
||||
message.setDeviceId(randomLongId());
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建有效的定时触发器
|
||||
*/
|
||||
private IotSceneRuleDO.Trigger createValidTrigger(String cronExpression) {
|
||||
IotSceneRuleDO.Trigger trigger = new IotSceneRuleDO.Trigger();
|
||||
trigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
|
||||
trigger.setCronExpression(cronExpression);
|
||||
return trigger;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package cn.iocoder.yudao.module.iot.service.rule.scene.timer;
|
||||
|
||||
import cn.hutool.core.collection.ListUtil;
|
||||
import cn.iocoder.yudao.module.iot.dal.dataobject.rule.IotSceneRuleDO;
|
||||
import cn.iocoder.yudao.module.iot.enums.rule.IotSceneRuleTriggerTypeEnum;
|
||||
import cn.iocoder.yudao.module.iot.framework.job.core.IotSchedulerManager;
|
||||
import cn.iocoder.yudao.module.iot.job.rule.IotSceneRuleJob;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.quartz.SchedulerException;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
/**
|
||||
* {@link IotSceneRuleTimerHandler} 的单元测试类
|
||||
*
|
||||
* @author HUIHUI
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class IotSceneRuleTimerHandlerTest {
|
||||
|
||||
@Mock
|
||||
private IotSchedulerManager schedulerManager;
|
||||
|
||||
@InjectMocks
|
||||
private IotSceneRuleTimerHandler timerHandler;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// 重置 Mock 对象
|
||||
reset(schedulerManager);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterTimerTriggers_success() throws SchedulerException {
|
||||
// 准备参数
|
||||
Long sceneRuleId = 1L;
|
||||
IotSceneRuleDO sceneRule = new IotSceneRuleDO();
|
||||
sceneRule.setId(sceneRuleId);
|
||||
sceneRule.setStatus(0); // 0 表示启用
|
||||
// 创建定时触发器
|
||||
IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger();
|
||||
timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
|
||||
timerTrigger.setCronExpression("0 0 12 * * ?"); // 每天中午12点
|
||||
sceneRule.setTriggers(ListUtil.toList(timerTrigger));
|
||||
|
||||
// 调用
|
||||
timerHandler.registerTimerTriggers(sceneRule);
|
||||
|
||||
// 验证
|
||||
verify(schedulerManager, times(1)).addOrUpdateJob(
|
||||
eq(IotSceneRuleJob.class),
|
||||
eq("iot_scene_rule_timer_" + sceneRuleId),
|
||||
eq("0 0 12 * * ?"),
|
||||
eq(IotSceneRuleJob.buildJobDataMap(sceneRuleId))
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterTimerTriggers_noTimerTrigger() throws SchedulerException {
|
||||
// 准备参数 - 没有定时触发器
|
||||
IotSceneRuleDO sceneRule = new IotSceneRuleDO();
|
||||
sceneRule.setStatus(0); // 0 表示启用
|
||||
// 创建非定时触发器
|
||||
IotSceneRuleDO.Trigger deviceTrigger = new IotSceneRuleDO.Trigger();
|
||||
deviceTrigger.setType(IotSceneRuleTriggerTypeEnum.DEVICE_PROPERTY_POST.getType());
|
||||
sceneRule.setTriggers(ListUtil.toList(deviceTrigger));
|
||||
|
||||
// 调用
|
||||
timerHandler.registerTimerTriggers(sceneRule);
|
||||
|
||||
// 验证 - 不应该调用调度器
|
||||
verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterTimerTriggers_emptyCronExpression() throws SchedulerException {
|
||||
// 准备参数 - CRON 表达式为空
|
||||
Long sceneRuleId = 2L;
|
||||
IotSceneRuleDO sceneRule = new IotSceneRuleDO();
|
||||
sceneRule.setId(sceneRuleId);
|
||||
sceneRule.setStatus(0); // 0 表示启用
|
||||
// 创建定时触发器但没有 CRON 表达式
|
||||
IotSceneRuleDO.Trigger timerTrigger = new IotSceneRuleDO.Trigger();
|
||||
timerTrigger.setType(IotSceneRuleTriggerTypeEnum.TIMER.getType());
|
||||
timerTrigger.setCronExpression(""); // 空的 CRON 表达式
|
||||
sceneRule.setTriggers(ListUtil.toList(timerTrigger));
|
||||
|
||||
// 调用
|
||||
timerHandler.registerTimerTriggers(sceneRule);
|
||||
|
||||
// 验证 - 不应该调用调度器
|
||||
verify(schedulerManager, never()).addOrUpdateJob(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnregisterTimerTriggers_success() throws SchedulerException {
|
||||
// 准备参数
|
||||
Long sceneRuleId = 3L;
|
||||
|
||||
// 调用
|
||||
timerHandler.unregisterTimerTriggers(sceneRuleId);
|
||||
|
||||
// 验证
|
||||
verify(schedulerManager, times(1)).deleteJob("iot_scene_rule_timer_" + sceneRuleId);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPauseTimerTriggers_success() throws SchedulerException {
|
||||
// 准备参数
|
||||
Long sceneRuleId = 4L;
|
||||
|
||||
// 调用
|
||||
timerHandler.pauseTimerTriggers(sceneRuleId);
|
||||
|
||||
// 验证
|
||||
verify(schedulerManager, times(1)).pauseJob("iot_scene_rule_timer_" + sceneRuleId);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
spring:
|
||||
main:
|
||||
lazy-initialization: true # 开启懒加载,加快速度
|
||||
banner-mode: off # 单元测试,禁用 Banner
|
||||
# 数据源配置项
|
||||
datasource:
|
||||
name: ruoyi-vue-pro
|
||||
url: jdbc:h2:mem:testdb;MODE=MYSQL;DATABASE_TO_UPPER=false;NON_KEYWORDS=value; # MODE 使用 MySQL 模式;DATABASE_TO_UPPER 配置表和字段使用小写
|
||||
driver-class-name: org.h2.Driver
|
||||
username: sa
|
||||
password:
|
||||
druid:
|
||||
async-init: true # 单元测试,异步初始化 Druid 连接池,提升启动速度
|
||||
initial-size: 1 # 单元测试,配置为 1,提升启动速度
|
||||
sql:
|
||||
init:
|
||||
schema-locations: classpath:/sql/create_tables.sql
|
||||
|
||||
mybatis-plus:
|
||||
lazy-initialization: true # 单元测试,设置 MyBatis Mapper 延迟加载,加速每个单元测试
|
||||
type-aliases-package: ${yudao.info.base-package}.module.*.dal.dataobject
|
||||
|
||||
# 日志配置
|
||||
logging:
|
||||
level:
|
||||
cn.iocoder.yudao.module.iot.service.rule.scene.matcher: DEBUG
|
||||
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager: INFO
|
||||
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition: DEBUG
|
||||
cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger: DEBUG
|
||||
root: WARN
|
||||
|
||||
--- #################### 定时任务相关配置 ####################
|
||||
|
||||
--- #################### 配置中心相关配置 ####################
|
||||
|
||||
--- #################### 服务保障相关配置 ####################
|
||||
|
||||
# Lock4j 配置项(单元测试,禁用 Lock4j)
|
||||
|
||||
--- #################### 监控相关配置 ####################
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
# 芋道配置项,设置当前项目所有自定义的配置
|
||||
yudao:
|
||||
info:
|
||||
base-package: cn.iocoder.yudao
|
||||
tenant: # 多租户相关配置项
|
||||
enable: true
|
||||
xss:
|
||||
enable: false
|
||||
demo: false # 关闭演示模式
|
||||
@@ -0,0 +1,37 @@
|
||||
<configuration>
|
||||
<!-- 引用 Spring Boot 的 logback 基础配置 -->
|
||||
<include resource="org/springframework/boot/logging/logback/defaults.xml"/>
|
||||
|
||||
<!-- 控制台输出 -->
|
||||
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
|
||||
<charset>UTF-8</charset>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!-- 设置特定包的日志级别 -->
|
||||
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<!-- 设置 IotSceneRuleMatcherManager 的日志级别 -->
|
||||
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.IotSceneRuleMatcherManager" level="INFO"
|
||||
additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<!-- 设置所有匹配器的日志级别 -->
|
||||
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.condition" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<logger name="cn.iocoder.yudao.module.iot.service.rule.scene.matcher.trigger" level="DEBUG" additivity="false">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</logger>
|
||||
|
||||
<!-- 根日志级别 -->
|
||||
<root level="WARN">
|
||||
<appender-ref ref="CONSOLE"/>
|
||||
</root>
|
||||
</configuration>
|
||||
@@ -0,0 +1,10 @@
|
||||
DELETE FROM "iot_scene_rule";
|
||||
DELETE FROM "iot_product";
|
||||
DELETE FROM "iot_device";
|
||||
DELETE FROM "iot_thing_model";
|
||||
DELETE FROM "iot_device_data";
|
||||
DELETE FROM "iot_alert_config";
|
||||
DELETE FROM "iot_alert_record";
|
||||
DELETE FROM "iot_ota_firmware";
|
||||
DELETE FROM "iot_ota_task";
|
||||
DELETE FROM "iot_ota_record";
|
||||
@@ -0,0 +1,182 @@
|
||||
CREATE TABLE IF NOT EXISTS "iot_scene_rule" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"description" varchar(500) DEFAULT NULL,
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"triggers" text,
|
||||
"actions" text,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 场景联动规则表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_product" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"product_key" varchar(100) NOT NULL DEFAULT '',
|
||||
"protocol_type" tinyint NOT NULL DEFAULT '0',
|
||||
"category_id" bigint DEFAULT NULL,
|
||||
"description" varchar(500) DEFAULT NULL,
|
||||
"data_format" tinyint NOT NULL DEFAULT '0',
|
||||
"device_type" tinyint NOT NULL DEFAULT '0',
|
||||
"net_type" tinyint NOT NULL DEFAULT '0',
|
||||
"validate_type" tinyint NOT NULL DEFAULT '0',
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 产品表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_device" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"device_name" varchar(255) NOT NULL DEFAULT '',
|
||||
"product_id" bigint NOT NULL,
|
||||
"device_key" varchar(100) NOT NULL DEFAULT '',
|
||||
"device_secret" varchar(100) NOT NULL DEFAULT '',
|
||||
"nickname" varchar(255) DEFAULT NULL,
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"status_last_update_time" timestamp DEFAULT NULL,
|
||||
"last_online_time" timestamp DEFAULT NULL,
|
||||
"last_offline_time" timestamp DEFAULT NULL,
|
||||
"active_time" timestamp DEFAULT NULL,
|
||||
"ip" varchar(50) DEFAULT NULL,
|
||||
"firmware_version" varchar(50) DEFAULT NULL,
|
||||
"device_type" tinyint NOT NULL DEFAULT '0',
|
||||
"gateway_id" bigint DEFAULT NULL,
|
||||
"sub_device_count" int NOT NULL DEFAULT '0',
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 设备表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_thing_model" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"product_id" bigint NOT NULL,
|
||||
"identifier" varchar(100) NOT NULL DEFAULT '',
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"description" varchar(500) DEFAULT NULL,
|
||||
"type" tinyint NOT NULL DEFAULT '1',
|
||||
"property" text,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 物模型表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_device_data" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"device_id" bigint NOT NULL,
|
||||
"product_id" bigint NOT NULL,
|
||||
"identifier" varchar(100) NOT NULL DEFAULT '',
|
||||
"type" tinyint NOT NULL DEFAULT '1',
|
||||
"data" text,
|
||||
"ts" bigint NOT NULL DEFAULT '0',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 设备数据表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_alert_config" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"product_id" bigint NOT NULL,
|
||||
"device_id" bigint DEFAULT NULL,
|
||||
"rule_id" bigint DEFAULT NULL,
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 告警配置表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_alert_record" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"alert_config_id" bigint NOT NULL,
|
||||
"alert_name" varchar(255) NOT NULL DEFAULT '',
|
||||
"product_id" bigint NOT NULL,
|
||||
"device_id" bigint DEFAULT NULL,
|
||||
"rule_id" bigint DEFAULT NULL,
|
||||
"alert_data" text,
|
||||
"alert_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deal_status" tinyint NOT NULL DEFAULT '0',
|
||||
"deal_time" timestamp DEFAULT NULL,
|
||||
"deal_user_id" bigint DEFAULT NULL,
|
||||
"deal_remark" varchar(500) DEFAULT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT 告警记录表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_ota_firmware" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"product_id" bigint NOT NULL,
|
||||
"version" varchar(50) NOT NULL DEFAULT '',
|
||||
"description" varchar(500) DEFAULT NULL,
|
||||
"file_url" varchar(500) DEFAULT NULL,
|
||||
"file_size" bigint NOT NULL DEFAULT '0',
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT OTA 固件表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_ota_task" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"name" varchar(255) NOT NULL DEFAULT '',
|
||||
"firmware_id" bigint NOT NULL,
|
||||
"product_id" bigint NOT NULL,
|
||||
"upgrade_type" tinyint NOT NULL DEFAULT '0',
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT OTA 升级任务表';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "iot_ota_record" (
|
||||
"id" bigint NOT NULL GENERATED BY DEFAULT AS IDENTITY,
|
||||
"task_id" bigint NOT NULL,
|
||||
"firmware_id" bigint NOT NULL,
|
||||
"device_id" bigint NOT NULL,
|
||||
"status" tinyint NOT NULL DEFAULT '0',
|
||||
"progress" int NOT NULL DEFAULT '0',
|
||||
"error_msg" varchar(500) DEFAULT NULL,
|
||||
"start_time" timestamp DEFAULT NULL,
|
||||
"end_time" timestamp DEFAULT NULL,
|
||||
"creator" varchar(64) DEFAULT '',
|
||||
"create_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updater" varchar(64) DEFAULT '',
|
||||
"update_time" timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"deleted" bit NOT NULL DEFAULT FALSE,
|
||||
"tenant_id" bigint NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY ("id")
|
||||
) COMMENT 'IoT OTA 升级记录表';
|
||||
Reference in New Issue
Block a user