package com.ruoyi.system.service.impl; import java.io.IOException; import java.io.InputStream; import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.List; 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; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.system.domain.TDetectionCabinet; import com.ruoyi.system.domain.vo.DiscoveredCabinetVo; import com.ruoyi.system.mapper.TDetectionCabinetMapper; /** * 检测柜业务层单元测试,验证字段规范化、状态限制及唯一性校验等关键规则。 */ @ExtendWith(MockitoExtension.class) class TDetectionCabinetServiceImplTest { @Mock private TDetectionCabinetMapper detectionCabinetMapper; @InjectMocks private TDetectionCabinetServiceImpl detectionCabinetService; private TDetectionCabinet detectionCabinet; /** * 初始化一条满足新增校验的检测柜 Mock 数据。 */ @BeforeEach void setUp() { detectionCabinet = new TDetectionCabinet(); detectionCabinet.setCabinetCode(" CABINET-001 "); detectionCabinet.setCabinetName(" 一号检测柜 "); detectionCabinet.setIpAddress(" 192.168.1.10 "); detectionCabinet.setMacAddress("aa-bb-cc-dd-ee-ff"); detectionCabinet.setManufacturer(" 厂商 "); detectionCabinet.setModel(" 型号A "); detectionCabinet.setCapacity(new BigDecimal("120.50")); detectionCabinet.setChannelCount(8); detectionCabinet.setRemark(" 备注 "); } /** * 验证新增时会规范化基础字段并初始化运行状态。 */ @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); int rows = detectionCabinetService.insertDetectionCabinet(detectionCabinet); ArgumentCaptor captor = ArgumentCaptor.forClass(TDetectionCabinet.class); verify(detectionCabinetMapper).registerDiscoveredCabinet(captor.capture()); TDetectionCabinet savedCabinet = captor.getValue(); assertEquals(1, rows); assertEquals(7L, savedCabinet.getCabinetId()); assertEquals("CABINET-001", savedCabinet.getCabinetCode()); assertEquals("一号检测柜", savedCabinet.getCabinetName()); 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("0", savedCabinet.getStatus()); assertNull(savedCabinet.getCurrentTest()); } /** * 验证设备选择查询不读取旧检测柜测试字段,并改由持久化通道状态聚合。 */ @Test void discoveredCabinetSqlShouldNotReadLegacyTestColumns() throws IOException { try (InputStream input = getClass().getResourceAsStream( "/mapper/system/TDetectionCabinetMapper.xml")) { assertNotNull(input); String mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8); int selectStart = mapperXml.indexOf("", listStart); assertTrue(mapperXml.substring(listStart, listEnd).contains("registration_status = 'REGISTERED'")); int registerStart = mapperXml.indexOf("", registerStart); String registerSql = mapperXml.substring(registerStart, registerEnd); assertTrue(registerSql.contains("registration_status = 'REGISTERED'")); assertTrue(registerSql.contains("create_by = #{createBy}")); } } private String sqlFragment(String mapperXml, String id) { int start = mapperXml.indexOf("", start); return mapperXml.substring(start, end); } /** * 验证设备选择列表复用已持久化检测柜,并保持原前端字段和状态文本。 */ @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)); List devices = detectionCabinetService.selectDiscoveredCabinetList(); assertEquals(1, devices.size()); assertEquals("CABINET-001", devices.get(0).getDeviceCode()); assertEquals("192.168.1.10", devices.get(0).getIp()); assertEquals("AA:BB:CC:DD:EE:FF", devices.get(0).getMac()); assertEquals("在线", devices.get(0).getOnlineStatus()); assertEquals("测试中", devices.get(0).getTestStatus()); assertEquals("待确认", devices.get(0).getCurrentTest()); assertEquals("一号模拟柜", devices.get(0).getName()); assertEquals("开发模拟器", devices.get(0).getManufacturer()); assertEquals("BTS Virtual Cabinet", devices.get(0).getModel()); assertEquals(6, devices.get(0).getChannelCount()); } /** * 验证重复设备编码会阻止新增且不会执行写库操作。 */ @Test void insertDetectionCabinetShouldRejectDuplicateCabinetCode() { TDetectionCabinet existing = new TDetectionCabinet(); existing.setCabinetId(2L); existing.setRegistrationStatus("REGISTERED"); when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(existing); ServiceException exception = assertThrows(ServiceException.class, () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet)); assertEquals("设备编码已存在", exception.getMessage()); verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class)); } @Test void insertDetectionCabinetShouldRequireDiscoveredCandidate() { when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(null); ServiceException exception = assertThrows(ServiceException.class, () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet)); assertEquals("请先选择发现候选设备", exception.getMessage()); verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any()); } /** * 验证非法 IP 地址会阻止新增。 */ @Test void insertDetectionCabinetShouldRejectInvalidIpAddress() { detectionCabinet.setIpAddress("invalid-ip"); ServiceException exception = assertThrows(ServiceException.class, () -> detectionCabinetService.insertDetectionCabinet(detectionCabinet)); assertEquals("IP地址格式不正确", exception.getMessage()); verify(detectionCabinetMapper, never()).registerDiscoveredCabinet(any(TDetectionCabinet.class)); } /** * 验证测试中的检测柜不能被禁用。 */ @Test void updateDetectionCabinetStatusShouldRejectDisablingTestingCabinet() { TDetectionCabinet current = new TDetectionCabinet(); current.setCabinetId(1L); current.setStatus("0"); current.setTestStatus("1"); when(detectionCabinetMapper.selectDetectionCabinetById(1L)).thenReturn(current); TDetectionCabinet statusCommand = new TDetectionCabinet(); statusCommand.setCabinetId(1L); statusCommand.setStatus("1"); ServiceException exception = assertThrows(ServiceException.class, () -> detectionCabinetService.updateDetectionCabinetStatus(statusCommand)); assertEquals("检测柜正在测试,不能禁用", exception.getMessage()); verify(detectionCabinetMapper, never()).updateDetectionCabinetStatus(any(TDetectionCabinet.class)); } /** * 验证检测中的检测柜允许执行启用操作。 */ @Test void updateDetectionCabinetStatusShouldAllowEnablingTestingCabinet() { TDetectionCabinet current = new TDetectionCabinet(); current.setCabinetId(1L); current.setStatus("1"); current.setTestStatus("1"); when(detectionCabinetMapper.selectDetectionCabinetById(1L)).thenReturn(current); when(detectionCabinetMapper.updateDetectionCabinetStatus(any(TDetectionCabinet.class))).thenReturn(1); TDetectionCabinet statusCommand = new TDetectionCabinet(); statusCommand.setCabinetId(1L); statusCommand.setStatus("0"); int rows = detectionCabinetService.updateDetectionCabinetStatus(statusCommand); assertEquals(1, rows); verify(detectionCabinetMapper).updateDetectionCabinetStatus(statusCommand); } /** * 验证重复提交相同状态时按幂等成功处理,不重复写库。 */ @Test void updateDetectionCabinetStatusShouldBeIdempotent() { TDetectionCabinet current = new TDetectionCabinet(); current.setCabinetId(1L); current.setStatus("0"); current.setTestStatus("0"); when(detectionCabinetMapper.selectDetectionCabinetById(1L)).thenReturn(current); TDetectionCabinet statusCommand = new TDetectionCabinet(); statusCommand.setCabinetId(1L); statusCommand.setStatus("0"); int rows = detectionCabinetService.updateDetectionCabinetStatus(statusCommand); assertEquals(1, rows); verify(detectionCabinetMapper, never()).updateDetectionCabinetStatus(any(TDetectionCabinet.class)); } /** * 验证批量删除会先检查所有检测柜,存在测试中记录时不删除任何数据。 */ @Test void deleteDetectionCabinetByIdsShouldRejectTestingCabinetBeforeDeleting() { TDetectionCabinet normal = new TDetectionCabinet(); normal.setCabinetId(1L); normal.setTestStatus("0"); TDetectionCabinet testing = new TDetectionCabinet(); testing.setCabinetId(2L); testing.setTestStatus("1"); when(detectionCabinetMapper.selectDetectionCabinetById(1L)).thenReturn(normal); when(detectionCabinetMapper.selectDetectionCabinetById(2L)).thenReturn(testing); ServiceException exception = assertThrows(ServiceException.class, () -> detectionCabinetService.deleteDetectionCabinetByIds(new Long[] { 1L, 2L }, "admin")); assertEquals("检测柜正在测试,不能删除", exception.getMessage()); verify(detectionCabinetMapper, never()).deleteDetectionCabinetById(any(), any()); } /** * 验证编辑时唯一性查询命中当前记录仍视为唯一。 */ @Test void checkUniqueShouldAllowCurrentCabinet() { TDetectionCabinet current = new TDetectionCabinet(); current.setCabinetId(1L); current.setCabinetCode("CABINET-001"); current.setMacAddress("AA:BB:CC:DD:EE:FF"); when(detectionCabinetMapper.selectByCabinetCode("CABINET-001")).thenReturn(current); when(detectionCabinetMapper.selectByMacAddress("AA:BB:CC:DD:EE:FF")).thenReturn(current); assertTrue(detectionCabinetService.checkCabinetCodeUnique(current)); assertTrue(detectionCabinetService.checkMacAddressUnique(current)); } }