From 27428b31edee97703b529559146cdd7eef87a7a9 Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期六, 05 九月 2026 11:00:58 +0800
Subject: [PATCH] 完成测试数据接收持久化与查询

---
 ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImplTest.java |  323 +++++++++++++++++++++++++++++++----------------------
 1 files changed, 188 insertions(+), 135 deletions(-)

diff --git a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImplTest.java b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImplTest.java
index 5a14a85..16cab20 100644
--- a/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImplTest.java
+++ b/ruoyi-admin/src/test/java/com/ruoyi/system/service/impl/TestDataReceptionServiceImplTest.java
@@ -1,188 +1,241 @@
 package com.ruoyi.system.service.impl;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 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 java.time.Clock;
+import java.time.Instant;
+import java.time.ZoneOffset;
+import java.util.Date;
+import java.util.HexFormat;
+import java.util.List;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
+import org.springframework.boot.test.system.CapturedOutput;
+import org.springframework.boot.test.system.OutputCaptureExtension;
 import com.battery.device.simulator.BtsDevelopmentSimulator;
+import com.battery.device.simulator.BtsDevelopmentSimulator.BtsCycleRecord;
+import com.battery.device.simulator.BtsDevelopmentSimulator.BtsStepRecord;
 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.domain.TTestCollectorChannel;
+import com.ruoyi.system.domain.TTestCycleRecord;
+import com.ruoyi.system.domain.TTestRealtimeRecord;
+import com.ruoyi.system.domain.TTestStepRecord;
 import com.ruoyi.system.mapper.TTestMapper;
 import com.ruoyi.system.service.ITTestService;
 
