import { describe, expect, test } from "bun:test"; import { computeRollupRecomputeSince, toSyncUploadError } from "./engine"; describe("computeRollupRecomputeSince", () => { test("explicit date range recomputes from the start date", () => { const since = computeRollupRecomputeSince({ startDate: "2026-01-15" }); expect(since).toBe(new Date("2026-01-15T00:00:00").toISOString()); }); test("incremental sync recomputes from the checkpoint", () => { const checkpoint = new Date("2026-06-01T12:34:56.000Z"); const since = computeRollupRecomputeSince({ incrementalSince: checkpoint }); expect(since).toBe(checkpoint.toISOString()); }); test("full sync recomputes from the epoch, not a 24h lookback", () => { // No startDate, no checkpoint = full / first-ever sync. Must cover the // entire historical range so older rollup buckets aren't dropped from the // rollup-backed hub timeline (OPL-2667). const since = computeRollupRecomputeSince({}); expect(since).toBe(new Date(0).toISOString()); }); test("full sync with a null checkpoint also recomputes from the epoch", () => { const since = computeRollupRecomputeSince({ incrementalSince: null }); expect(since).toBe(new Date(0).toISOString()); }); test("start date takes precedence over a checkpoint", () => { const since = computeRollupRecomputeSince({ startDate: "2026-03-10", incrementalSince: new Date("2026-06-01T00:00:00.000Z"), }); expect(since).toBe(new Date("2026-03-10T00:00:00").toISOString()); }); test("never returns a recent 24h-window timestamp for a full sync", () => { const since = new Date(computeRollupRecomputeSince({})); const dayAgo = Date.now() - 24 * 60 * 60 * 1000; expect(since.getTime()).toBeLessThan(dayAgo); }); }); describe("toSyncUploadError", () => { test("returns the clean team-membership error", () => { const error = toSyncUploadError( 422, JSON.stringify({ error: "NO_TEAM", message: "raw server message" }), "Projects upload failed", ); expect(error.message).toBe( "You must create or join a team on the hosted hub before syncing.", ); expect(error.code).toBe("NO_TEAM"); expect(error.upgradeUrl).toBeUndefined(); }); test("preserves the subscription message and upgrade URL", () => { const error = toSyncUploadError( 402, JSON.stringify({ error: "SUBSCRIPTION_REQUIRED", message: "Multi-device sync requires a paid plan.", details: { upgradeUrl: "https://getscribe.ai/pricing" }, }), "Projects upload failed", ); expect(error.message).toBe("Multi-device sync requires a paid plan."); expect(error.code).toBe("SUBSCRIPTION_REQUIRED"); expect(error.upgradeUrl).toBe("https://getscribe.ai/pricing"); }); test("treats an unstructured 402 as subscription-required", () => { const error = toSyncUploadError(402, "not-json", "Projects upload failed"); expect(error.message).toBe("Multi-device sync requires a paid plan."); expect(error.code).toBe("SUBSCRIPTION_REQUIRED"); }); test("uses a parsed message without exposing the raw response", () => { const rawBody = JSON.stringify({ error: "SERVER_ERROR", message: "The hub is temporarily unavailable.", internal: { trace: "sensitive" }, }); const error = toSyncUploadError(503, rawBody, "Projects upload failed"); expect(error.message).toBe( "Projects upload failed: The hub is temporarily unavailable.", ); expect(error.message).not.toContain(rawBody); expect(error.code).toBe("UPLOAD_FAILED"); }); });