/** * earth-now.co * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * File: packages/counter/test/value.test.ts * Purpose: Golden tests for counterValue/rateAt across all RateFunction kinds at precise instants t */ import { describe, expect, it } from "vitest"; import { type CounterModel, counterValue, rateAt, startOfUtcDay, startOfUtcYear, windowValue, YEAR_SECONDS, SEASONAL_EPOCH_MS, } from "../src/index"; const T0 = "2026-01-01T00:00:00.000Z"; const t0 = Date.parse(T0); function base(over: Partial): CounterModel { return { metricId: "test_metric", anchorValue: 0, anchorTime: T0, rateFn: { kind: "linear", perSecond: 1 }, observedAt: T0, sourceId: "test_source", modelVersion: "test-v1", displayHints: { decimals: 0, unit: "units" }, ...over, }; } describe("linear", () => { it("value(anchorTime) === anchorValue", () => { const m = base({ anchorValue: 42 }); expect(counterValue(m, t0)).toBe(42); }); it("advances by perSecond", () => { const m = base({ rateFn: { kind: "linear", perSecond: 2.5 } }); expect(counterValue(m, t0 + 10_000)).toBeCloseTo(25, 10); expect(counterValue(m, t0 - 4_000)).toBeCloseTo(-10, 10); expect(rateAt(m, t0)).toBe(2.5); }); }); describe("piecewise", () => { const m = base({ anchorValue: 100, rateFn: { kind: "piecewise", segments: [ { from: "2026-01-01T00:00:00.000Z", perSecond: 1 }, { from: "2026-01-01T00:01:00.000Z", perSecond: 3 }, ], }, }); it("integrates across the junction", () => { // 60 s at 1/s + 30 s at 3/s = 60 + 90. expect(counterValue(m, t0 + 90_000)).toBeCloseTo(250, 10); }); it("is continuous at the junction", () => { const eps = 1; const before = counterValue(m, t0 + 60_000 - eps); const after = counterValue(m, t0 + 60_000 + eps); expect(Math.abs(after - before)).toBeLessThan(0.01); }); it("reports the active segment's rate", () => { expect(rateAt(m, t0 + 30_000)).toBe(1); expect(rateAt(m, t0 + 61_000)).toBe(3); }); }); describe("seasonal", () => { const m = base({ anchorValue: 1000, rateFn: { kind: "seasonal", base: 2, harmonics: [{ period: "year", order: 1, amplitude: 1, phase: 0 }], }, }); it("value(anchorTime) === anchorValue", () => { expect(counterValue(m, t0)).toBeCloseTo(1000, 9); }); it("harmonic integrates to ~zero over a full year (only base remains)", () => { const oneYearMs = YEAR_SECONDS * 1000; const v = counterValue(m, t0 + oneYearMs); expect(v).toBeCloseTo(1000 + 2 * YEAR_SECONDS, 4); }); it("rate oscillates around base with the harmonic amplitude", () => { // At the seasonal epoch itself, cos(0 + 0) = 1 → rate = base + amplitude. expect(rateAt(m, SEASONAL_EPOCH_MS)).toBeCloseTo(3, 9); }); }); describe("seasonal week period", () => { const m = base({ rateFn: { kind: "seasonal", base: 10, harmonics: [{ period: "week", order: 1, amplitude: 2, phase: 0 }], }, }); it("weekly harmonic integrates to ~zero over a full week", () => { const oneWeekMs = 604_800_000; expect(counterValue(m, t0 + oneWeekMs)).toBeCloseTo(10 * 604_800, 4); }); it("weekly phase is anchored to Saturday 00:00 UTC (the 2000-01-01 epoch)", () => { // 2026-01-03 is a Saturday: cos(0) = 1 → rate = base + amplitude. expect(rateAt(m, Date.parse("2026-01-03T00:00:00Z"))).toBeCloseTo(12, 9); // Half a week later (Tue 12:00): cos(π) = −1 → base − amplitude. expect(rateAt(m, Date.parse("2026-01-06T12:00:00Z"))).toBeCloseTo(8, 9); }); }); describe("spline (PCHIP)", () => { const m = base({ anchorValue: 20, anchorTime: "2026-01-02T00:00:00.000Z", rateFn: { kind: "spline", knots: [ ["2026-01-01T00:00:00.000Z", 10], ["2026-01-02T00:00:00.000Z", 20], ["2026-01-03T00:00:00.000Z", 40], ["2026-01-04T00:00:00.000Z", 45], ], }, }); it("interpolates exactly at knots and anchor", () => { expect(counterValue(m, Date.parse("2026-01-01T00:00:00Z"))).toBeCloseTo(10, 9); expect(counterValue(m, Date.parse("2026-01-02T00:00:00Z"))).toBeCloseTo(20, 9); expect(counterValue(m, Date.parse("2026-01-04T00:00:00Z"))).toBeCloseTo(45, 9); }); it("never overshoots between monotone knots", () => { const a = Date.parse("2026-01-01T00:00:00Z"); const b = Date.parse("2026-01-04T00:00:00Z"); for (let i = 0; i <= 200; i++) { const v = counterValue(m, a + ((b - a) * i) / 200); expect(v).toBeGreaterThanOrEqual(10 - 1e-9); expect(v).toBeLessThanOrEqual(45 + 1e-9); } }); it("extrapolates linearly beyond the last knot", () => { const end = Date.parse("2026-01-04T00:00:00Z"); const slopeEnd = rateAt(m, end); const v = counterValue(m, end + 3_600_000); expect(v).toBeCloseTo(45 + slopeEnd * 3600, 6); }); }); describe("windows", () => { it("today/ytd windows subtract the model value at window start", () => { const m = base({ rateFn: { kind: "linear", perSecond: 1 } }); const t = Date.parse("2026-03-10T06:00:00Z"); expect(windowValue(m, t, "today")).toBeCloseTo(6 * 3600, 6); expect(windowValue(m, t, "ytd")).toBeCloseTo((t - t0) / 1000, 6); expect(windowValue(m, t, "session", t - 90_000)).toBeCloseTo(90, 9); }); it("UTC boundaries are correct", () => { const t = Date.parse("2026-03-10T06:07:08Z"); expect(new Date(startOfUtcDay(t)).toISOString()).toBe("2026-03-10T00:00:00.000Z"); expect(new Date(startOfUtcYear(t)).toISOString()).toBe("2026-01-01T00:00:00.000Z"); }); });