import { describe, expect, it } from "bun:test"; import { StateInterpolator } from "../stateInterpolation"; type NumericState = { value: number }; const interpolateNumber = (a: NumericState, b: NumericState, t: number): NumericState => ({ value: a.value + (b.value - a.value) * t, }); describe("StateInterpolator", () => { it("interpolates between history entries", () => { let now = 0; const interpolator = new StateInterpolator( { state: { value: 0 }, timestamp: 0 }, { interpolate: interpolateNumber, getNow: () => now, interpolationDelayMs: 50 } ); interpolator.addConfirmedState({ value: 10 }, { timestamp: 50 }); interpolator.addConfirmedState({ value: 20 }, { timestamp: 100 }); now = 130; // targetTime=80, between timestamps 50 and 100 => 60% toward 20 expect(interpolator.getRenderState().value).toBeCloseTo(16); }); it("resets history when requested", () => { let now = 0; const interpolator = new StateInterpolator( { state: { value: 0 }, timestamp: 0 }, { interpolate: interpolateNumber, getNow: () => now, interpolationDelayMs: 0 } ); interpolator.addConfirmedState({ value: 10 }, { timestamp: 50 }); interpolator.addConfirmedState( { value: 20 }, { timestamp: 100, resetHistory: true } ); now = 150; // targetTime=150, only latest history entry should remain after reset expect(interpolator.getHistory().length).toBe(1); expect(interpolator.getRenderState().value).toBe(20); }); it("lightly blends toward the next predicted invocation", () => { let now = 0; const interpolator = new StateInterpolator( { state: { value: 0 }, timestamp: 0, invocation: 0 }, { interpolate: interpolateNumber, getNow: () => now, interpolationDelayMs: 50, maxPredictionBlend: 0.35, } ); interpolator.addConfirmedState( { value: 10 }, { timestamp: 100, invocation: 1 } ); interpolator.setPrediction({ state: { value: 20 }, invocation: 2, timestamp: 150, }); now = 200; // targetTime=150 blends 35% toward prediction expect(interpolator.getRenderState().value).toBeCloseTo(13.5); }); });