idle fleet slows world clock to real time instead of freezing (ISS-128)
CI / build + unit + e2e (pull_request) Successful in 10m32s
CI / build + unit + e2e (pull_request) Successful in 10m32s
This commit is contained in:
+28
-11
@@ -16,6 +16,7 @@ import {
|
||||
deliverPackages,
|
||||
expirePackages,
|
||||
finishRepairs,
|
||||
IDLE_WORLD_TIME_SCALE,
|
||||
MAX_TICK_ELAPSED_MS,
|
||||
reputationGainFor,
|
||||
shouldAdvanceTime,
|
||||
@@ -30,8 +31,9 @@ import {
|
||||
// public tickNow()/setTestTimeScale() wrappers (singleton + lock) are covered by the e2e suite
|
||||
// (every spec's setScale helper hits /api/test/set-timescale; sim.spec.ts pins its validation).
|
||||
|
||||
// ISS-117: fresh DBs seed a GROUNDED starter, so any test that needs an advancing world must put
|
||||
// a plane in flight first — departureSimTime at the game-clock origin keeps its arrival hours away.
|
||||
// ISS-117 / ISS-128: fresh DBs seed a GROUNDED starter, so any test that needs FULL-scale advance
|
||||
// must put a plane in flight first (a grounded fleet only advances at the idle real-time rate) —
|
||||
// departureSimTime at the game-clock origin keeps its arrival hours away.
|
||||
async function makeWorldActive(inst: Awaited<ReturnType<typeof openGameDb>>) {
|
||||
await inst.db.update(schema.planes)
|
||||
.set({ status: 'flying', destIcao: 'LEMD', departureSimTime: STARTING_CLOCK_MS })
|
||||
@@ -60,7 +62,7 @@ async function freshDb(name: string) {
|
||||
describe('tickOnce — clock advance', () => {
|
||||
it('advances the clock by elapsed real ms × timeScale', async () => {
|
||||
const inst = await freshDb('scale.db');
|
||||
await makeWorldActive(inst); // a grounded fleet would freeze the world (ISS-117)
|
||||
await makeWorldActive(inst); // a grounded fleet would slow the world to the idle real-time rate (ISS-128)
|
||||
vi.setSystemTime(T0 + 1_000); // one real second later
|
||||
await inst.db.update(schema.simState).set({ timeScale: 5 });
|
||||
|
||||
@@ -70,12 +72,12 @@ describe('tickOnce — clock advance', () => {
|
||||
expect(sim.lastRealTickMs).toBe(T0 + 1_000);
|
||||
});
|
||||
|
||||
it('freezes the clock while the fleet is fully grounded but keeps tracking real time', async () => {
|
||||
const inst = await freshDb('idle.db'); // seeded starter is on the ground → idle (ISS-117)
|
||||
it('a fully grounded fleet advances at the idle real-time rate, not frozen (ISS-128)', async () => {
|
||||
const inst = await freshDb('idle.db'); // seeded starter is on the ground → idle
|
||||
|
||||
vi.setSystemTime(T0 + 3_600_000); // an hour of real idleness
|
||||
let sim = await tickOnce(inst);
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS); // frozen — no advance at all
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS + MAX_TICK_ELAPSED_MS * IDLE_WORLD_TIME_SCALE); // clamped to one idle-rate tick — no fast-forward, no freeze
|
||||
expect(sim.lastRealTickMs).toBe(T0 + 3_600_000);
|
||||
|
||||
// A plane takes off: resumes from where it stopped, with NO catch-up for the idle time.
|
||||
@@ -83,7 +85,17 @@ describe('tickOnce — clock advance', () => {
|
||||
await makeWorldActive(inst);
|
||||
vi.setSystemTime(T0 + 3_600_500);
|
||||
sim = await tickOnce(inst);
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS + 500 * 60); // only the 500 ms since activity × 60
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS + MAX_TICK_ELAPSED_MS * IDLE_WORLD_TIME_SCALE + 500 * 60); // only the 500 ms since activity × 60
|
||||
});
|
||||
|
||||
it('a grounded fleet ignores any raised timeScale — the idle rate wins (ISS-128)', async () => {
|
||||
const inst = await freshDb('idle-scale.db');
|
||||
await inst.db.update(schema.simState).set({ timeScale: 1_000 }); // FLIGHT_TEST_HOOKS-style override
|
||||
|
||||
vi.setSystemTime(T0 + 1_000);
|
||||
const sim = await tickOnce(inst);
|
||||
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS + 1_000 * IDLE_WORLD_TIME_SCALE); // real time, NOT ×1000
|
||||
});
|
||||
|
||||
it('clamps long absences: a 1-hour gap advances at most MAX_TICK_ELAPSED_MS × scale', async () => {
|
||||
@@ -574,7 +586,12 @@ describe('finishRepairs (v0.39.0, ISS-069)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('idle-pause — the world freezes when nothing is happening (ISS-117)', () => {
|
||||
describe('idle slowdown — a grounded fleet slows the world to real time (ISS-128)', () => {
|
||||
it('pins IDLE_WORLD_TIME_SCALE = WORLD_TIME_SCALE / 24, i.e. exactly real time', () => {
|
||||
expect(IDLE_WORLD_TIME_SCALE).toBe(WORLD_TIME_SCALE / 24);
|
||||
expect(IDLE_WORLD_TIME_SCALE).toBe(1); // one game-second per real second
|
||||
});
|
||||
|
||||
it('shouldAdvanceTime: grounded/empty fleets are idle; any flying or repairing plane advances', () => {
|
||||
expect(shouldAdvanceTime([])).toEqual({ advance: false, flying: 0, repairing: 0 });
|
||||
expect(shouldAdvanceTime([{ status: 'available' }, { status: 'available' }])).toEqual({ advance: false, flying: 0, repairing: 0 });
|
||||
@@ -583,14 +600,14 @@ describe('idle-pause — the world freezes when nothing is happening (ISS-117)',
|
||||
expect(shouldAdvanceTime([{ status: 'flying' }, { status: 'repairing' }])).toEqual({ advance: true, flying: 1, repairing: 1 });
|
||||
});
|
||||
|
||||
it('a fully grounded fleet freezes the clock but keeps tracking real time', async () => {
|
||||
it('a fully grounded fleet advances at the idle rate but keeps tracking real time', async () => {
|
||||
const inst = await freshDb('idle-pause.db'); // seeded starter is on the ground → idle
|
||||
vi.setSystemTime(T0 + 3_600_000);
|
||||
|
||||
const sim = await tickOnce(inst);
|
||||
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS); // frozen — no advance at all
|
||||
expect(sim.lastRealTickMs).toBe(T0 + 3_600_000); // …but the real-time anchor moved on
|
||||
expect(sim.clockMs).toBe(STARTING_CLOCK_MS + MAX_TICK_ELAPSED_MS * IDLE_WORLD_TIME_SCALE); // real-time rate, clamped — not frozen
|
||||
expect(sim.lastRealTickMs).toBe(T0 + 3_600_000); // …and the real-time anchor moved on
|
||||
});
|
||||
|
||||
it('a repairing plane keeps the world advancing', async () => {
|
||||
|
||||
Reference in New Issue
Block a user