import { describe, expect, test } from "bun:test"; import { formatRecurrenceText, getRecurrencePreview, parseRecurrenceRule, serializeRecurrenceRule, validateRecurrence, } from "@/lib/recurrence"; describe("recurrence helpers", () => { test("serializes a weekly weekday recurrence into an RFC5545 RRULE string", () => { const rule = serializeRecurrenceRule({ freq: "WEEKLY", interval: 2, byDay: ["TU", "TH"], until: "2026-06-30", }); expect(rule).toBe("FREQ=WEEKLY;INTERVAL=2;UNTIL=20260630T235959Z;BYDAY=TU,TH"); }); test("rehydrates an existing RRULE string into picker-friendly values", () => { const parsed = parseRecurrenceRule( "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE,FR;COUNT=6", ); expect(parsed).toEqual({ freq: "WEEKLY", interval: 1, byDay: ["MO", "WE", "FR"], count: 6, until: undefined, }); }); test("returns a preview of upcoming recurrence instances from the public helper", () => { const preview = getRecurrencePreview( { freq: "MONTHLY", interval: 1, byDay: [], count: 3, }, "2026-04-09T10:00:00.000Z", ); expect(preview).toHaveLength(3); expect(preview[0]).toContain("2026-04-09T10:00:00.000Z"); expect(preview[1]).toContain("2026-05-09T10:00:00.000Z"); expect(preview[2]).toContain("2026-06-09T10:00:00.000Z"); }); test("rejects invalid recurrence endings that mix count and until", () => { const result = validateRecurrence({ freq: "DAILY", interval: 1, byDay: [], count: 5, until: "2026-05-01", }); expect(result.isValid).toBe(false); expect(result.errors.rule).toContain("either count or until"); }); test("rejects malformed count inputs", () => { const result = validateRecurrence({ freq: "MONTHLY", interval: 1, byDay: [], count: 0, }); expect(result.isValid).toBe(false); expect(result.errors.count).toContain("greater than 0"); }); test("formats supported recurrence strings into human text", () => { expect(formatRecurrenceText("FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,WE")).toContain( "every week", ); }); });