package com.ruoyi.system.service.impl; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.time.Instant; import java.util.Date; import java.util.List; import java.math.BigDecimal; import org.junit.jupiter.api.Test; import com.battery.device.simulator.BtsDevelopmentSimulator; import com.battery.device.simulator.BtsSimulatorStateStore.BtsHistoryRecord; import com.battery.device.simulator.BtsSimulatorStateStore.CollectorRealtimeMeasurement; import com.battery.device.simulator.DevelopmentCollectorSimulation.CollectorRealtimeSnapshot; import com.ruoyi.system.domain.TTestCollectorChannel; import com.ruoyi.system.domain.TTest; import com.ruoyi.system.domain.TTestBattery; import com.ruoyi.system.domain.TTestChannelBinding; import com.ruoyi.system.mapper.TTestMapper; import com.ruoyi.system.service.ITTestService; /** 接收服务只应把本次测试开始后的模拟器事实归入测试。 */ class TestDataReceptionServiceImplTest { @Test void ignoresRecordsBeforeReceptionStart() { TTestMapper mapper = mock(TTestMapper.class); ITTestService tests = mock(ITTestService.class); BtsDevelopmentSimulator simulator = mock(BtsDevelopmentSimulator.class); TTest test = test(Date.from(Instant.parse("2026-09-02T04:00:00Z"))); when(mapper.selectReceivingTestIds()).thenReturn(List.of(1L)); when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test); when(tests.selectTestById(1L)).thenReturn(test); when(simulator.btsRecords(1, 1)).thenReturn(List.of( record(1L, "2026-09-02T03:59:59Z"), record(2L, "2026-09-02T04:00:01Z"))); when(mapper.selectBtsSampleContentHash(any(), anyString(), any())).thenReturn(null); when(mapper.insertBtsSample(any(), any(), any(), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyInt(), anyInt(), anyString(), anyString(), isNull(), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(byte[].class))).thenReturn(1); new TestDataReceptionServiceImpl(mapper, tests, simulator).captureDevelopmentCabinetRecords(); verify(mapper, times(1)).insertBtsSample(eq(1L), eq(2L), eq(3L), anyString(), eq(2L), any(Date.class), anyString(), any(Date.class), anyInt(), anyInt(), anyString(), anyString(), isNull(), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(byte[].class)); } @Test void doesNotWriteSamplesWhenNoSuccessfulRecordsAreAvailable() { TTestMapper mapper = mock(TTestMapper.class); ITTestService tests = mock(ITTestService.class); BtsDevelopmentSimulator simulator = mock(BtsDevelopmentSimulator.class); TTest test = test(Date.from(Instant.parse("2026-09-02T04:00:00Z"))); when(mapper.selectReceivingTestIds()).thenReturn(List.of(1L)); when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test); when(tests.selectTestById(1L)).thenReturn(test); when(simulator.btsRecords(1, 1)).thenReturn(List.of()); new TestDataReceptionServiceImpl(mapper, tests, simulator).captureDevelopmentCabinetRecords(); verify(mapper, never()).insertBtsSample(any(), any(), any(), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyInt(), anyInt(), anyString(), anyString(), isNull(), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(byte[].class)); } @Test void ordinaryEditsDoNotMoveReceptionBoundary() { TTestMapper mapper = mock(TTestMapper.class); ITTestService tests = mock(ITTestService.class); BtsDevelopmentSimulator simulator = mock(BtsDevelopmentSimulator.class); TTest test = test(Date.from(Instant.parse("2026-09-02T04:00:00Z"))); test.setUpdateTime(Date.from(Instant.parse("2026-09-02T04:00:10Z"))); when(mapper.selectReceivingTestIds()).thenReturn(List.of(1L)); when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test); when(tests.selectTestById(1L)).thenReturn(test); when(simulator.btsRecords(1, 1)).thenReturn(List.of( record(1L, "2026-09-02T04:00:05Z"), record(2L, "2026-09-02T04:00:11Z"))); when(mapper.selectBtsSampleContentHash(any(), anyString(), any())).thenReturn(null); when(mapper.insertBtsSample(any(), any(), any(), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyInt(), anyInt(), anyString(), anyString(), isNull(), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(byte[].class))).thenReturn(1); new TestDataReceptionServiceImpl(mapper, tests, simulator).captureDevelopmentCabinetRecords(); verify(mapper, times(2)).insertBtsSample(eq(1L), eq(2L), eq(3L), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyInt(), anyInt(), anyString(), anyString(), isNull(), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(BigDecimal.class), any(byte[].class)); } @Test void capturesOnlyMappedCollectorRealtimeMeasurementsAfterReceptionStart() { TTestMapper mapper = mock(TTestMapper.class); ITTestService tests = mock(ITTestService.class); BtsDevelopmentSimulator simulator = mock(BtsDevelopmentSimulator.class); TTest test = test(Date.from(Instant.parse("2026-09-02T04:00:00Z"))); test.setReceptionBoundaryAtUtc(Date.from(Instant.parse("2026-09-02T04:00:01.500Z"))); test.getBatteries().get(0).setCollectorId(9L); TTestCollectorChannel mapping = new TTestCollectorChannel(); mapping.setMeasurementCode("temperature"); mapping.setChannelCode("S-01"); test.getBatteries().get(0).setCollectorChannels(List.of(mapping)); when(mapper.selectReceivingTestIds()).thenReturn(List.of(1L)); when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test); when(tests.selectTestById(1L)).thenReturn(test); CollectorRealtimeMeasurement matched = new CollectorRealtimeMeasurement("dev-sim", "collector-9", "S-01", 3L, Instant.parse("2026-09-02T04:00:01Z"), "2026-09-02 12:00:01.000", Instant.parse("2026-09-02T04:00:02Z"), "temperature", new BigDecimal("25.1"), "℃"); CollectorRealtimeMeasurement ignored = new CollectorRealtimeMeasurement("dev-sim", "collector-9", "S-02", 3L, matched.deviceTime(), matched.deviceTimeRaw(), matched.serverReceivedAt(), "temperature", new BigDecimal("25.2"), "℃"); CollectorRealtimeMeasurement beforeBoundary = new CollectorRealtimeMeasurement("dev-sim", "collector-9", "S-01", 2L, Instant.parse("2026-09-02T04:00:00Z"), "2026-09-02 12:00:00.000", Instant.parse("2026-09-02T04:00:01Z"), "temperature", new BigDecimal("24.9"), "℃"); when(simulator.collectorRealtime(9)).thenReturn(new CollectorRealtimeSnapshot("collector-9", true, 1000, List.of(matched, ignored, beforeBoundary))); when(mapper.selectCollectorSampleContentHash(any(), anyString(), any(), anyString())).thenReturn(null); when(mapper.insertCollectorSample(any(), any(), any(), anyString(), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyString(), any(BigDecimal.class), anyString(), any(byte[].class))).thenReturn(1); new TestDataReceptionServiceImpl(mapper, tests, simulator).captureDevelopmentCollectorMeasurements(); verify(mapper).insertCollectorSample(eq(1L), eq(2L), eq(9L), eq("dev-sim"), eq("S-01"), eq(3L), any(Date.class), eq("2026-09-02 12:00:01.000"), any(Date.class), eq("temperature"), eq(new BigDecimal("25.1")), eq("℃"), any(byte[].class)); verify(mapper, never()).insertCollectorSample(eq(1L), eq(2L), eq(9L), anyString(), eq("S-02"), any(), any(Date.class), anyString(), any(Date.class), anyString(), any(BigDecimal.class), anyString(), any(byte[].class)); } @Test void recordsCollectorContentConflictWithoutOverwritingOriginal() { TTestMapper mapper = mock(TTestMapper.class); ITTestService tests = mock(ITTestService.class); BtsDevelopmentSimulator simulator = mock(BtsDevelopmentSimulator.class); TTest test = test(Date.from(Instant.parse("2026-09-02T04:00:00Z"))); test.getBatteries().get(0).setCollectorId(9L); TTestCollectorChannel mapping = new TTestCollectorChannel(); mapping.setMeasurementCode("temperature"); mapping.setChannelCode("S-01"); test.getBatteries().get(0).setCollectorChannels(List.of(mapping)); CollectorRealtimeMeasurement measurement = new CollectorRealtimeMeasurement("dev-sim", "collector-9", "S-01", 3L, Instant.parse("2026-09-02T04:00:01Z"), "2026-09-02 12:00:01.000", Instant.now(), "temperature", new BigDecimal("25.1"), "℃"); when(mapper.selectReceivingTestIds()).thenReturn(List.of(1L)); when(mapper.selectTestByIdForUpdate(1L)).thenReturn(test); when(tests.selectTestById(1L)).thenReturn(test); when(simulator.collectorRealtime(9)).thenReturn(new CollectorRealtimeSnapshot("collector-9", true, 1000, List.of(measurement))); when(mapper.selectCollectorSampleContentHash(9L, "S-01", 3L, "temperature")).thenReturn("different"); new TestDataReceptionServiceImpl(mapper, tests, simulator).captureDevelopmentCollectorMeasurements(); verify(mapper).insertSampleConflict(eq(1L), eq(3L), org.mockito.ArgumentMatchers.contains("采集器实时接收内容冲突")); verify(mapper, never()).insertCollectorSample(any(), any(), any(), anyString(), anyString(), any(), any(Date.class), anyString(), any(Date.class), anyString(), any(BigDecimal.class), anyString(), any(byte[].class)); } private static TTest test(Date startedAt) { TTestChannelBinding binding = new TTestChannelBinding(); binding.setBindingId(2L); binding.setChannelId(3L); binding.setDevid(1); binding.setChlid(1); TTestBattery battery = new TTestBattery(); battery.setBinding(binding); TTest test = new TTest(); test.setTestId(1L); test.setTestStatus("RUNNING"); test.setReceptionStatus("RECEIVING"); test.setStartedAtUtc(startedAt); test.setBatteries(List.of(battery)); return test; } private static BtsHistoryRecord record(long sequenceId, String deviceTime) { Instant parsed = Instant.parse(deviceTime); return new BtsHistoryRecord(sequenceId, parsed, 1d, 2d, 3d, 4d, 1, 1, "恒流充电", "cc", parsed); } }