yeg
2026-08-27 210663040bf65ee00b5f86545fdc31bd0356fddf
ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TDetectionCabinetServiceImplTest.java
@@ -28,6 +28,11 @@
import com.ruoyi.system.domain.TDetectionCabinet;
import com.ruoyi.system.domain.vo.DiscoveredCabinetVo;
import com.ruoyi.system.mapper.TDetectionCabinetMapper;
import com.ruoyi.device.discovery.InMemoryDeviceDiscoveryCandidateStore;
import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.CabinetCandidate;
import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore.ChannelCandidate;
import com.ruoyi.device.model.ChannelIdentity;
import com.ruoyi.device.model.DeviceWorkStatus;
/**
 * 检测柜业务层单元测试,验证字段规范化、状态限制及唯一性校验等关键规则。
@@ -38,8 +43,8 @@
    @Mock
    private TDetectionCabinetMapper detectionCabinetMapper;
    @InjectMocks
    private TDetectionCabinetServiceImpl detectionCabinetService;
    private InMemoryDeviceDiscoveryCandidateStore candidateStore;
    private TDetectionCabinet detectionCabinet;
@@ -49,6 +54,9 @@
    @BeforeEach
    void setUp()
    {
        candidateStore = new InMemoryDeviceDiscoveryCandidateStore();
        detectionCabinetService = new TDetectionCabinetServiceImpl(detectionCabinetMapper, candidateStore);
        candidateStore.recordCabinets(List.of(candidate("CABINET-001", true, DeviceWorkStatus.WORKING)));
        detectionCabinet = new TDetectionCabinet();
        detectionCabinet.setCabinetCode(" CABINET-001 ");
        detectionCabinet.setCabinetName(" 一号检测柜 ");
@@ -67,19 +75,15 @@
    @Test
    void insertDetectionCabinetShouldPromoteDiscoveredCandidate()
    {
        TDetectionCabinet candidate = new TDetectionCabinet();
        candidate.setCabinetId(7L);
        candidate.setCabinetCode("CABINET-001");
        candidate.setMacAddress("AA:BB:CC:DD:EE:FF");
        candidate.setRegistrationStatus("DISCOVERED");
        when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(candidate);
        when(detectionCabinetMapper.selectByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(candidate);
        when(detectionCabinetMapper.registerDiscoveredCabinet(any(TDetectionCabinet.class))).thenReturn(1);
        when(detectionCabinetMapper.insertDetectionCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> {
            invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(7L);
            return 1;
        });
        int rows = detectionCabinetService.insertDetectionCabinet(detectionCabinet);
        ArgumentCaptor<TDetectionCabinet> captor = ArgumentCaptor.forClass(TDetectionCabinet.class);
        verify(detectionCabinetMapper).registerDiscoveredCabinet(captor.capture());
        verify(detectionCabinetMapper).insertDetectionCabinet(captor.capture());
        TDetectionCabinet savedCabinet = captor.getValue();
        assertEquals(1, rows);
        assertEquals(7L, savedCabinet.getCabinetId());
@@ -88,13 +92,13 @@
        assertEquals("192.168.1.10", savedCabinet.getIpAddress());
        assertEquals("AA:BB:CC:DD:EE:FF", savedCabinet.getMacAddress());
        assertEquals(new BigDecimal("120.50"), savedCabinet.getCapacity());
        assertNull(savedCabinet.getOnlineStatus());
        assertEquals("OFFLINE", savedCabinet.getOnlineStatus());
        assertEquals("0", savedCabinet.getStatus());
        assertNull(savedCabinet.getCurrentTest());
    }
    /**
     * 验证设备选择查询不读取旧检测柜测试字段,并改由持久化通道状态聚合。
     * 验证设备选择查询不再从 MySQL 读取发现候选。
     */
    @Test
    void discoveredCabinetSqlShouldNotReadLegacyTestColumns() throws IOException
