package com.ruoyi.system.service.impl; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.device.api.DeviceAdapter; import com.ruoyi.device.bts.BtsTransportException; import com.ruoyi.device.model.ChannelIdentity; import com.ruoyi.device.model.DetectedChannel; import com.ruoyi.device.registry.DeviceAdapterRegistry; import com.ruoyi.system.domain.TDetectionCabinet; import com.ruoyi.system.domain.TDetectionChannel; import com.ruoyi.system.domain.TDeviceAdapter; import com.ruoyi.system.mapper.TDeviceAdapterMapper; import com.ruoyi.system.service.DeviceAdapterErrorCodes; @ExtendWith(MockitoExtension.class) class DeviceAdapterServiceImplTest { @Mock private TDeviceAdapterMapper adapterMapper; @Mock private DeviceAdapterRegistry registry; @Mock private DeviceAdapter deviceAdapter; private DeviceAdapterServiceImpl service; private TDeviceAdapter adapter; @BeforeEach void setUp() { service = new DeviceAdapterServiceImpl(adapterMapper, registry); adapter = new TDeviceAdapter(); adapter.setAdapterId(7L); adapter.setAdapterCode("lab-bts"); adapter.setAdapterName("一号 BTS"); adapter.setAdapterType("BTS"); adapter.setHost("127.0.0.1"); adapter.setPort(502); adapter.setPollIntervalMs(5000); adapter.setTimezoneId("Asia/Hong_Kong"); adapter.setReadOnly(true); adapter.setStatus("0"); } @Test void enablingAdapterSynchronizesRuntimeWithoutConstructingVendorClient() { adapter.setStatus("1"); when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); when(adapterMapper.updateDeviceAdapterStatus(7L, "0", "admin")).thenReturn(1); int rows = service.changeStatus(7L, true, "admin"); assertEquals(1, rows); verify(registry).synchronize(any()); } @Test void repeatedDiscoveryUpsertsStableChannelIdentityWithoutDeletingMissingChannels() { when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); when(registry.require("lab-bts")).thenReturn(deviceAdapter); when(deviceAdapter.discoverChannels()).thenReturn(List.of( new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 2, 3), "192.168.1.35", true), new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 2, 4), "192.168.1.35", false))); when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L); assertEquals(2, service.discover(7L).channelCount()); assertEquals(2, service.discover(7L).channelCount()); verify(adapterMapper, times(4)).upsertDetectedChannel(any(TDetectionChannel.class)); verify(adapterMapper, never()).deleteMissingChannels(any(), any()); verify(adapterMapper, times(2)).updateDiscoverySuccess(eq(7L), any()); verify(adapterMapper, times(2)).updateDetectedCabinet(any(TDetectionCabinet.class)); } @Test void connectionFailureHasStableCodeAndLaterDiscoveryClearsError() { when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); when(registry.require("lab-bts")).thenReturn(deviceAdapter); when(deviceAdapter.discoverChannels()) .thenThrow(new BtsTransportException(BtsTransportException.Reason.CONNECTION_FAILED, "connection refused", null)) .thenReturn(List.of()); ServiceException failure = assertThrows(ServiceException.class, () -> service.discover(7L)); assertEquals(DeviceAdapterErrorCodes.DISCOVERY_FAILED, failure.getCode()); verify(adapterMapper).updateDiscoveryFailure(7L, "CONNECTION_FAILED", "connection refused"); assertEquals(0, service.discover(7L).channelCount()); verify(adapterMapper).updateDiscoverySuccess(eq(7L), any()); } }