Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,157 @@
import { describe, expect, it } from "@suaveplan/testing/runner";
import * as fc from "fast-check";
import { formatRRule, parseRRule } from "./recurrence.js";
describe("parseRRule", () => {
it("parses FREQ=DAILY", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=DAILY");
expect(rule.freq).toBe("daily");
});
it("parses FREQ=WEEKLY", { timeout: 5000 }, () => {
expect(parseRRule("FREQ=WEEKLY").freq).toBe("weekly");
});
it("parses FREQ=MONTHLY", { timeout: 5000 }, () => {
expect(parseRRule("FREQ=MONTHLY").freq).toBe("monthly");
});
it("parses FREQ=YEARLY", { timeout: 5000 }, () => {
expect(parseRRule("FREQ=YEARLY").freq).toBe("yearly");
});
it("parses INTERVAL", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=WEEKLY;INTERVAL=2");
expect(rule.interval).toBe(2);
});
it("parses COUNT", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=DAILY;COUNT=5");
expect(rule.count).toBe(5);
});
it("parses UNTIL (YYYYMMDD)", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=DAILY;UNTIL=20241231");
expect(rule.until?.getFullYear()).toBe(2024);
expect(rule.until?.getMonth()).toBe(11);
expect(rule.until?.getDate()).toBe(31);
});
it("parses UNTIL with time suffix", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=DAILY;UNTIL=20241231T000000Z");
expect(rule.until?.getFullYear()).toBe(2024);
});
it("parses BYDAY", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=WEEKLY;BYDAY=MO,WE,FR");
expect(rule.byDay).toEqual([1, 3, 5]);
});
it("parses BYMONTHDAY", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=MONTHLY;BYMONTHDAY=15");
expect(rule.byMonthDay).toEqual([15]);
});
it("ignores parts without '=' sign", { timeout: 5000 }, () => {
// Covers the `if (eq === -1) continue` branch in the parser loop
const rule = parseRRule("FREQ=DAILY;BADPART;COUNT=3");
expect(rule.freq).toBe("daily");
expect(rule.count).toBe(3);
});
it("throws for missing FREQ", { timeout: 5000 }, () => {
let err: unknown;
try {
parseRRule("INTERVAL=2");
} catch (e) {
err = e;
}
expect((err as { code?: string }).code).toBe(
"DATETIME.PARSE.INVALID_INPUT"
);
});
it("throws for unknown FREQ", { timeout: 5000 }, () => {
let err: unknown;
try {
parseRRule("FREQ=HOURLY");
} catch (e) {
err = e;
}
expect((err as { code?: string }).code).toBe(
"DATETIME.PARSE.INVALID_INPUT"
);
});
it("handles all day abbreviations", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=WEEKLY;BYDAY=SU,MO,TU,WE,TH,FR,SA");
expect(rule.byDay).toEqual([0, 1, 2, 3, 4, 5, 6]);
});
});
describe("formatRRule", () => {
it("formats basic rule", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=DAILY;COUNT=5");
expect(formatRRule(rule)).toBe("FREQ=DAILY;COUNT=5");
});
it("formats UNTIL", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=WEEKLY;UNTIL=20241231");
const formatted = formatRRule(rule);
expect(formatted).toContain("UNTIL=20241231");
});
it("formats BYDAY", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=WEEKLY;BYDAY=MO,WE,FR;COUNT=6");
const formatted = formatRRule(rule);
expect(formatted).toContain("BYDAY=MO,WE,FR");
expect(formatted).toContain("FR");
});
it("formats all day abbreviations round-trip", { timeout: 5000 }, () => {
const rule = parseRRule(
"FREQ=WEEKLY;BYDAY=SU,MO,TU,WE,TH,FR,SA;COUNT=1"
);
const formatted = formatRRule(rule);
expect(formatted).toContain("SU");
expect(formatted).toContain("MO");
expect(formatted).toContain("TU");
expect(formatted).toContain("WE");
expect(formatted).toContain("TH");
expect(formatted).toContain("FR");
expect(formatted).toContain("SA");
});
it("formats BYMONTHDAY", { timeout: 5000 }, () => {
const rule = parseRRule("FREQ=MONTHLY;BYMONTHDAY=15;COUNT=3");
expect(formatRRule(rule)).toContain("BYMONTHDAY=15");
});
it("formats INTERVAL", { timeout: 5000 }, () => {
// Ensures the `if (rule.interval !== undefined)` branch is taken
const rule = parseRRule("FREQ=WEEKLY;INTERVAL=2;COUNT=4");
const formatted = formatRRule(rule);
expect(formatted).toContain("INTERVAL=2");
});
it("parseRRule → formatRRule → parseRRule identity (property test)", {
timeout: 30000,
}, () => {
const freqs = ["DAILY", "WEEKLY", "MONTHLY", "YEARLY"] as const;
fc.assert(
fc.property(
fc.constantFrom(...freqs),
fc.integer({ min: 1, max: 10 }),
(freq, count) => {
const input = `FREQ=${freq};COUNT=${count}`;
const parsed = parseRRule(input);
const formatted = formatRRule(parsed);
const reparsed = parseRRule(formatted);
expect(reparsed.freq).toBe(parsed.freq);
expect(reparsed.count).toBe(parsed.count);
}
),
{ numRuns: 100 }
);
});
});