yeg
2026-08-29 7ab0e426264707fb85ee38d0a9a73caf41899635
ruoyi-device/src/test/java/com/ruoyi/device/contract/SimulatorPublicContractTest.java
@@ -1,9 +1,14 @@
package com.ruoyi.device.contract;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.util.List;
import java.util.Set;
import org.junit.jupiter.api.Test;
import com.ruoyi.device.api.DeviceAdapter;
@@ -16,6 +21,8 @@
import com.ruoyi.device.model.DeviceWorkStatus;
import com.ruoyi.device.simulator.BtsSimulatorStateStore;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.ChannelState;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryFaults;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMetricState;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.DeviceState;
import com.ruoyi.device.simulator.BtsSimulatorStateStore.SimulatorState;
import com.ruoyi.device.simulator.InMemoryBtsSimulatorStateStore;
@@ -23,6 +30,89 @@
class SimulatorPublicContractTest
{
    @Test
    void collectorGeneratesIndependentRealtimeAndHistoricalMeasurements() throws Exception
    {
        Instant startedAt = Instant.parse("2026-08-29T01:00:00Z");
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
        {
            simulator.configureCollectorSampling(1, 200, CollectorHistoryFaults.none());
            simulator.configureCollectorChannel(1, "S-01", List.of(
                    new CollectorMetricState("temperature", new BigDecimal("26.5"), "℃"),
                    new CollectorMetricState("positive_potential", new BigDecimal("3.801"), "V"),
                    new CollectorMetricState("negative_potential", new BigDecimal("-0.120"), "V"),
                    new CollectorMetricState("pressure", new BigDecimal("101.2"), "kPa")));
            simulator.advanceCollectors(startedAt);
            simulator.advanceCollectors(startedAt.plusMillis(400));
            var realtime = simulator.snapshot().collectors().get(0).latestMeasurements();
            var history = simulator.collectorHistory(1, 0, 1000);
            assertEquals(13, realtime.size());
            assertTrue(realtime.stream().allMatch(item -> item.sequenceId() == 3));
            assertTrue(realtime.stream().anyMatch(item -> item.channelCode().equals("S-01")
                    && item.metricCode().equals("pressure") && item.unit().equals("kPa")));
            assertEquals(39, history.total());
            assertEquals("dev-sim", history.records().get(0).adapterCode());
            assertEquals("dev-sim-collector-1", history.records().get(0).collectorCode());
            assertEquals(startedAt, history.records().get(0).deviceTime());
            assertTrue(history.records().get(0).deviceTimeRaw().startsWith("2026-08-29 09:00:00"));
            assertEquals(startedAt, history.records().get(0).serverReceivedAt());
        }
    }
    @Test
    void collectorHistorySupportsPagingAndRepeatableFaultScenarios() throws Exception
    {
        Instant startedAt = Instant.parse("2026-08-29T02:00:00Z");
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0))
        {
            simulator.configureCollectorSampling(1, 100,
                    new CollectorHistoryFaults(List.of(2L), 1L, 3L, true));
            simulator.advanceCollectors(startedAt);
            simulator.advanceCollectors(startedAt.plusMillis(200));
            var first = simulator.collectorHistory(1, 0, 17);
            var second = simulator.collectorHistory(1, first.nextOffset(), 1000);
            List<com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord> records =
                    new java.util.ArrayList<>(first.records());
            records.addAll(second.records());
            assertEquals(48, first.total());
            assertTrue(first.hasMore());
            assertFalse(second.hasMore());
            assertTrue(records.stream().noneMatch(item -> item.sequenceId() == 2));
            assertEquals(24, records.stream().filter(item -> item.sequenceId() == 1).count());
            assertEquals(2, records.stream().filter(item -> item.sequenceId() == 3
                    && item.channelCode().equals("S-01") && item.metricCode().equals("temperature")).count());
            assertTrue(records.get(0).sequenceId() >= records.get(records.size() - 1).sequenceId());
        }
    }
    @Test
    void collectorStopsWhileOfflineAndResumesFromPersistedSequence() throws Exception
    {
        InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore();
        Instant now = Instant.now();
        try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store))
        {
            simulator.advanceCollectors(now);
            simulator.updateCollector(1, "127.0.1.2", 4, false);
            simulator.advanceCollectors(now.plusSeconds(10));
            assertEquals(1, simulator.snapshot().collectors().get(0).latestMeasurements().get(0).sequenceId());
        }
        try (BtsDevelopmentSimulator restored = new BtsDevelopmentSimulator(0, store))
        {
            restored.updateCollector(1, "127.0.1.2", 4, true);
            restored.advanceCollectors(Instant.now().plusSeconds(2));
            assertTrue(restored.snapshot().collectors().get(0).latestMeasurements().get(0).sequenceId() >= 2);
            assertEquals(List.of("S-01", "S-02", "S-03", "S-04"),
                    restored.snapshot().collectors().get(0).channels().stream()
                            .map(channel -> channel.channelCode()).toList());
        }
    }
    @Test
    void simulatorIsReusableOutsideTheBtsPackage() throws Exception
    {
        try (ScriptedBtsTcpSimulator simulator = new ScriptedBtsTcpSimulator();