package com.ruoyi.system.service.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.time.Instant; import java.util.List; import java.util.Set; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; import com.ruoyi.system.domain.TTestCollectorExceptionInterval; import com.ruoyi.system.domain.TTestCollectorSource; import com.ruoyi.system.mapper.TTestMapper; /** 采集器断线对账的来源级幂等与恢复边界测试。 */ @ExtendWith(MockitoExtension.class) class CollectorDisconnectRecoveryServiceImplTest { @Mock private TTestMapper mapper; @InjectMocks private CollectorDisconnectRecoveryServiceImpl service; @Test void opensOnlyOfflineSourceAndKeepsOtherSourcesIndependent() { TTestCollectorSource offline = source(10L, 20L, 8L, "COL-1", "S-01"); TTestCollectorSource online = source(10L, 20L, 9L, "COL-2", "S-01"); when(mapper.selectReceivingCollectorSources()).thenReturn(List.of(offline, online)); when(mapper.selectOpenCollectorException(10L, 20L, 8L, "S-01")).thenReturn(null); when(mapper.selectOpenCollectorException(10L, 20L, 9L, "S-01")).thenReturn(null); service.reconcile(Set.of("COL-2"), Instant.parse("2026-08-30T09:00:00Z")); verify(mapper).insertCollectorExceptionInterval(any(TTestCollectorExceptionInterval.class)); verify(mapper, never()).closeCollectorExceptionInterval(any(), any(), any()); } @Test void closesOpenIntervalAtRecoveryBoundary() { TTestCollectorSource source = source(10L, 20L, 8L, "COL-1", "S-01"); TTestCollectorExceptionInterval open = new TTestCollectorExceptionInterval(); open.setIntervalId(99L); open.setIntervalStatus("OPEN"); when(mapper.selectReceivingCollectorSources()).thenReturn(List.of(source)); when(mapper.selectOpenCollectorException(10L, 20L, 8L, "S-01")).thenReturn(open); service.reconcile(Set.of("COL-1"), Instant.parse("2026-08-30T09:01:00Z")); verify(mapper).closeCollectorExceptionInterval(eq(99L), any(), any()); verify(mapper, never()).insertCollectorExceptionInterval(any()); } private TTestCollectorSource source(Long testId, Long relationId, Long collectorId, String collectorCode, String channelCode) { TTestCollectorSource source = new TTestCollectorSource(); source.setTestId(testId); source.setBatteryRelationId(relationId); source.setCollectorId(collectorId); source.setCollectorCode(collectorCode); source.setBatteryNo("BAT-01"); source.setChannelCode(channelCode); return source; } }