|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+package com.iot.platform.mqtt;
|
|
|
2
|
+
|
|
|
3
|
+import com.iot.platform.config.IotProperties;
|
|
|
4
|
+import com.iot.platform.domain.SysController;
|
|
|
5
|
+import com.iot.platform.service.SysControllerService;
|
|
|
6
|
+import com.iot.platform.service.TDengineService;
|
|
|
7
|
+import org.junit.jupiter.api.BeforeEach;
|
|
|
8
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
9
|
+import org.junit.jupiter.api.Test;
|
|
|
10
|
+import org.junit.jupiter.api.extension.ExtendWith;
|
|
|
11
|
+import org.mockito.Mock;
|
|
|
12
|
+import org.mockito.junit.jupiter.MockitoExtension;
|
|
|
13
|
+import org.springframework.data.redis.core.HashOperations;
|
|
|
14
|
+import org.springframework.data.redis.core.SetOperations;
|
|
|
15
|
+import org.springframework.data.redis.core.StringRedisTemplate;
|
|
|
16
|
+
|
|
|
17
|
+import java.util.*;
|
|
|
18
|
+import java.util.concurrent.ExecutorService;
|
|
|
19
|
+import java.util.concurrent.Executors;
|
|
|
20
|
+import java.util.concurrent.ScheduledExecutorService;
|
|
|
21
|
+import java.util.concurrent.TimeUnit;
|
|
|
22
|
+
|
|
|
23
|
+import static org.assertj.core.api.Assertions.assertThat;
|
|
|
24
|
+import static org.mockito.ArgumentMatchers.*;
|
|
|
25
|
+import static org.mockito.Mockito.*;
|
|
|
26
|
+
|
|
|
27
|
+@ExtendWith(MockitoExtension.class)
|
|
|
28
|
+class MqttDynamicConsumerTest {
|
|
|
29
|
+
|
|
|
30
|
+ @Mock
|
|
|
31
|
+ private IotProperties iotProperties;
|
|
|
32
|
+
|
|
|
33
|
+ @Mock
|
|
|
34
|
+ private SysControllerService sysControllerService;
|
|
|
35
|
+
|
|
|
36
|
+ @Mock
|
|
|
37
|
+ private TDengineService tdengineService;
|
|
|
38
|
+
|
|
|
39
|
+ @Mock
|
|
|
40
|
+ private StringRedisTemplate stringRedisTemplate;
|
|
|
41
|
+
|
|
|
42
|
+ @Mock
|
|
|
43
|
+ private HashOperations<String, Object, Object> hashOperations;
|
|
|
44
|
+
|
|
|
45
|
+ @Mock
|
|
|
46
|
+ private SetOperations<String, String> setOperations;
|
|
|
47
|
+
|
|
|
48
|
+ private MqttDynamicConsumer consumer;
|
|
|
49
|
+
|
|
|
50
|
+ @BeforeEach
|
|
|
51
|
+ void setUp() {
|
|
|
52
|
+ ScheduledExecutorService coreExecutor = Executors.newSingleThreadScheduledExecutor();
|
|
|
53
|
+ ExecutorService writeExecutor = Executors.newSingleThreadExecutor();
|
|
|
54
|
+ consumer = new MqttDynamicConsumer(iotProperties, coreExecutor, writeExecutor,
|
|
|
55
|
+ sysControllerService, tdengineService, stringRedisTemplate);
|
|
|
56
|
+ }
|
|
|
57
|
+
|
|
|
58
|
+ @Test
|
|
|
59
|
+ @DisplayName("fetchTopics 应返回 sysControllerService.selectall() 的结果")
|
|
|
60
|
+ void fetchTopics_delegatesToService() {
|
|
|
61
|
+ List<String> expected = Arrays.asList("topic/1", "topic/2");
|
|
|
62
|
+ when(sysControllerService.selectall()).thenReturn(expected);
|
|
|
63
|
+
|
|
|
64
|
+ List<String> result = consumer.fetchTopics();
|
|
|
65
|
+
|
|
|
66
|
+ assertThat(result).isEqualTo(expected);
|
|
|
67
|
+ verify(sysControllerService).selectall();
|
|
|
68
|
+ }
|
|
|
69
|
+
|
|
|
70
|
+ @Test
|
|
|
71
|
+ @DisplayName("fetchTopics 应处理 null 返回值")
|
|
|
72
|
+ void fetchTopics_nullReturn_returnsNull() {
|
|
|
73
|
+ when(sysControllerService.selectall()).thenReturn(null);
|
|
|
74
|
+
|
|
|
75
|
+ List<String> result = consumer.fetchTopics();
|
|
|
76
|
+
|
|
|
77
|
+ assertThat(result).isNull();
|
|
|
78
|
+ }
|
|
|
79
|
+
|
|
|
80
|
+ @Test
|
|
|
81
|
+ @DisplayName("insertredis: 应写入 Redis hash 和 active devices set")
|
|
|
82
|
+ void insertredis_validData_writesToRedis() throws Exception {
|
|
|
83
|
+ when(stringRedisTemplate.opsForHash()).thenReturn(hashOperations);
|
|
|
84
|
+ when(stringRedisTemplate.opsForSet()).thenReturn(setOperations);
|
|
|
85
|
+
|
|
|
86
|
+ SysController controller = new SysController();
|
|
|
87
|
+ controller.setName("temperature");
|
|
|
88
|
+ when(sysControllerService.selectcontrollerpath("ctrl1/temperature")).thenReturn(controller);
|
|
|
89
|
+
|
|
|
90
|
+ Map<String, Object> weather = new HashMap<>();
|
|
|
91
|
+ weather.put("timestamp", "1234567890");
|
|
|
92
|
+ weather.put("device_id", "dev001");
|
|
|
93
|
+ Map<String, Object> metricData = new HashMap<>();
|
|
|
94
|
+ metricData.put("val", "25.5");
|
|
|
95
|
+ weather.put("temperature", metricData);
|
|
|
96
|
+
|
|
|
97
|
+ consumer.insertredis(weather, "ctrl1/temperature");
|
|
|
98
|
+
|
|
|
99
|
+ verify(hashOperations).putAll(eq("DSB:ctrl1:temperature"), anyMap());
|
|
|
100
|
+ verify(stringRedisTemplate).expire("DSB:ctrl1:temperature", 2, TimeUnit.HOURS);
|
|
|
101
|
+ verify(setOperations).add("DSB:active:devices", "DSB:ctrl1:temperature");
|
|
|
102
|
+ verify(stringRedisTemplate).expire("DSB:active:devices", 2, TimeUnit.HOURS);
|
|
|
103
|
+ }
|
|
|
104
|
+
|
|
|
105
|
+ @Test
|
|
|
106
|
+ @DisplayName("insertredis: topic 格式无效时应直接返回")
|
|
|
107
|
+ void insertredis_invalidTopic_returnsSilently() throws Exception {
|
|
|
108
|
+ consumer.insertredis(new HashMap<>(), "invalidtopic");
|
|
|
109
|
+
|
|
|
110
|
+ verifyNoInteractions(stringRedisTemplate);
|
|
|
111
|
+ }
|
|
|
112
|
+
|
|
|
113
|
+ @Test
|
|
|
114
|
+ @DisplayName("insertredis: controller 未找到时应直接返回")
|
|
|
115
|
+ void insertredis_controllerNotFound_returnsSilently() throws Exception {
|
|
|
116
|
+ when(sysControllerService.selectcontrollerpath("ctrl1/missing")).thenReturn(null);
|
|
|
117
|
+
|
|
|
118
|
+ Map<String, Object> weather = new HashMap<>();
|
|
|
119
|
+ consumer.insertredis(weather, "ctrl1/missing");
|
|
|
120
|
+
|
|
|
121
|
+ verifyNoInteractions(stringRedisTemplate);
|
|
|
122
|
+ }
|
|
|
123
|
+
|
|
|
124
|
+ @Test
|
|
|
125
|
+ @DisplayName("insertredis: metricData 为 null 时应直接返回")
|
|
|
126
|
+ void insertredis_nullMetricData_returnsSilently() throws Exception {
|
|
|
127
|
+ SysController controller = new SysController();
|
|
|
128
|
+ controller.setName("temperature");
|
|
|
129
|
+ when(sysControllerService.selectcontrollerpath("ctrl1/temperature")).thenReturn(controller);
|
|
|
130
|
+
|
|
|
131
|
+ Map<String, Object> weather = new HashMap<>();
|
|
|
132
|
+ weather.put("temperature", null);
|
|
|
133
|
+
|
|
|
134
|
+ consumer.insertredis(weather, "ctrl1/temperature");
|
|
|
135
|
+
|
|
|
136
|
+ verifyNoInteractions(stringRedisTemplate);
|
|
|
137
|
+ }
|
|
|
138
|
+}
|