@@ -104,17 +108,28 @@
        {
            assertNotNull(input);
            String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
            int selectStart = mapperXml.indexOf("<select id=\"selectDiscoveredCabinetList\"");
            int selectEnd = mapperXml.indexOf("</select>", selectStart);
            String selectSql = mapperXml.substring(selectStart, selectEnd);
            String activeChannelSql = sqlFragment(mapperXml, "activeCabinetChannelExists");
            assertFalse(mapperXml.contains("selectDiscoveredCabinetList"));
            assertFalse(mapperXml.contains("registration_status = 'DISCOVERED'"));
        }
    }
            assertFalse(selectSql.contains("cabinet.test_status"));
            assertFalse(selectSql.contains("cabinet.current_test"));
            assertTrue(selectSql.contains("activeCabinetChannelExists"));
            assertTrue(activeChannelSql.contains("t_detection_channel"));
            assertTrue(activeChannelSql.contains("work_status"));
            assertTrue(selectSql.contains("registration_status = 'DISCOVERED'"));
    /**
     * 验证删除正式检测柜只取消人工登记,不删除适配器持续维护的设备事实。
     */
    @Test
    void deleteDetectionCabinetSqlShouldReturnOnlineDeviceToDiscoveryPool() throws IOException
    {
        try (InputStream input = getClass().getResourceAsStream(
                "/mapper/system/TDetectionCabinetMapper.xml"))
        {
            assertNotNull(input);
            String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8);
            int deleteStart = mapperXml.indexOf("<update id=\"deleteDetectionCabinetById\"");
            int deleteEnd = mapperXml.indexOf("</update>", deleteStart);
            String deleteSql = mapperXml.substring(deleteStart, deleteEnd);
            assertTrue(deleteSql.contains("set del_flag = cabinet_id"));
            assertTrue(deleteSql.contains("registration_status = 'REGISTERED'"));
        }
    }
@@ -132,23 +147,24 @@
            int selectStart = mapperXml.indexOf("<sql id=\"selectDetectionCabinetVo\"");
            int selectEnd = mapperXml.indexOf("</sql>", selectStart);
            String selectSql = mapperXml.substring(selectStart, selectEnd);
            String activeChannelSql = sqlFragment(mapperXml, "activeCabinetChannelExists");
            assertFalse(selectSql.contains("online_status, test_status"));
            assertFalse(selectSql.contains("current_test, status"));
            assertTrue(selectSql.contains("activeCabinetChannelExists"));
            assertTrue(activeChannelSql.contains("t_detection_channel"));
            assertTrue(activeChannelSql.contains("work_status"));
            assertTrue(selectSql.contains("as test_status"));
            assertTrue(selectSql.contains("as current_test"));
            String activeTestSql = sqlFragment(mapperXml, "activeCabinetTestExists");
            assertTrue(activeTestSql.contains("t_test_channel_binding"));
            assertTrue(activeTestSql.contains("t_detection_channel"));
            assertTrue(activeTestSql.contains("test_fact.test_status"));
            assertFalse(activeTestSql.contains("t_test_battery"));
            assertFalse(activeTestSql.contains("battery_fact.cabinet_id"));
            int listStart = mapperXml.indexOf("<select id=\"selectDetectionCabinetList\"");
            int listEnd = mapperXml.indexOf("</select>", listStart);
            assertTrue(mapperXml.substring(listStart, listEnd).contains("registration_status = 'REGISTERED'"));
            int registerStart = mapperXml.indexOf("<update id=\"registerDiscoveredCabinet\"");
            int registerEnd = mapperXml.indexOf("</update>", registerStart);
            int registerStart = mapperXml.indexOf("<insert id=\"insertDetectionCabinet\"");
            int registerEnd = mapperXml.indexOf("</insert>", registerStart);
            String registerSql = mapperXml.substring(registerStart, registerEnd);
            assertTrue(registerSql.contains("registration_status = 'REGISTERED'"));
            assertTrue(registerSql.contains("create_by = #{createBy}"));
            assertTrue(registerSql.contains("'REGISTERED'"));
            assertTrue(registerSql.contains("#{createBy}"));
        }
    }
