yeg
2026-09-29 b78295e8da96c3e5e76befeef9582ce5273dbb74
ruoyi-admin/src/test/java/com/battery/business/service/impl/DeviceDiscoveryPersistenceServiceTest.java
@@ -20,6 +20,36 @@
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();
@@ -46,4 +76,40 @@
        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());
    }
}