From e38aca8134570513435c003c25dc05f1ae51c9c9 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期四, 08 十月 2026 13:56:28 +0800
Subject: [PATCH] 实现 Ticket04:采集器辅助测量与登记入口,拆除开发模拟器
---
ruoyi-admin/src/test/java/com/battery/business/service/impl/TCollectorServiceImplTest.java | 172 +++++++++++++++++++++++++++++++++++++++------------------
1 files changed, 118 insertions(+), 54 deletions(-)
diff --git a/ruoyi-admin/src/test/java/com/battery/business/service/impl/TCollectorServiceImplTest.java b/ruoyi-admin/src/test/java/com/battery/business/service/impl/TCollectorServiceImplTest.java
index 3aef6e8..f132f1f 100644
--- a/ruoyi-admin/src/test/java/com/battery/business/service/impl/TCollectorServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/battery/business/service/impl/TCollectorServiceImplTest.java
@@ -3,6 +3,7 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -24,12 +25,15 @@
import org.mockito.junit.jupiter.MockitoExtension;
import com.ruoyi.common.exception.ServiceException;
import com.battery.business.domain.TCollector;
+import com.battery.business.domain.TDeviceAdapter;
import com.battery.business.mapper.TCollectorMapper;
-import com.battery.device.api.discovery.InMemoryDeviceDiscoveryCandidateStore;
-import com.battery.device.api.discovery.DeviceDiscoveryCandidateStore.CollectorCandidate;
+import com.battery.business.mapper.TDeviceAdapterMapper;
/**
* 采集器 Service 单元测试。
+ *
+ * <p>采集器按 ADR-0064 不建独立设备模型:登记不再依赖发现候选(真实来源不产出采集器候选),
+ * 由操作员手工登记来源身份,在线状态取所链接来源实例的连接结果。
*/
@ExtendWith(MockitoExtension.class)
class TCollectorServiceImplTest
@@ -37,20 +41,17 @@
@Mock
private TCollectorMapper collectorMapper;
+ @Mock
+ private TDeviceAdapterMapper adapterMapper;
+
private TCollectorServiceImpl collectorService;
- private InMemoryDeviceDiscoveryCandidateStore candidateStore;
private TCollector collector;
@BeforeEach
void setUp()
{
- candidateStore = new InMemoryDeviceDiscoveryCandidateStore();
- collectorService = new TCollectorServiceImpl(collectorMapper, candidateStore);
- java.time.Instant now = java.time.Instant.now();
- candidateStore.replaceCollectors("dev-sim", java.util.List.of(new CollectorCandidate(
- "dev-sim", "COL-001", "192.168.1.10", "AA:BB:CC:DD:EE:FF", "一号采集器",
- "开发模拟器", "Virtual Collector", 8, true, now, now.plusSeconds(60))));
+ collectorService = new TCollectorServiceImpl(collectorMapper, adapterMapper);
collector = new TCollector();
collector.setCollectorId(1L);
collector.setCollectorCode("COL-001");
@@ -80,21 +81,52 @@
}
}
+ /** 在线事实只有一个来源:所链接来源实例的连接结果;登记表里的存储列不再充当这个事实。 */
@Test
- void registeredCollectorCodeSqlShouldIgnoreDeletedRows() throws IOException
+ void onlineStatusSqlShouldDeriveFromLinkedAdapter() throws IOException
{
try (InputStream input = getClass().getResourceAsStream(
"/mapper/battery/TCollectorMapper.xml"))
{
assertNotNull(input);
String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
- int selectStart = mapperXml.indexOf("<select id=\"selectRegisteredCollectorCodes\"");
- assertTrue(selectStart >= 0);
- int selectEnd = mapperXml.indexOf("</select>", selectStart);
- String selectSql = mapperXml.substring(selectStart, selectEnd);
-
- assertTrue(selectSql.contains("where del_flag = '0'"));
+ int fragmentStart = mapperXml.indexOf("<sql id=\"derivedOnlineStatus\"");
+ assertTrue(fragmentStart >= 0, "在线状态的派生表达式应当是一个共用片段");
+ String fragment = mapperXml.substring(fragmentStart, mapperXml.indexOf("</sql>", fragmentStart));
+ assertTrue(fragment.contains("from t_device_adapter adapter_fact"), fragment);
+ // 存储列不再参与:只有派生的那个表达式。
+ assertFalse(mapperXml.contains("upper(c.online_status)"));
+ // SELECT 与 WHERE 各用一次(WHERE 里不能用别名,所以走同一个片段)。
+ assertEquals(2, mapperXml.split("<include refid=\"derivedOnlineStatus\" />", -1).length - 1);
}
+ }
+
+ /** 登记不再要求发现候选:手工填写的来源身份即可落库。 */
+ @Test
+ void insertCollectorShouldNotRequireADiscoveryCandidate()
+ {
+ collector.setCollectorId(null);
+ when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
+
+ assertEquals(1, collectorService.insertCollector(collector));
+
+ verify(collectorMapper).insertCollector(any(TCollector.class));
+ }
+
+ @Test
+ void insertCollectorShouldAcceptCollectorWithoutIpAndMac()
+ {
+ collector.setCollectorId(null);
+ collector.setIpAddress(null);
+ collector.setMacAddress(null);
+ when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
+
+ assertEquals(1, collectorService.insertCollector(collector));
+
+ ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
+ verify(collectorMapper).insertCollector(captor.capture());
+ assertNull(captor.getValue().getIpAddress());
+ assertNull(captor.getValue().getMacAddress());
}
@Test
@@ -117,6 +149,55 @@
assertEquals("0", saved.getTestStatus());
assertEquals("0", saved.getStatus());
assertEquals("0", saved.getDelFlag());
+ }
+
+ @Test
+ void insertCollectorShouldRejectMalformedIpAndMacInsteadOfGuessing()
+ {
+ collector.setCollectorId(null);
+ collector.setIpAddress("invalid-host");
+ collector.setMacAddress("AA:BB:CC:DD:EE:FF");
+
+ ServiceException ip = assertThrows(ServiceException.class,
+ () -> collectorService.insertCollector(collector));
+ assertEquals("IP地址格式不正确", ip.getMessage());
+
+ collector.setIpAddress("192.168.1.10");
+ collector.setMacAddress("invalid-mac");
+ ServiceException mac = assertThrows(ServiceException.class,
+ () -> collectorService.insertCollector(collector));
+ assertEquals("MAC地址格式不正确", mac.getMessage());
+ verify(collectorMapper, never()).insertCollector(any(TCollector.class));
+ }
+
+ @Test
+ void insertCollectorShouldRejectUnknownLinkedSource()
+ {
+ collector.setCollectorId(null);
+ collector.setAdapterId(9L);
+ when(adapterMapper.selectDeviceAdapterById(9L)).thenReturn(null);
+
+ ServiceException exception = assertThrows(ServiceException.class,
+ () -> collectorService.insertCollector(collector));
+
+ assertEquals("所链接的数据来源不存在", exception.getMessage());
+ verify(collectorMapper, never()).insertCollector(any(TCollector.class));
+ }
+
+ /** 链接到来源实例后,登记保存该链接:采集器的辅助测量由它提供。 */
+ @Test
+ void insertCollectorShouldPersistLinkedSource()
+ {
+ collector.setCollectorId(null);
+ collector.setAdapterId(3L);
+ when(adapterMapper.selectDeviceAdapterById(3L)).thenReturn(new TDeviceAdapter());
+ when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
+
+ assertEquals(1, collectorService.insertCollector(collector));
+
+ ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
+ verify(collectorMapper).insertCollector(captor.capture());
+ assertEquals(Long.valueOf(3L), captor.getValue().getAdapterId());
}
@Test
@@ -148,36 +229,6 @@
}
@Test
- void insertCollectorShouldValidateAuthoritativeCandidateTopology()
- {
- collector.setCollectorId(null);
- collector.setIpAddress("invalid-host");
- collector.setMacAddress("invalid-mac");
- collector.setChannelCount(99);
- when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
-
- assertEquals(1, collectorService.insertCollector(collector));
-
- ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
- verify(collectorMapper).insertCollector(captor.capture());
- assertEquals("192.168.1.10", captor.getValue().getIpAddress());
- assertEquals("AA:BB:CC:DD:EE:FF", captor.getValue().getMacAddress());
- assertEquals(8, captor.getValue().getChannelCount());
- }
-
- @Test
- void offlineCollectorCanBeRegisteredWithoutChangingOnlineFact()
- {
- java.time.Instant now = java.time.Instant.now();
- candidateStore.replaceCollectors("dev-sim", java.util.List.of(new CollectorCandidate(
- "dev-sim", "COL-001", "192.168.1.10", "AA:BB:CC:DD:EE:FF", "一号采集器",
- "开发模拟器", "Virtual Collector", 8, false, now, now.plusSeconds(60))));
- when(collectorMapper.insertCollector(any(TCollector.class))).thenReturn(1);
- assertEquals(1, collectorService.insertCollector(collector));
- verify(collectorMapper).updateRegisteredObservation("COL-001", "OFFLINE");
- }
-
- @Test
void insertCollectorShouldRejectDuplicateCode()
{
collector.setCollectorId(null);
@@ -203,6 +254,27 @@
assertEquals(1, collectorService.updateCollector(collector));
verify(collectorMapper).updateCollector(collector);
+ }
+
+ /** 编辑只提交可改字段:未提交的 IP/MAC 不能被当成"清空"。 */
+ @Test
+ void updateCollectorShouldKeepUnsubmittedIpAndMac()
+ {
+ when(collectorMapper.selectCollectorById(1L)).thenReturn(collector);
+ when(collectorMapper.selectCollectorByCode("COL-001")).thenReturn(collector);
+ when(collectorMapper.selectCollectorByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(collector);
+ when(collectorMapper.updateCollector(any(TCollector.class))).thenReturn(1);
+ TCollector edit = new TCollector();
+ edit.setCollectorId(1L);
+ edit.setCollectorName("改名后");
+
+ assertEquals(1, collectorService.updateCollector(edit));
+
+ ArgumentCaptor<TCollector> captor = ArgumentCaptor.forClass(TCollector.class);
+ verify(collectorMapper).updateCollector(captor.capture());
+ assertEquals("192.168.1.10", captor.getValue().getIpAddress());
+ assertEquals("AA:BB:CC:DD:EE:FF", captor.getValue().getMacAddress());
+ assertEquals("改名后", captor.getValue().getCollectorName());
}
@Test
@@ -256,14 +328,6 @@
assertEquals(1, collectorService.updateCollectorStatus(command));
verify(collectorMapper, never()).updateCollectorStatus(any(TCollector.class));
- }
-
- @Test
- void selectRegisteredCollectorCodesShouldReturnUndeletedCodes()
- {
- when(collectorMapper.selectRegisteredCollectorCodes()).thenReturn(java.util.List.of("COL-001"));
-
- assertEquals(java.util.Set.of("COL-001"), collectorService.selectRegisteredCollectorCodes());
}
@Test
--
Gitblit v1.7.1