@@ -159,24 +175,25 @@
        return mapperXml.substring(start, end);
    }
    private CabinetCandidate candidate(String deviceCode, boolean online, DeviceWorkStatus workStatus)
    {
        java.time.Instant observedAt = java.time.Instant.now();
        List<ChannelCandidate> channels = java.util.stream.IntStream.rangeClosed(1, 8)
                .mapToObj(channelId -> new ChannelCandidate(
                        new ChannelIdentity("dev-sim", 22, 1, 1, channelId), online, workStatus, observedAt))
                .toList();
        return new CabinetCandidate("dev-sim", null, deviceCode, 22, 1, "192.168.1.10",
                "AA:BB:CC:DD:EE:FF", "一号模拟柜", "开发模拟器", "BTS Virtual Cabinet",
                channels, observedAt, observedAt.plusSeconds(60));
    }
    /**
     * 验证设备选择列表复用已持久化检测柜,并保持原前端字段和状态文本。
     * 验证设备选择列表读取 Redis 候选,并保持原前端字段和状态文本。
     */
    @Test
    void selectDiscoveredCabinetListShouldMapPersistedCabinetFacts()
    {
        TDetectionCabinet persisted = new TDetectionCabinet();
        persisted.setCabinetCode("CABINET-001");
        persisted.setIpAddress("192.168.1.10");
        persisted.setMacAddress("AA:BB:CC:DD:EE:FF");
        persisted.setCabinetName("一号模拟柜");
        persisted.setManufacturer("开发模拟器");
        persisted.setModel("BTS Virtual Cabinet");
        persisted.setChannelCount(6);
        persisted.setOnlineStatus("ONLINE");
        persisted.setObservedWorkStatus("ACTIVE");
        when(detectionCabinetMapper.selectDiscoveredCabinetList())
                .thenReturn(List.of(persisted));
        when(detectionCabinetMapper.selectRegisteredCabinetCodes()).thenReturn(List.of());
        List<DiscoveredCabinetVo> devices = detectionCabinetService.selectDiscoveredCabinetList();
@@ -190,7 +207,7 @@
        assertEquals("一号模拟柜", devices.get(0).getName());
        assertEquals("开发模拟器", devices.get(0).getManufacturer());
        assertEquals("BTS Virtual Cabinet", devices.get(0).getModel());
        assertEquals(6, devices.get(0).getChannelCount());
        assertEquals(8, devices.get(0).getChannelCount());
    }
    /**
@@ -208,34 +225,40 @@
                () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
        assertEquals("设备编码已存在", exception.getMessage());
        verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class));
        verify(detectionCabinetMapper, never()).insertDetectionCabinet(any(TDetectionCabinet.class));
    }
    @Test
    void insertDetectionCabinetShouldRequireDiscoveredCandidate()
    {
        when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(null);
        detectionCabinetService = new TDetectionCabinetServiceImpl(detectionCabinetMapper,
                new InMemoryDeviceDiscoveryCandidateStore());
        ServiceException exception = assertThrows(ServiceException.class,
                () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
        assertEquals("请先选择发现候选设备", exception.getMessage());
        verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any());
        assertEquals("请先选择在线的发现候选设备", exception.getMessage());
        verify(detectionCabinetMapper, never()).insertDetectionCabinet(any());
    }
    /**
     * 验证非法 IP 地址会阻止新增。
     */
    @Test
    void insertDetectionCabinetShouldRejectInvalidIpAddress()
    void insertDetectionCabinetShouldValidateAuthoritativeCandidateTopology()
    {
        detectionCabinet.setIpAddress("invalid-ip");
        detectionCabinet.setMacAddress("invalid-mac");
        detectionCabinet.setChannelCount(99);
        when(detectionCabinetMapper.insertDetectionCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> {
            invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(7L);
            return 1;
        });
        ServiceException exception = assertThrows(ServiceException.class,
                () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet));
        assertEquals(1, detectionCabinetService.insertDetectionCabinet(detectionCabinet));
        assertEquals("IP地址格式不正确", exception.getMessage());
        verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class));
        ArgumentCaptor<TDetectionCabinet> captor = ArgumentCaptor.forClass(TDetectionCabinet.class);
        verify(detectionCabinetMapper).insertDetectionCabinet(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());
    }
    /**