| | |
| | | class DeviceDiscoveryPersistenceServiceTest |
| | | { |
| | | @Test |
| | | void failedDiscoveryInvalidatesSuccessfulSnapshotUntilSoftwareReturns() throws Exception |
| | | { |
| | | try (var simulator = new com.battery.device.simulator.BtsDevelopmentSimulator(0); |
| | | var registry = new DeviceAdapterRegistry(List.of(new com.battery.device.bts.BtsDeviceAdapterFactory()))) |
| | | { |
| | | registry.synchronize(new com.battery.device.api.registry.AdapterInstanceConfig("dev-sim", "BTS", |
| | | "127.0.0.1", simulator.port(), 5000, "Asia/Hong_Kong", true, |
| | | com.battery.device.api.model.AdapterCapabilities.observeOnly())); |
| | | var store = new InMemoryDeviceDiscoveryCandidateStore(); |
| | | var mapper = Mockito.mock(TDetectionCabinetMapper.class); |
| | | var persistence = new DeviceDiscoveryPersistenceService(store, mapper); |
| | | var scheduler = new com.battery.business.runtime.DevelopmentSimulatorDiscoveryScheduler(registry, persistence); |
| | | simulator.updateChannel(1, 1, true, com.battery.device.api.model.DeviceWorkStatus.WORKING); |
| | | scheduler.discover(); |
| | | var before = store.snapshot(java.time.Instant.now()).cabinets().get(0); |
| | | org.junit.jupiter.api.Assertions.assertNotNull(before.channels().get(0).latestSnapshot()); |
| | | simulator.setSoftwareAccessible(false); |
| | | scheduler.discover(); |
| | | var failed = store.snapshot(java.time.Instant.now()).cabinets().get(0); |
| | | assertEquals(before.deviceCode(), failed.deviceCode()); |
| | | org.junit.jupiter.api.Assertions.assertFalse(failed.online()); |
| | | org.junit.jupiter.api.Assertions.assertNull(failed.channels().get(0).latestSnapshot()); |
| | | verify(mapper).markRegisteredCabinetsOffline("dev-sim"); |
| | | simulator.setSoftwareAccessible(true); |
| | | scheduler.discover(); |
| | | org.junit.jupiter.api.Assertions.assertTrue(store.snapshot(java.time.Instant.now()).cabinets().get(0).online()); |
| | | } |
| | | } |
| | | |
| | | @Test |
| | | void discoveryPersistsCandidatesWithoutReceivingTest() |
| | | { |
| | | InMemoryDeviceDiscoveryCandidateStore candidateStore = new InMemoryDeviceDiscoveryCandidateStore(); |
| | |
| | | assertEquals(1, candidateStore.snapshot(java.time.Instant.now()).cabinets().size()); |
| | | verify(adapter).discoverChannels(); |
| | | } |
| | | |
| | | /** |
| | | * 直连厂商存储的来源:通道登记是长期事实,候选不按时间过期, |
| | | * 每条候选附带最近一次采样行的设备时间,供操作员判断哪几台真的在产出数据。 |
| | | */ |
| | | @Test |
| | | void vendorSourceCandidatesCarryLastDataTimeAndDoNotExpireByTime() |
| | | { |
| | | InMemoryDeviceDiscoveryCandidateStore candidateStore = new InMemoryDeviceDiscoveryCandidateStore(); |
| | | TDetectionCabinetMapper cabinetMapper = Mockito.mock(TDetectionCabinetMapper.class); |
| | | TTestMapper testMapper = Mockito.mock(TTestMapper.class); |
| | | DeviceAdapterRegistry registry = Mockito.mock(DeviceAdapterRegistry.class); |
| | | DeviceAdapter adapter = Mockito.mock(DeviceAdapter.class); |
| | | ChannelIdentity identity = new ChannelIdentity("neware76", 21, 36, 1, 2); |
| | | java.time.Instant lastDataAt = java.time.Instant.parse("2026-09-28T01:54:56Z"); |
| | | when(testMapper.hasReceivingTestForAdapter("neware76")).thenReturn(0); |
| | | when(adapter.discoverChannels()).thenReturn(List.of(new DetectedChannel(identity, "26.1.2.3", true, |
| | | com.battery.device.api.model.DetectedDeviceMetadata.empty(), lastDataAt))); |
| | | when(registry.execute(eq("neware76"), any())).thenAnswer(invocation -> { |
| | | @SuppressWarnings("unchecked") |
| | | Function<DeviceAdapter, Object> operation = (Function<DeviceAdapter, Object>) invocation.getArgument(1); |
| | | return operation.apply(adapter); |
| | | }); |
| | | |
| | | DeviceDiscoveryPersistenceService service = new DeviceDiscoveryPersistenceService(candidateStore, |
| | | cabinetMapper, testMapper); |
| | | service.discoverForReception(registry, new DeviceDiscoveryPersistenceService.DiscoverySource( |
| | | "neware76", 8L, "新威 7.6 那台", 60000, true)); |
| | | |
| | | var candidate = candidateStore.snapshot(java.time.Instant.now()).cabinets().get(0); |
| | | assertEquals(lastDataAt, candidate.lastDataAt()); |
| | | assertEquals(lastDataAt, candidate.channels().get(0).lastDataAt()); |
| | | // 一年后仍然是候选:候选只按"已登记且未删除"排除,不按时间过期。 |
| | | assertEquals(1, candidateStore.snapshot(java.time.Instant.now().plusSeconds(365L * 24 * 3600)) |
| | | .cabinets().size()); |
| | | } |
| | | } |