import { describe, expect, it } from 'vitest'; import { advanceSimClock, type SimClockAnchor } from './useSimClock'; import { IDLE_WORLD_TIME_SCALE, WORLD_TIME_SCALE } from './worldClock'; // ISS-007 (v0.16.1) — the pure extrapolation core of lib/useSimClock.ts. The hook itself is a // thin setInterval + re-sync wrapper covered by e2e (panes.spec.ts "ticks smoothly between polls"); // this suite pins the math: game time advances real-time × WORLD_TIME_SCALE, and slows to // real-time × IDLE_WORLD_TIME_SCALE while idle (ISS-128). const anchor = (over: Partial = {}): SimClockAnchor => ({ clockMs: 1_750_000_000_000, at: 1_000_000, advancing: true, ...over, }); describe('advanceSimClock', () => { it('advances by exactly elapsed real-time × WORLD_TIME_SCALE while running', () => { expect(advanceSimClock(anchor(), 1_001_000)).toBe(1_750_000_000_000 + 1_000 * WORLD_TIME_SCALE); // The e2e scenario: ~2.6 s real at the fixed world rate. expect(advanceSimClock(anchor(), 1_002_600)).toBe(1_750_000_000_000 + 2_600 * WORLD_TIME_SCALE); }); it('returns the anchor value exactly at zero elapsed', () => { expect(advanceSimClock(anchor(), 1_000_000)).toBe(1_750_000_000_000); }); it('advances at the idle real-time rate (IDLE_WORLD_TIME_SCALE) while grounded, not frozen (ISS-128)', () => { const a = anchor({ advancing: false }); expect(advanceSimClock(a, 1_000_000)).toBe(1_750_000_000_000); // zero elapsed — still the anchor value expect(advanceSimClock(a, 1_001_000)).toBe(1_750_000_000_000 + 1_000 * IDLE_WORLD_TIME_SCALE); // a full second later — real time, not frozen }); it('clamps to the anchor value when nowMs is before the anchor (clock skew), never backwards', () => { expect(advanceSimClock(anchor(), 999_999)).toBe(1_750_000_000_000); expect(advanceSimClock(anchor({ advancing: false }), 999_999)).toBe(1_750_000_000_000); }); it('mirrors the server tick model (tickOnce): advance = elapsed × WORLD_TIME_SCALE while actively polled', () => { // lib/sim/engine.ts: clockMs += min(now − lastRealTickMs, MAX_TICK_ELAPSED_MS) × timeScale. // For gaps under the 2 s clamp the client extrapolation is exactly the server's own formula. for (const gap of [1, 250, 999, 1_999]) { expect(advanceSimClock(anchor(), 1_000_000 + gap)).toBe(1_750_000_000_000 + gap * WORLD_TIME_SCALE); } }); });