| | |
| | | package com.ruoyi.system.service.impl; |
| | | |
| | | import static org.junit.jupiter.api.Assertions.assertEquals; |
| | | import static org.junit.jupiter.api.Assertions.assertNotNull; |
| | | 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.ArgumentMatchers.eq; |
| | | import static org.mockito.Mockito.times; |
| | | import static org.mockito.Mockito.never; |
| | | import static org.mockito.Mockito.verify; |
| | | import static org.mockito.Mockito.when; |
| | | import java.io.InputStream; |
| | | import java.math.BigDecimal; |
| | | import java.nio.charset.StandardCharsets; |
| | | import java.time.Instant; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | |
| | | import org.junit.jupiter.api.extension.ExtendWith; |
| | | import org.mockito.Mock; |
| | | import org.mockito.junit.jupiter.MockitoExtension; |
| | | import org.springframework.context.annotation.AnnotationConfigApplicationContext; |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.device.api.DeviceAdapter; |
| | | import com.ruoyi.device.api.DeviceAdapterException; |
| | | import com.ruoyi.device.model.ChannelIdentity; |
| | | import com.ruoyi.device.model.DetectedChannel; |
| | | import com.ruoyi.device.model.DetectedDeviceMetadata; |
| | | import com.ruoyi.device.model.DeviceWorkStatus; |
| | | import com.ruoyi.device.model.RealtimeSnapshot; |
| | | import com.ruoyi.device.registry.DeviceAdapterRegistry; |
| | |
| | | @BeforeEach |
| | | void setUp() |
| | | { |
| | | service = new DeviceAdapterServiceImpl(adapterMapper, registry); |
| | | service = new DeviceAdapterServiceImpl(adapterMapper, registry, |
| | | new DeviceDiscoveryPersistenceService(adapterMapper)); |
| | | adapter = new TDeviceAdapter(); |
| | | adapter.setAdapterId(7L); |
| | | adapter.setAdapterCode("lab-bts"); |
| | |
| | | adapter.setPollIntervalMs(5000); |
| | | adapter.setTimezoneId("Asia/Hong_Kong"); |
| | | adapter.setReadOnly(true); |
| | | adapter.setCapabilities("{\"discovery\":true,\"realtimeObservation\":true,\"historyDownload\":true,\"control\":false}"); |
| | | adapter.setStatus("0"); |
| | | } |
| | | |
| | |
| | | |
| | | assertEquals(1, rows); |
| | | verify(registry).synchronize(any()); |
| | | } |
| | | |
| | | @Test |
| | | void springSelectsProductionConstructor() |
| | | { |
| | | try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) |
| | | { |
| | | context.registerBean(TDeviceAdapterMapper.class, () -> org.mockito.Mockito.mock(TDeviceAdapterMapper.class)); |
| | | context.registerBean(DeviceAdapterRegistry.class, () -> org.mockito.Mockito.mock(DeviceAdapterRegistry.class)); |
| | | context.registerBean(DeviceDiscoveryPersistenceService.class, |
| | | () -> org.mockito.Mockito.mock(DeviceDiscoveryPersistenceService.class)); |
| | | context.register(DeviceAdapterServiceImpl.class); |
| | | |
| | | context.refresh(); |
| | | |
| | | assertNotNull(context.getBean(DeviceAdapterServiceImpl.class)); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | |
| | | verify(adapterMapper, times(4)).upsertDetectedChannel(any(TDetectionChannel.class)); |
| | | verify(adapterMapper, times(2)).updateDiscoverySuccess(eq(7L), any()); |
| | | verify(adapterMapper, times(2)).updateDetectedCabinet(any(TDetectionCabinet.class)); |
| | | } |
| | | |
| | | @Test |
| | | void discoveryPersistsOptionalDeviceMetadataFromAdapterBoundary() |
| | | { |
| | | ChannelIdentity identity = new ChannelIdentity("lab-bts", 22, 8, 1, 1); |
| | | when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); |
| | | when(deviceAdapter.discoverChannels()).thenReturn(List.of(new DetectedChannel(identity, "127.0.0.8", true, |
| | | new DetectedDeviceMetadata("开发设备 8", "开发模拟器", "BTS Virtual Cabinet", |
| | | "02:44:00:16:00:08")))); |
| | | when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 8)).thenReturn(null); |
| | | when(adapterMapper.insertDetectedCabinet(any(TDetectionCabinet.class))).thenAnswer(invocation -> { |
| | | invocation.getArgument(0, TDetectionCabinet.class).setCabinetId(18L); |
| | | return 1; |
| | | }); |
| | | stubRegistryExecution(); |
| | | |
| | | service.discover(7L); |
| | | |
| | | var cabinet = org.mockito.ArgumentCaptor.forClass(TDetectionCabinet.class); |
| | | verify(adapterMapper).insertDetectedCabinet(cabinet.capture()); |
| | | assertEquals("开发设备 8", cabinet.getValue().getCabinetName()); |
| | | assertEquals("开发模拟器", cabinet.getValue().getManufacturer()); |
| | | assertEquals("BTS Virtual Cabinet", cabinet.getValue().getModel()); |
| | | assertEquals("02:44:00:16:00:08", cabinet.getValue().getMacAddress()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | } |
| | | |
| | | @Test |
| | | void unavailableDevicePersistsExplicitOfflineFactsWithoutRealtimeInquiry() |
| | | { |
| | | when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); |
| | | when(deviceAdapter.discoverChannels()).thenReturn(List.of( |
| | | new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 1, 1), "127.0.0.1", false), |
| | | new DetectedChannel(new ChannelIdentity("lab-bts", 22, 1, 1, 2), "127.0.0.1", false))); |
| | | when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L); |
| | | stubRegistryExecution(); |
| | | |
| | | service.discover(7L); |
| | | |
| | | var cabinet = org.mockito.ArgumentCaptor.forClass(TDetectionCabinet.class); |
| | | var channels = org.mockito.ArgumentCaptor.forClass(TDetectionChannel.class); |
| | | verify(adapterMapper).updateDetectedCabinet(cabinet.capture()); |
| | | verify(adapterMapper, times(2)).upsertDetectedChannel(channels.capture()); |
| | | verify(deviceAdapter, never()).inquire(any()); |
| | | assertEquals("OFFLINE", cabinet.getValue().getOnlineStatus()); |
| | | assertTrue(channels.getAllValues().stream() |
| | | .allMatch(channel -> "OFFLINE".equals(channel.getOnlineStatus()))); |
| | | } |
| | | |
| | | @Test |
| | | void connectionFailureHasStableCodeAndLaterDiscoveryClearsError() |
| | | { |
| | | when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); |
| | |
| | | verify(adapterMapper).updateDiscoverySuccess(eq(7L), any()); |
| | | } |
| | | |
| | | @Test |
| | | void ensuresManagedAdapterFromApplicationProvidedConfiguration() |
| | | { |
| | | when(adapterMapper.selectDeviceAdapterByCode("lab-bts")).thenReturn(adapter); |
| | | |
| | | TDeviceAdapter registered = service.ensureManagedAdapter(adapter); |
| | | |
| | | verify(adapterMapper).upsertManagedDeviceAdapter(adapter); |
| | | assertEquals(adapter, registered); |
| | | } |
| | | |
| | | @Test |
| | | void developmentSimulatorProvisioningIsIdempotentAndReadOnly() |
| | | { |
| | | String mapperXml; |
| | | try (InputStream input = getClass().getResourceAsStream("/mapper/system/TDeviceAdapterMapper.xml")) |
| | | { |
| | | if (input == null) throw new AssertionError("缺少设备适配器 Mapper"); |
| | | mapperXml = new String(input.readAllBytes(), StandardCharsets.UTF_8); |
| | | } |
| | | catch (Exception exception) |
| | | { |
| | | throw new AssertionError("无法读取设备适配器 Mapper", exception); |
| | | } |
| | | |
| | | assertTrue(mapperXml.contains("upsertManagedDeviceAdapter")); |
| | | assertTrue(mapperXml.contains("#{adapterCode}")); |
| | | assertTrue(mapperXml.contains("#{capabilities}")); |
| | | assertTrue(mapperXml.contains("on duplicate key update")); |
| | | assertTrue(mapperXml.contains("del_flag=0")); |
| | | } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | private void stubRegistryExecution() |
| | | { |