| | |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.ChannelState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorChannelState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryAppend; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryBatch; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorHistoryFaults; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMeasurementRecord; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorMetricState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.CollectorState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.DeviceState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.SimulatorState; |
| | | import com.ruoyi.device.simulator.BtsSimulatorStateStore.RecentRequest; |
| | | import com.ruoyi.device.simulator.InMemoryBtsSimulatorStateStore; |
| | | |
| | | class SimulatorPublicContractTest |
| | |
| | | void collectorKeepsAppendOnlyHistoryBeyondPreviousDisplayLimit() throws Exception |
| | | { |
| | | Instant startedAt = Instant.parse("2026-08-29T03:00:00Z"); |
| | | try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0)) |
| | | TrackingHistoryStore store = new TrackingHistoryStore(); |
| | | try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store)) |
| | | { |
| | | simulator.configureCollectorSampling(1, 100, CollectorHistoryFaults.none()); |
| | | simulator.advanceCollectors(startedAt); |
| | | for (int batch = 1; batch <= 5; batch++) |
| | | simulator.advanceCollectors(startedAt.plusMillis(batch * 100_000L)); |
| | | |
| | | assertEquals(60_012, simulator.collectorHistory(1, 0, 1).total()); |
| | | assertEquals(1, simulator.collectorHistory(1, 0, 1).records().get(0).sequenceId()); |
| | | store.resetReadStats(); |
| | | var page = simulator.collectorHistory(1, 0, 1); |
| | | assertEquals(60_012, page.total()); |
| | | assertEquals(1, page.records().get(0).sequenceId()); |
| | | assertEquals(1, store.historyReads); |
| | | assertEquals(1, store.recordsRead); |
| | | } |
| | | |
| | | store.resetReadStats(); |
| | | try (BtsDevelopmentSimulator ignored = new BtsDevelopmentSimulator(0, store)) |
| | | { |
| | | assertTrue(store.historyReads > 1); |
| | | assertEquals(1000, store.maximumRequestedLimit); |
| | | assertEquals(60_012, store.recordsRead); |
| | | } |
| | | } |
| | | |
| | |
| | | assertThrows(IllegalStateException.class, |
| | | () -> new BtsDevelopmentSimulator(0, store)); |
| | | } |
| | | |
| | | @Test |
| | | void developmentSimulatorRejectsHistorySequenceRegressionAcrossPages() |
| | | { |
| | | Instant observedAt = Instant.parse("2026-08-29T06:30:00Z"); |
| | | List<CollectorChannelState> channels = List.of(new CollectorChannelState("S-01", List.of( |
| | | new CollectorMetricState("temperature", new BigDecimal("25.0"), "℃")))); |
| | | List<CollectorMeasurementRecord> records = new java.util.ArrayList<>(); |
| | | for (long sequence = 1; sequence <= 1000; sequence++) |
| | | records.add(new CollectorMeasurementRecord("dev-sim", "dev-sim-collector-1", "S-01", sequence, |
| | | observedAt, "2026-08-29 14:30:00.000", observedAt, "temperature", |
| | | new BigDecimal("25.0"), "℃")); |
| | | records.add(new CollectorMeasurementRecord("dev-sim", "dev-sim-collector-1", "S-01", 999, |
| | | observedAt, "2026-08-29 14:30:00.000", observedAt, "temperature", |
| | | new BigDecimal("25.0"), "℃")); |
| | | CollectorState collector = new CollectorState(1, "dev-sim-collector-1", "127.0.1.2", |
| | | "02:43:00:00:00:01", "模拟采集器", "开发模拟器", "Virtual Collector", 1, true, |
| | | 1000, 1001L, observedAt, channels, List.of(), records.size(), CollectorHistoryFaults.none()); |
| | | SimulatorState damaged = new SimulatorState(BtsSimulatorStateStore.CURRENT_VERSION, |
| | | List.of(), List.of(collector), 1, 2, List.of()); |
| | | InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore(damaged); |
| | | store.save(damaged, List.of(new CollectorHistoryAppend(collector.deviceCode(), records))); |
| | | |
| | | assertThrows(IllegalStateException.class, |
| | | () -> new BtsDevelopmentSimulator(0, store)); |
| | | } |
| | | |
| | | private static final class TrackingHistoryStore implements BtsSimulatorStateStore |
| | | { |
| | | private final InMemoryBtsSimulatorStateStore delegate = new InMemoryBtsSimulatorStateStore(); |
| | | private int historyReads; |
| | | private int maximumRequestedLimit; |
| | | private long recordsRead; |
| | | |
| | | @Override |
| | | public SimulatorState loadOrInitialize(SimulatorState defaultState) |
| | | { |
| | | return delegate.loadOrInitialize(defaultState); |
| | | } |
| | | |
| | | @Override |
| | | public void save(SimulatorState state) |
| | | { |
| | | delegate.save(state); |
| | | } |
| | | |
| | | @Override |
| | | public void save(SimulatorState state, List<CollectorHistoryAppend> historyAppends) |
| | | { |
| | | delegate.save(state, historyAppends); |
| | | } |
| | | |
| | | @Override |
| | | public CollectorHistoryBatch loadCollectorHistoryPage(String collectorCode, long offset, int limit, |
| | | boolean reverse) |
| | | { |
| | | CollectorHistoryBatch batch = delegate.loadCollectorHistoryPage(collectorCode, offset, limit, reverse); |
| | | historyReads++; |
| | | maximumRequestedLimit = Math.max(maximumRequestedLimit, limit); |
| | | recordsRead += batch.records().size(); |
| | | return batch; |
| | | } |
| | | |
| | | @Override |
| | | public void saveAndDeleteCollectorHistory(SimulatorState state, String collectorCode) |
| | | { |
| | | delegate.saveAndDeleteCollectorHistory(state, collectorCode); |
| | | } |
| | | |
| | | @Override |
| | | public List<RecentRequest> loadRecentRequests() |
| | | { |
| | | return delegate.loadRecentRequests(); |
| | | } |
| | | |
| | | @Override |
| | | public void saveRecentRequests(List<RecentRequest> requests) |
| | | { |
| | | delegate.saveRecentRequests(requests); |
| | | } |
| | | |
| | | @Override |
| | | public void reset(SimulatorState defaultState) |
| | | { |
| | | delegate.reset(defaultState); |
| | | } |
| | | |
| | | private void resetReadStats() |
| | | { |
| | | historyReads = 0; |
| | | maximumRequestedLimit = 0; |
| | | recordsRead = 0; |
| | | } |
| | | } |
| | | } |