diff --git a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java index d653c9c..1f019b5 100644 --- a/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java +++ b/yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java @@ -38,8 +38,7 @@ public class IotDevicePropertyPostTriggerMatcher implements IotSceneRuleTriggerM // 1.3 检查消息中是否包含触发器指定的属性标识符 // 注意:属性上报可能同时上报多个属性,所以需要判断 trigger.getIdentifier() 是否在 message 的 params 中 - // TODO @puhui999:可以考虑 notXXX 方法,简化代码(尽量取反) - if (!IotDeviceMessageUtils.containsIdentifier(message, trigger.getIdentifier())) { + if (IotDeviceMessageUtils.notContainsIdentifier(message, trigger.getIdentifier())) { IotSceneRuleMatcherHelper.logTriggerMatchFailure(message, trigger, "消息中不包含属性: " + trigger.getIdentifier()); return false; diff --git a/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java new file mode 100644 index 0000000..cd28f8f --- /dev/null +++ b/yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/data/action/tcp/IotTcpClientTest.java @@ -0,0 +1,151 @@ +package cn.iocoder.yudao.module.iot.service.rule.data.action.tcp; + +import cn.hutool.core.util.ReflectUtil; +import cn.iocoder.yudao.module.iot.dal.dataobject.rule.config.IotDataSinkTcpConfig; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * {@link IotTcpClient} 的单元测试 + *
+ * 测试 dataFormat 默认值行为
+ * Property 1: TCP 客户端 dataFormat 默认值行为
+ * Validates: Requirements 1.1, 1.2
+ *
+ * @author HUIHUI
+ */
+class IotTcpClientTest {
+
+ @Test
+ public void testConstructor_dataFormatNull() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);
+
+ // 断言:dataFormat 为 null 时应使用默认值
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,
+ ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testConstructor_dataFormatEmpty() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, "");
+
+ // 断言:dataFormat 为空字符串时应使用默认值
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,
+ ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testConstructor_dataFormatBlank() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, " ");
+
+ // 断言:dataFormat 为纯空白字符串时应使用默认值
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,
+ ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testConstructor_dataFormatValid() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+ String dataFormat = "BINARY";
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, dataFormat);
+
+ // 断言:dataFormat 为有效值时应保持原值
+ assertEquals(dataFormat, ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testConstructor_defaultValues() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);
+
+ // 断言:验证所有默认值
+ assertEquals(host, ReflectUtil.getFieldValue(client, "host"));
+ assertEquals(port, ReflectUtil.getFieldValue(client, "port"));
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_CONNECT_TIMEOUT_MS,
+ ReflectUtil.getFieldValue(client, "connectTimeoutMs"));
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_READ_TIMEOUT_MS,
+ ReflectUtil.getFieldValue(client, "readTimeoutMs"));
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_SSL,
+ ReflectUtil.getFieldValue(client, "ssl"));
+ assertEquals(IotDataSinkTcpConfig.DEFAULT_DATA_FORMAT,
+ ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testConstructor_customValues() {
+ // 准备参数
+ String host = "192.168.1.100";
+ Integer port = 9090;
+ Integer connectTimeoutMs = 3000;
+ Integer readTimeoutMs = 8000;
+ Boolean ssl = true;
+ String dataFormat = "BINARY";
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, connectTimeoutMs, readTimeoutMs, ssl, dataFormat);
+
+ // 断言:验证自定义值
+ assertEquals(host, ReflectUtil.getFieldValue(client, "host"));
+ assertEquals(port, ReflectUtil.getFieldValue(client, "port"));
+ assertEquals(connectTimeoutMs, ReflectUtil.getFieldValue(client, "connectTimeoutMs"));
+ assertEquals(readTimeoutMs, ReflectUtil.getFieldValue(client, "readTimeoutMs"));
+ assertEquals(ssl, ReflectUtil.getFieldValue(client, "ssl"));
+ assertEquals(dataFormat, ReflectUtil.getFieldValue(client, "dataFormat"));
+ }
+
+ @Test
+ public void testIsConnected_initialState() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);
+
+ // 断言:初始状态应为未连接
+ assertFalse(client.isConnected());
+ }
+
+ @Test
+ public void testToString() {
+ // 准备参数
+ String host = "localhost";
+ Integer port = 8080;
+
+ // 调用
+ IotTcpClient client = new IotTcpClient(host, port, null, null, null, null);
+ String result = client.toString();
+
+ // 断言
+ assertNotNull(result);
+ assertTrue(result.contains("host='localhost'"));
+ assertTrue(result.contains("port=8080"));
+ assertTrue(result.contains("dataFormat='JSON'"));
+ assertTrue(result.contains("connected=false"));
+ }
+
+}
diff --git a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
index 5c1ac26..b02a9b4 100644
--- a/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
+++ b/yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtils.java
@@ -100,6 +100,17 @@ public class IotDeviceMessageUtils {
}
/**
+ * 判断消息中是否不包含指定的标识符
+ *
+ * @param message 消息
+ * @param identifier 要检查的标识符
+ * @return 是否不包含
+ */
+ public static boolean notContainsIdentifier(IotDeviceMessage message, String identifier) {
+ return !containsIdentifier(message, identifier);
+ }
+
+ /**
* 将 params 解析为 Map
*
* @param params 参数(可能是 Map 或 JSON 字符串)
diff --git a/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java b/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java
index a6d669d..b0d39be 100644
--- a/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java
+++ b/yudao-module-iot/yudao-module-iot-core/src/test/java/cn/iocoder/yudao/module/iot/core/util/IotDeviceMessageUtilsTest.java
@@ -1,13 +1,13 @@
package cn.iocoder.yudao.module.iot.core.util;
+import cn.iocoder.yudao.module.iot.core.enums.IotDeviceMessageMethodEnum;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import org.junit.jupiter.api.Test;
import java.util.HashMap;
import java.util.Map;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.*;
/**
* {@link IotDeviceMessageUtils} 的单元测试
@@ -138,4 +138,72 @@ public class IotDeviceMessageUtilsTest {
Object result = IotDeviceMessageUtils.extractPropertyValue(message, "temperature");
assertEquals(25.5, result); // 应该返回直接标识符的值
}
+
+ // ========== notContainsIdentifier 测试 ==========
+
+ /**
+ * 测试 notContainsIdentifier 与 containsIdentifier 的互补性
+ * **Property 2: notContainsIdentifier 与 containsIdentifier 互补性**
+ * **Validates: Requirements 4.1**
+ */
+ @Test
+ public void testNotContainsIdentifier_complementary_whenContains() {
+ // 准备参数:消息包含指定标识符
+ IotDeviceMessage message = new IotDeviceMessage();
+ message.setMethod(IotDeviceMessageMethodEnum.PROPERTY_POST.getMethod());
+ Map