-/** 接收服务只应把本次测试开始后的模拟器事实归入测试。 */
+/** Ticket08 通过接收入口验证三类持久化事实。 */
+@ExtendWith(OutputCaptureExtension.class)
 class TestDataReceptionServiceImplTest
 {
+    private static final Instant CAPTURED_AT = Instant.parse("2026-09-02T04:01:00Z");
+    private static final Clock CLOCK = Clock.fixed(CAPTURED_AT, ZoneOffset.UTC);
+
     @Test
-    void ignoresRecordsBeforeReceptionStart()
+    void savesOneCombinedRealtimeRowForOneBatteryPollingSlot()
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.btsRecords(1, 1)).thenReturn(List.of(cabinetRecord()));
+        when(fixture.simulator.collectorRealtime(9)).thenReturn(collectorSnapshot(true, List.of(
+                collector("S-01", 10L, "TEMPERATURE", "25.100", "℃"),
+                collector("S-02", 10L, "POSITIVE_POTENTIAL", "1.620", "V"),
+                collector("S-03", 10L, "NEGATIVE_POTENTIAL", "1.610", "V"),
+                collector("S-04", 10L, "AMBIENT_TEMPERATURE", "23.400", "℃"))));
+        when(fixture.mapper.insertRealtimeRecord(any())).thenReturn(1);
+
+        fixture.service().captureDevelopmentRealtimeSnapshots();
+
+        ArgumentCaptor<TTestRealtimeRecord> saved = ArgumentCaptor.forClass(TTestRealtimeRecord.class);
+        verify(fixture.mapper).insertRealtimeRecord(saved.capture());
+        TTestRealtimeRecord row = saved.getValue();
+        assertEquals(1L, row.getTestId());
+        assertEquals(4L, row.getTestBatteryId());
+        assertEquals(2L, row.getBindingId());
+        assertEquals("dev-sim:22:1:1:1", row.getSourceIdentity());
+        assertEquals("cabinet:7:collector:10", row.getSourceRecordId());
+        assertEquals(Date.from(Instant.parse("2026-09-02T04:00:30Z")), row.getCabinetDeviceTimeUtc());
+        assertEquals(Date.from(Instant.parse("2026-09-02T04:00:40Z")), row.getCollectorDeviceTimeUtc());
+        assertEquals(new BigDecimal("25.100"), row.getTemperature());
+        assertEquals(new BigDecimal("1.620"), row.getPositivePotential());
+        assertEquals(new BigDecimal("1.610"), row.getNegativePotential());
+        assertTrue(row.getExtensionMeasurementsJson().contains("AMBIENT_TEMPERATURE"));
+        assertTrue(!row.getExtensionMeasurementsJson().contains("POSITIVE_POTENTIAL"));
+    }
+
+    @Test
+    void savesCollectorFieldsWhenCabinetSourceFails()
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.btsRecords(1, 1)).thenThrow(new IllegalArgumentException("offline"));
+        when(fixture.simulator.collectorRealtime(9)).thenReturn(collectorSnapshot(true,
+                List.of(collector("S-01", 11L, "TEMPERATURE", "26.2", "℃"))));
+        when(fixture.mapper.insertRealtimeRecord(any())).thenReturn(1);
+
+        fixture.service().captureDevelopmentRealtimeSnapshots();
+
+        ArgumentCaptor<TTestRealtimeRecord> saved = ArgumentCaptor.forClass(TTestRealtimeRecord.class);
+        verify(fixture.mapper).insertRealtimeRecord(saved.capture());
+        assertNull(saved.getValue().getCabinetSequenceId());
+        assertNull(saved.getValue().getCabinetDevtype());
+        assertNull(saved.getValue().getCabinetAdapterTimezoneId());
+        assertNull(saved.getValue().getVoltageV());
+        assertEquals(11L, saved.getValue().getCollectorSequenceId());
+        assertEquals("collector:11", saved.getValue().getSourceRecordId());
+        assertEquals(new BigDecimal("26.2"), saved.getValue().getTemperature());
+        assertNull(saved.getValue().getPositivePotential());
+    }
+
+    @Test
+    void savesOnlyDeviceReturnedStepAndCycleRecords()
+    {
+        Fixture fixture = fixture();
+        Instant started = Instant.parse("2026-09-02T04:00:10Z");
+        Instant ended = Instant.parse("2026-09-02T04:00:50Z");
+        when(fixture.simulator.btsSteps(1, 1)).thenReturn(List.of(new BtsStepRecord(
+                1, 2, 2, "恒流充电", "cc", 41, started, ended, 40,
+                2.5, 8.4, 3.1, 3.4, 3.4, 0)));
+        when(fixture.simulator.btsCycles(1, 1)).thenReturn(List.of(new BtsCycleRecord(
+                "CYCLE-1-1-1", 1, 80, started, ended, 2.5, 2.4, 8.4, 8.0, 96)));
+        when(fixture.mapper.insertStepRecord(any())).thenReturn(1);
+        when(fixture.mapper.insertCycleRecord(any())).thenReturn(1);
+
+        fixture.service().captureDevelopmentCabinetRecords();
+
+        ArgumentCaptor<TTestStepRecord> step = ArgumentCaptor.forClass(TTestStepRecord.class);
+        ArgumentCaptor<TTestCycleRecord> cycle = ArgumentCaptor.forClass(TTestCycleRecord.class);
+        verify(fixture.mapper).insertStepRecord(step.capture());
+        verify(fixture.mapper).insertCycleRecord(cycle.capture());
+        assertEquals("fallback:2026-09-02T04:00:50Z:1:2", step.getValue().getDedupeKey());
+        assertEquals("CYCLE-1-1-1", cycle.getValue().getSourceRecordId());
+        assertEquals("record:CYCLE-1-1-1", cycle.getValue().getDedupeKey());
+        assertEquals(Date.from(CAPTURED_AT), step.getValue().getServerReceivedAtUtc());
+        assertEquals(Date.from(CAPTURED_AT), cycle.getValue().getServerReceivedAtUtc());
+    }
+
+    @Test
+    void ignoresSameRealtimeContentOnRepeatedReception()
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.btsRecords(1, 1)).thenReturn(List.of(cabinetRecord()));
+        when(fixture.simulator.collectorRealtime(9)).thenReturn(collectorSnapshot(false, List.of()));
+        when(fixture.mapper.insertRealtimeRecord(any())).thenReturn(1);
+
+        fixture.service().captureDevelopmentRealtimeSnapshots();
+        ArgumentCaptor<TTestRealtimeRecord> saved = ArgumentCaptor.forClass(TTestRealtimeRecord.class);
+        verify(fixture.mapper).insertRealtimeRecord(saved.capture());
+        when(fixture.mapper.selectRealtimeRecordContentHash(eq(1L), eq(4L), any(Date.class)))
+                .thenReturn(HexFormat.of().formatHex(saved.getValue().getContentSha256()));
+
+        fixture.service().captureDevelopmentRealtimeSnapshots();
+
+        verify(fixture.mapper, times(1)).insertRealtimeRecord(any());
+    }
+
+    @Test
+    void rejectsConflictingRealtimeContentAndWritesApplicationLog(CapturedOutput output)
+    {
+        Fixture fixture = fixture();
+        when(fixture.simulator.btsRecords(1, 1)).thenReturn(List.of(cabinetRecord()));
+        when(fixture.simulator.collectorRealtime(9)).thenReturn(collectorSnapshot(false, List.of()));
+        when(fixture.mapper.selectRealtimeRecordContentHash(eq(1L), eq(4L), any(Date.class)))
+                .thenReturn("different-content");
+
+        fixture.service().captureDevelopmentRealtimeSnapshots();
+
+        verify(fixture.mapper, never()).insertRealtimeRecord(any());
+        assertTrue(output.getOut().contains("实时采样内容冲突"));
+    }
+
+    private static Fixture fixture()
     {
         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")));
+        TTest test = receivingTest();
         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));
