| | |
| | | import com.ruoyi.common.exception.ServiceException; |
| | | import com.ruoyi.device.api.DeviceAdapter; |
| | | import com.ruoyi.device.api.DeviceAdapterException; |
| | | import com.ruoyi.device.discovery.InMemoryDeviceDiscoveryCandidateStore; |
| | | import com.ruoyi.device.discovery.DeviceDiscoveryCandidateStore; |
| | | import com.ruoyi.device.model.ChannelIdentity; |
| | | import com.ruoyi.device.model.DetectedChannel; |
| | | import com.ruoyi.device.model.DetectedDeviceMetadata; |
| | |
| | | import com.ruoyi.system.domain.TDetectionChannel; |
| | | import com.ruoyi.system.domain.TDeviceAdapter; |
| | | import com.ruoyi.system.mapper.TDeviceAdapterMapper; |
| | | import com.ruoyi.system.mapper.TDetectionCabinetMapper; |
| | | import com.ruoyi.system.service.DeviceAdapterErrorCodes; |
| | | |
| | | @ExtendWith(MockitoExtension.class) |
| | |
| | | @Mock |
| | | private DeviceAdapter deviceAdapter; |
| | | |
| | | @Mock |
| | | private TDetectionCabinetMapper detectionCabinetMapper; |
| | | |
| | | private DeviceAdapterServiceImpl service; |
| | | private TDeviceAdapter adapter; |
| | | private InMemoryDeviceDiscoveryCandidateStore candidateStore; |
| | | |
| | | @BeforeEach |
| | | void setUp() |
| | | { |
| | | candidateStore = new InMemoryDeviceDiscoveryCandidateStore(); |
| | | service = new DeviceAdapterServiceImpl(adapterMapper, registry, |
| | | new DeviceDiscoveryPersistenceService(adapterMapper)); |
| | | new DeviceDiscoveryPersistenceService(candidateStore, detectionCabinetMapper), candidateStore); |
| | | adapter = new TDeviceAdapter(); |
| | | adapter.setAdapterId(7L); |
| | | adapter.setAdapterCode("lab-bts"); |
| | |
| | | context.registerBean(DeviceAdapterRegistry.class, () -> org.mockito.Mockito.mock(DeviceAdapterRegistry.class)); |
| | | context.registerBean(DeviceDiscoveryPersistenceService.class, |
| | | () -> org.mockito.Mockito.mock(DeviceDiscoveryPersistenceService.class)); |
| | | context.registerBean(DeviceDiscoveryCandidateStore.class, |
| | | () -> org.mockito.Mockito.mock(DeviceDiscoveryCandidateStore.class)); |
| | | context.register(DeviceAdapterServiceImpl.class); |
| | | |
| | | context.refresh(); |
| | |
| | | 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))); |
| | | stubRegistryExecution(); |
| | | 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)); |
| | | var snapshot = candidateStore.snapshot(Instant.now()); |
| | | assertEquals(1, snapshot.cabinets().size()); |
| | | assertEquals(2, snapshot.cabinets().get(0).channels().size()); |
| | | verify(adapterMapper, times(2)).updateDiscoverySuccess(eq(7L), any()); |
| | | verify(adapterMapper, times(2)).updateDetectedCabinet(any(TDetectionCabinet.class)); |
| | | } |
| | | |
| | | @Test |
| | |
| | | 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()); |
| | | var cabinet = candidateStore.snapshot(Instant.now()).cabinets().get(0); |
| | | assertEquals("开发设备 8", cabinet.name()); |
| | | assertEquals("开发模拟器", cabinet.manufacturer()); |
| | | assertEquals("BTS Virtual Cabinet", cabinet.model()); |
| | | assertEquals("02:44:00:16:00:08", cabinet.mac()); |
| | | } |
| | | |
| | | @Test |
| | |
| | | new RealtimeSnapshot(identity, DeviceWorkStatus.WORKING, 1, 2, "cc", null, |
| | | BigDecimal.ONE, BigDecimal.TEN, null, null, 3L, 2L, null, |
| | | Instant.parse("2026-08-26T01:02:03Z"), Map.of()))); |
| | | when(adapterMapper.selectCabinetIdByIdentity(7L, 22, 1)).thenReturn(10L); |
| | | stubRegistryExecution(); |
| | | |
| | | service.discover(7L); |
| | | |
| | | var channel = org.mockito.ArgumentCaptor.forClass(TDetectionChannel.class); |
| | | verify(adapterMapper).upsertDetectedChannel(channel.capture()); |
| | | assertEquals("WORKING", channel.getValue().getWorkStatus()); |
| | | var channel = candidateStore.snapshot(Instant.now()).cabinets().get(0).channels().get(0); |
| | | assertEquals(DeviceWorkStatus.WORKING, channel.workStatus()); |
| | | verify(detectionCabinetMapper).updateRegisteredChannelObservation(eq("lab-bts"), eq(22), eq(1), eq(2), |
| | | eq(3), eq("ONLINE"), eq("WORKING"), any(), eq(true)); |
| | | verify(detectionCabinetMapper).updateRegisteredCabinetObservation(eq("lab-bts"), eq(22), eq(1), |
| | | eq("ONLINE"), any(), eq(true)); |
| | | } |
| | | |
| | | @Test |
| | |
| | | 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()); |
| | | var cabinet = candidateStore.snapshot(Instant.now()).cabinets().get(0); |
| | | verify(deviceAdapter, never()).inquire(any()); |
| | | assertEquals("OFFLINE", cabinet.getValue().getOnlineStatus()); |
| | | assertTrue(channels.getAllValues().stream() |
| | | .allMatch(channel -> "OFFLINE".equals(channel.getOnlineStatus()))); |
| | | assertEquals(false, cabinet.online()); |
| | | assertTrue(cabinet.channels().stream().noneMatch(channel -> channel.online())); |
| | | verify(detectionCabinetMapper).updateRegisteredChannelObservation(eq("lab-bts"), eq(22), eq(1), eq(1), |
| | | eq(1), eq("OFFLINE"), eq("UNKNOWN"), any(), eq(false)); |
| | | verify(detectionCabinetMapper).updateRegisteredCabinetObservation(eq("lab-bts"), eq(22), eq(1), |
| | | eq("OFFLINE"), any(), eq(false)); |
| | | } |
| | | |
| | | @Test |
| | | void availableChannelWithoutSuccessfulSnapshotCannotAdvanceLastSeenBoundary() |
| | | { |
| | | ChannelIdentity identity = new ChannelIdentity("lab-bts", 22, 1, 1, 1); |
| | | when(adapterMapper.selectDeviceAdapterById(7L)).thenReturn(adapter); |
| | | when(deviceAdapter.discoverChannels()).thenReturn(List.of(new DetectedChannel(identity, "127.0.0.1", true))); |
| | | when(deviceAdapter.inquire(Set.of(identity))).thenReturn(Map.of()); |
| | | stubRegistryExecution(); |
| | | |
| | | service.discover(7L); |
| | | |
| | | verify(detectionCabinetMapper).updateRegisteredChannelObservation(eq("lab-bts"), eq(22), eq(1), eq(1), |
| | | eq(1), eq("ONLINE"), eq("UNKNOWN"), any(), eq(false)); |
| | | verify(detectionCabinetMapper).updateRegisteredCabinetObservation(eq("lab-bts"), eq(22), eq(1), |
| | | eq("ONLINE"), any(), eq(false)); |
| | | } |
| | | |
| | | @Test |
| | |
| | | } |
| | | |
| | | @Test |
| | | void rejectsReservedDevelopmentSimulatorCodeFromDatabaseConfiguration() |
| | | { |
| | | adapter.setAdapterCode("dev-sim"); |
| | | |
| | | assertThrows(ServiceException.class, () -> service.insert(adapter)); |
| | | assertThrows(ServiceException.class, () -> service.ensureManagedAdapter(adapter)); |
| | | verify(adapterMapper, never()).insertDeviceAdapter(any()); |
| | | verify(adapterMapper, never()).upsertManagedDeviceAdapter(any()); |
| | | } |
| | | |
| | | @Test |
| | | void developmentSimulatorProvisioningIsIdempotentAndReadOnly() |
| | | { |
| | | String mapperXml; |