package com.ruoyi.device.bts; 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 java.time.Instant; import org.junit.jupiter.api.Test; import com.ruoyi.device.model.DeviceWorkStatus; import com.ruoyi.device.simulator.BtsSimulatorStateStore.BtsPlaybackState; import com.ruoyi.device.simulator.InMemoryBtsSimulatorStateStore; /** Ticket21:检测柜 record、step、cycle 生命周期和播放控制合同。 */ class BtsDevelopmentSimulatorRecordStepCycleTest { @Test void recordsUseMinuteDeviceTimeAndActualStepCycleIdentity() throws Exception { Instant start = Instant.parse("2026-08-31T00:00:00Z"); try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, new InMemoryBtsSimulatorStateStore())) { simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING); simulator.advanceBts(start); simulator.advanceBts(start.plusSeconds(5 * 60L)); var records = simulator.btsRecords(1, 1); assertTrue(records.size() >= 6); assertEquals(1L, records.get(0).sequenceId()); assertEquals(60L, records.get(1).deviceTime().getEpochSecond() - records.get(0).deviceTime().getEpochSecond()); assertEquals(1, records.get(0).cycleNo()); assertTrue(simulator.btsSteps(1, 1).stream().anyMatch(step -> step.stepNo() == 1)); assertTrue(simulator.btsCycles(1, 1).stream().anyMatch(cycle -> cycle.cycleNo() == 1)); } } @Test void playbackSpeedPauseResumeAndRestartAreIndependentOfDeviceStatus() throws Exception { Instant start = Instant.parse("2026-08-31T00:00:00Z"); InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore(); try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store)) { simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING); simulator.setBtsSpeed(1, 1, 600); simulator.advanceBts(start); simulator.pauseBts(1, 1); int pausedCount = simulator.btsRecords(1, 1).size(); simulator.advanceBts(start.plusSeconds(600)); assertEquals(pausedCount, simulator.btsRecords(1, 1).size()); assertFalse(simulator.btsPlayback(1, 1).playback().playing()); } try (BtsDevelopmentSimulator restored = new BtsDevelopmentSimulator(0, store)) { BtsPlaybackState playback = restored.btsPlayback(1, 1).playback(); assertEquals(600, playback.speedMultiplier()); assertFalse(playback.playing()); restored.resumeBts(1, 1); assertTrue(restored.btsPlayback(1, 1).playback().playing()); } } @Test void nextCycleAndResetDoNotSendDeviceControlAndNaturalFinishStopsPlayback() throws Exception { InMemoryBtsSimulatorStateStore store = new InMemoryBtsSimulatorStateStore(); try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, store)) { simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING); simulator.nextBtsCycle(1, 1); assertEquals(2, simulator.btsPlayback(1, 1).playback().cycleNo()); long firstCycleTwoRecord = BtsExcelFixture.records().stream() .filter(record -> record.cycleNo() == 2).findFirst().orElseThrow().sequenceId(); assertEquals(firstCycleTwoRecord, simulator.btsPlayback(1, 1).playback().nextSequence()); Instant jumpedAt = simulator.btsPlayback(1, 1).playback().wallClockAt(); simulator.advanceBts(jumpedAt.plusSeconds(60)); assertEquals(2, simulator.btsRecords(1, 1).get(0).cycleNo()); simulator.setBtsSpeed(1, 1, 600); Instant wallClock = simulator.btsPlayback(1, 1).playback().wallClockAt(); long durationMillis = BtsExcelFixture.record(BtsExcelFixture.recordCount()).offsetSeconds() * 1000L; simulator.advanceBts(wallClock.plusMillis(durationMillis / 600L + 1000L)); assertTrue(simulator.btsPlayback(1, 1).playback().naturallyFinished()); simulator.resetBts(1, 1); assertEquals(0, simulator.btsRecords(1, 1).size()); assertEquals(1, simulator.btsPlayback(1, 1).playback().cycleNo()); } try (BtsDevelopmentSimulator restored = new BtsDevelopmentSimulator(0, store)) { assertEquals(0, restored.btsRecords(1, 1).size()); assertEquals(1, restored.btsPlayback(1, 1).playback().cycleNo()); } } @Test void playbackFollowsExcelFixtureThroughAllCyclesAndRecords() throws Exception { Instant start = Instant.parse("2026-08-31T00:00:00Z"); try (BtsDevelopmentSimulator simulator = new BtsDevelopmentSimulator(0, new InMemoryBtsSimulatorStateStore())) { simulator.updateChannel(1, 1, true, DeviceWorkStatus.WORKING); simulator.advanceBts(start); simulator.setBtsSpeed(1, 1, 600); Instant wallClock = simulator.btsPlayback(1, 1).playback().wallClockAt(); long durationMillis = BtsExcelFixture.record(BtsExcelFixture.recordCount()).offsetSeconds() * 1000L; simulator.advanceBts(wallClock.plusMillis(durationMillis / 600L + 1000L)); var records = simulator.btsRecords(1, 1); assertEquals(7605, records.size()); assertEquals(7605L, records.get(records.size() - 1).sequenceId()); assertEquals(33, records.get(records.size() - 1).cycleNo()); assertEquals(33, simulator.btsCycles(1, 1).size()); assertEquals(129, simulator.btsSteps(1, 1).size()); assertTrue(simulator.btsPlayback(1, 1).playback().naturallyFinished()); } } }