+        when(simulator.persistenceIntervalMillis(1, 1)).thenReturn(60000L);
+        return new Fixture(mapper, tests, simulator);
     }
 
-    @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)
+    private static TTest receivingTest()
     {
         TTestChannelBinding binding = new TTestChannelBinding();
         binding.setBindingId(2L);
         binding.setChannelId(3L);
+        binding.setAdapterCode("dev-sim");
+        binding.setAdapterTimezoneId("Asia/Hong_Kong");
+        binding.setDevtype(22);
         binding.setDevid(1);
+        binding.setSubdevid(1);
         binding.setChlid(1);
         TTestBattery battery = new TTestBattery();
+        battery.setTestBatteryId(4L);
+        battery.setCollectorId(9L);
         battery.setBinding(binding);
+        battery.setCollectorChannels(List.of(
+                mapping("TEMPERATURE", "S-01"), mapping("POSITIVE_POTENTIAL", "S-02"),
+                mapping("NEGATIVE_POTENTIAL", "S-03"), mapping("AMBIENT_TEMPERATURE", "S-04")));
         TTest test = new TTest();
         test.setTestId(1L);
         test.setTestStatus("RUNNING");
         test.setReceptionStatus("RECEIVING");
-        test.setStartedAtUtc(startedAt);
+        test.setStartedAtUtc(Date.from(Instant.parse("2026-09-02T04:00:00Z")));
+        test.setReceptionBoundaryAtUtc(Date.from(Instant.parse("2026-09-02T04:00:00Z")));
         test.setBatteries(List.of(battery));
         return test;
     }
 
-    private static BtsHistoryRecord record(long sequenceId, String deviceTime)
+    private static TTestCollectorChannel mapping(String metric, String channel)
     {
-        Instant parsed = Instant.parse(deviceTime);
-        return new BtsHistoryRecord(sequenceId, parsed, 1d, 2d, 3d, 4d,
-                1, 1, "恒流充电", "cc", parsed);
+        TTestCollectorChannel mapping = new TTestCollectorChannel();
+        mapping.setMeasurementCode(metric);
+        mapping.setChannelCode(channel);
+        return mapping;
+    }
+
+    private static BtsHistoryRecord cabinetRecord()
+    {
+        Instant deviceTime = Instant.parse("2026-09-02T04:00:30Z");
+        return new BtsHistoryRecord(7L, deviceTime, 1, 3.2, 2.5, 8.4,
+                1, 2, "恒流充电", "cc", Instant.parse("2026-09-02T04:00:35Z"));
+    }
+
+    private static CollectorRealtimeMeasurement collector(String channel, long sequenceId, String metric,
+            String value, String unit)
+    {
+        return new CollectorRealtimeMeasurement("dev-sim", "collector-9", channel, sequenceId,
+                Instant.parse("2026-09-02T04:00:40Z"), "2026-09-02 12:00:40.000",
+                Instant.parse("2026-09-02T04:00:50Z"), metric, new BigDecimal(value), unit);
+    }
+
+    private static CollectorRealtimeSnapshot collectorSnapshot(boolean online,
+            List<CollectorRealtimeMeasurement> measurements)
+    {
+        return new CollectorRealtimeSnapshot("collector-9", online, 1000, measurements);
+    }
+
+    private record Fixture(TTestMapper mapper, ITTestService tests, BtsDevelopmentSimulator simulator)
+    {
+        TestDataReceptionServiceImpl service()
+        {
+            return new TestDataReceptionServiceImpl(mapper, tests, simulator, CLOCK);
+        }
     }
 }

--
Gitblit v1.7.1