import { test, expect, describe } from "bun:test"; import { computeApplyPlan, computeExpiry, insertIntoStack } from "./monitor"; const day = 86400000; const now = new Date("2026-03-24T00:00:00Z"); function daysFromNow(d: number) { return new Date(now.getTime() + d * day); } describe("insertIntoStack", () => { test("inserts into empty stack", () => { expect(insertIntoStack([], { plan: "pro", remaining_days: 20 })) .toEqual([{ plan: "pro", remaining_days: 20 }]); }); test("maintains tier-descending order", () => { const stack = [{ plan: "pro2x", remaining_days: 10 }]; expect(insertIntoStack(stack, { plan: "pro4x", remaining_days: 5 })) .toEqual([{ plan: "pro4x", remaining_days: 5 }, { plan: "pro2x", remaining_days: 10 }]); }); test("inserts lower tier after higher", () => { const stack = [{ plan: "pro2x", remaining_days: 10 }]; expect(insertIntoStack(stack, { plan: "pro", remaining_days: 30 })) .toEqual([{ plan: "pro2x", remaining_days: 10 }, { plan: "pro", remaining_days: 30 }]); }); test("merges same plan by adding days", () => { const stack = [{ plan: "pro", remaining_days: 20 }]; expect(insertIntoStack(stack, { plan: "pro", remaining_days: 30 })) .toEqual([{ plan: "pro", remaining_days: 50 }]); }); test("merges same plan - null wins (lifetime)", () => { const stack = [{ plan: "lifetime", remaining_days: null }]; expect(insertIntoStack(stack, { plan: "lifetime", remaining_days: null })) .toEqual([{ plan: "lifetime", remaining_days: null }]); }); test("merges timed into permanent → permanent", () => { const stack = [{ plan: "pro", remaining_days: 20 }]; // This shouldn't happen in practice but tests null-wins logic expect(insertIntoStack(stack, { plan: "pro", remaining_days: null })) .toEqual([{ plan: "pro", remaining_days: null }]); }); }); describe("computeApplyPlan", () => { test("free user buys Pro 1mo", () => { const acc = { plan: "free", plan_expires_at: null, plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "pro", months: 1 }, now); expect(result.plan).toBe("pro"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 30 * day); expect(result.plan_stack).toEqual([]); }); test("pro user renews same plan", () => { const expires = daysFromNow(20); const acc = { plan: "pro", plan_expires_at: expires, plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "pro", months: 1 }, now); expect(result.plan).toBe("pro"); expect(result.plan_expires_at!.getTime()).toBe(expires.getTime() + 30 * day); expect(result.plan_stack).toEqual([]); }); test("pro (20d left) upgrades to pro2x 1mo", () => { const acc = { plan: "pro", plan_expires_at: daysFromNow(20), plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "pro2x", months: 1 }, now); expect(result.plan).toBe("pro2x"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 30 * day); expect(result.plan_stack).toEqual([{ plan: "pro", remaining_days: 20 }]); }); test("pro2x (10d left) upgrades to pro4x, existing pro base", () => { const acc = { plan: "pro2x", plan_expires_at: daysFromNow(10), plan_stack: [{ plan: "pro", remaining_days: 20 }] }; const result = computeApplyPlan(acc, { plan: "pro4x", months: 1 }, now); expect(result.plan).toBe("pro4x"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 30 * day); // pro2x (tier 2) should be before pro (tier 1) in stack expect(result.plan_stack).toEqual([ { plan: "pro2x", remaining_days: 10 }, { plan: "pro", remaining_days: 20 }, ]); }); test("lifetime user buys pro2x 1mo", () => { const acc = { plan: "lifetime", plan_expires_at: null, plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "pro2x", months: 1 }, now); expect(result.plan).toBe("pro2x"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 30 * day); expect(result.plan_stack).toEqual([{ plan: "lifetime", remaining_days: null }]); }); test("pro4x (15d left) buys lifetime - goes to stack", () => { const acc = { plan: "pro4x", plan_expires_at: daysFromNow(15), plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "lifetime", months: null }, now); expect(result.plan).toBe("pro4x"); // stays active (higher tier) expect(result.plan_expires_at!.getTime()).toBe(daysFromNow(15).getTime()); expect(result.plan_stack).toEqual([{ plan: "lifetime", remaining_days: null }]); }); test("pro2x (10d left) buys pro 1mo with lifetime in stack", () => { const acc = { plan: "pro2x", plan_expires_at: daysFromNow(10), plan_stack: [{ plan: "lifetime", remaining_days: null }] }; const result = computeApplyPlan(acc, { plan: "pro", months: 1 }, now); expect(result.plan).toBe("pro2x"); // stays active expect(result.plan_stack).toEqual([ { plan: "pro", remaining_days: 30 }, { plan: "lifetime", remaining_days: null }, ]); }); test("expired pro activates new plan without saving expired", () => { const acc = { plan: "pro", plan_expires_at: daysFromNow(-5), plan_stack: [] }; const result = computeApplyPlan(acc, { plan: "pro2x", months: 1 }, now); expect(result.plan).toBe("pro2x"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 30 * day); expect(result.plan_stack).toEqual([]); }); test("free with existing stack - preserves stack", () => { const acc = { plan: "free", plan_expires_at: null, plan_stack: [{ plan: "lifetime", remaining_days: null }] }; const result = computeApplyPlan(acc, { plan: "pro", months: 1 }, now); expect(result.plan).toBe("pro"); expect(result.plan_stack).toEqual([{ plan: "lifetime", remaining_days: null }]); }); test("buying same lower tier twice merges days", () => { const acc = { plan: "pro2x", plan_expires_at: daysFromNow(20), plan_stack: [{ plan: "pro", remaining_days: 15 }] }; const result = computeApplyPlan(acc, { plan: "pro", months: 1 }, now); expect(result.plan).toBe("pro2x"); // stays expect(result.plan_stack).toEqual([{ plan: "pro", remaining_days: 45 }]); // 15 + 30 }); }); describe("computeExpiry", () => { test("returns null for non-pro plans", () => { expect(computeExpiry({ plan: "free", plan_expires_at: null, plan_stack: [] }, now)).toBeNull(); expect(computeExpiry({ plan: "lifetime", plan_expires_at: null, plan_stack: [] }, now)).toBeNull(); }); test("returns null for non-expired pro", () => { const acc = { plan: "pro", plan_expires_at: daysFromNow(10), plan_stack: [] }; expect(computeExpiry(acc, now)).toBeNull(); }); test("expired pro with empty stack → free", () => { const acc = { plan: "pro", plan_expires_at: daysFromNow(-1), plan_stack: [] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("free"); expect(result.plan_expires_at).toBeNull(); expect(result.plan_stack).toEqual([]); }); test("expired pro with lifetime in stack → promote lifetime", () => { const acc = { plan: "pro2x", plan_expires_at: daysFromNow(-1), plan_stack: [{ plan: "lifetime", remaining_days: null }] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("lifetime"); expect(result.plan_expires_at).toBeNull(); expect(result.plan_stack).toEqual([]); }); test("expired pro with timed base → promote with computed expiry", () => { const acc = { plan: "pro2x", plan_expires_at: daysFromNow(-1), plan_stack: [{ plan: "pro", remaining_days: 20 }] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("pro"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 20 * day); expect(result.plan_stack).toEqual([]); }); test("expired pro with multi-layer stack → promotes top, keeps rest", () => { const acc = { plan: "pro4x", plan_expires_at: daysFromNow(-1), plan_stack: [ { plan: "pro2x", remaining_days: 15 }, { plan: "lifetime", remaining_days: null }, ] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("pro2x"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 15 * day); expect(result.plan_stack).toEqual([{ plan: "lifetime", remaining_days: null }]); }); test("expired pro skips zero-day layers", () => { const acc = { plan: "pro4x", plan_expires_at: daysFromNow(-1), plan_stack: [ { plan: "pro2x", remaining_days: 0 }, { plan: "pro", remaining_days: 10 }, ] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("pro"); expect(result.plan_expires_at!.getTime()).toBe(now.getTime() + 10 * day); expect(result.plan_stack).toEqual([]); }); test("expired pro with all zero-day layers → free", () => { const acc = { plan: "pro", plan_expires_at: daysFromNow(-1), plan_stack: [{ plan: "pro2x", remaining_days: 0 }] }; const result = computeExpiry(acc, now)!; expect(result.plan).toBe("free"); expect(result.plan_stack).toEqual([]); }); });