SPB Git forge

spb/polyllm

Public
15commits 1branches 0releases
2.2 MBsize
maindefault branch
13 days agolast push
TypeScript 97.4% SQL 1% JavaScript 0.9% CSS 0.6%
3.7 KB · 68 lines typescript
Raw Blame History
1import { describe, it, expect } from "vitest";2import { resolveRange, bucketKeys, bucketKey, previousPeriod, startOfDayInTz, addDays, observedDays, isValidTimeZone } from "@/lib/usage/time";34// 2026-09-11 22:30 in Montréal = 2026-09-12 02:30Z5const NOW = new Date("2026-09-12T02:30:00Z");6const TZ = "America/Toronto";78describe("usage time ranges", () => {9  it("resolves `today` to local midnight → next local midnight, hourly buckets", () => {10    const r = resolveRange({ range: "today", tz: TZ }, NOW);11    expect(r.from?.toISOString()).toBe("2026-09-11T04:00:00.000Z");12    expect(r.to.toISOString()).toBe("2026-09-12T04:00:00.000Z");13    expect(r.bucket).toBe("hour");14    expect(r.days).toBe(1);15    expect(bucketKeys(r.from!, r.to, "hour", TZ)).toHaveLength(24);16    expect(bucketKeys(r.from!, r.to, "hour", TZ)[0]).toBe("2026-09-11T00:00");17  });18  it("7d / 30d / 90d cover N whole local days ending today", () => {19    const r = resolveRange({ range: "7d", tz: TZ }, NOW);20    expect(bucketKey(r.from!, "day", TZ)).toBe("2026-09-05");21    expect(bucketKeys(r.from!, r.to, "day", TZ)).toEqual(["2026-09-05", "2026-09-06", "2026-09-07", "2026-09-08", "2026-09-09", "2026-09-10", "2026-09-11"]);22    expect(resolveRange({ range: "90d", tz: TZ }, NOW).days).toBe(90);23    expect(bucketKeys(resolveRange({ range: "90d", tz: TZ }, NOW).from!, NOW, "day", TZ)).toHaveLength(90);24  });25  it("custom ranges are inclusive of both dates, clamped to today and to one year", () => {26    const r = resolveRange({ range: "custom", from: "2026-08-01", to: "2026-08-03", tz: TZ }, NOW);27    expect(bucketKeys(r.from!, r.to, r.bucket, TZ)).toEqual(["2026-08-01", "2026-08-02", "2026-08-03"]);28    expect(r.days).toBe(3);29    const fut = resolveRange({ range: "custom", from: "2026-09-10", to: "2030-01-01", tz: TZ }, NOW);30    expect(fut.to.toISOString()).toBe("2026-09-12T04:00:00.000Z");31    const huge = resolveRange({ range: "custom", from: "2020-01-01", to: "2026-09-11", tz: TZ }, NOW);32    expect(huge.days).toBeLessThanOrEqual(366);33    const swapped = resolveRange({ range: "custom", from: "2026-08-10", to: "2026-08-01", tz: TZ }, NOW);34    expect(swapped.from! < swapped.to).toBe(true);35    // two days or less → hourly36    expect(resolveRange({ range: "custom", from: "2026-09-10", to: "2026-09-11", tz: TZ }, NOW).bucket).toBe("hour");37  });38  it("falls back to UTC / 30d on bad input", () => {39    const r = resolveRange({ range: "nope", tz: "Mars/Olympus" }, NOW);40    expect(r.key).toBe("30d");41    expect(r.tz).toBe("UTC");42    expect(isValidTimeZone("Europe/Paris")).toBe(true);43    expect(isValidTimeZone("../etc")).toBe(false);44    expect(isValidTimeZone("UTC")).toBe(true);45  });46  it("previous period has the same length and ends where the range starts", () => {47    const r = resolveRange({ range: "7d", tz: TZ }, NOW);48    const p = previousPeriod(r)!;49    expect(p.to.getTime()).toBe(r.from!.getTime());50    expect(r.to.getTime() - r.from!.getTime()).toBe(p.to.getTime() - p.from.getTime());51    expect(previousPeriod(resolveRange({ range: "all", tz: TZ }, NOW))).toBeNull();52  });53  it("handles DST when adding days", () => {54    // 2026-11-01 is the fall-back date in Toronto55    const oct31 = startOfDayInTz(new Date("2026-10-31T12:00:00Z"), TZ);56    const nov2 = addDays(oct31, 2, TZ);57    expect(bucketKey(nov2, "day", TZ)).toBe("2026-11-02");58    expect((nov2.getTime() - oct31.getTime()) / 3_600_000).toBe(49);59  });60  it("observed days is fractional for today and never below one hour", () => {61    const r = resolveRange({ range: "today", tz: TZ }, NOW);62    const d = observedDays(r, NOW);63    expect(d).toBeGreaterThan(0.9);64    expect(d).toBeLessThan(1);65    expect(observedDays(resolveRange({ range: "all", tz: TZ }, NOW), NOW, null)).toBeCloseTo(1 / 24, 5);66  });67});68