Files
minecraft-account-manager/apps/web/src/lib/group-schedule.test.ts
T
dmg 6fa33c9f7b
CI / validate (push) Successful in 6m9s
Release / release (push) Successful in 7m51s
feat(admin): show group schedule status
2026-08-02 14:26:24 -04:00

124 lines
4.9 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
evaluateGroupSchedule,
groupScheduleStatus,
localWindowToUtc,
parseScheduleWindows,
utcWindowToLocal,
type WeeklyAccessWindow,
} from "./group-schedule";
const fridayEvening: WeeklyAccessWindow = {
startMinuteOfWeek: 4 * 24 * 60 + 20 * 60,
endMinuteOfWeek: 4 * 24 * 60 + 23 * 60 + 59,
};
describe("weekly group access schedules", () => {
it("summarizes whether a group has configured windows", () => {
expect(groupScheduleStatus(0)).toBe("Unrestricted");
expect(groupScheduleStatus(1)).toBe("1 window");
expect(groupScheduleStatus(3)).toBe("3 windows");
});
it("allows an enabled group at any time when no schedule is configured", () => {
expect(evaluateGroupSchedule([], new Date("2026-08-07T19:00:00Z"))).toEqual({
allowed: true,
nextWindow: null,
});
});
it("allows only inside a UTC window and identifies the next window when denied", () => {
expect(evaluateGroupSchedule([fridayEvening], new Date("2026-08-07T21:30:00Z")).allowed).toBe(true);
const denied = evaluateGroupSchedule([fridayEvening], new Date("2026-08-08T01:00:00Z"));
expect(denied.allowed).toBe(false);
expect(denied.nextWindow).toEqual({
start: new Date("2026-08-14T20:00:00.000Z"),
end: new Date("2026-08-14T23:59:00.000Z"),
});
});
it("uses inclusive starts and exclusive ends across the UTC week boundary", () => {
const sundayNight = { startMinuteOfWeek: 6 * 1440 + 23 * 60, endMinuteOfWeek: 2 * 60 };
expect(evaluateGroupSchedule([sundayNight], new Date("2026-08-09T23:00:00Z")).allowed).toBe(true);
expect(evaluateGroupSchedule([sundayNight], new Date("2026-08-10T01:59:59Z")).allowed).toBe(true);
expect(evaluateGroupSchedule([sundayNight], new Date("2026-08-10T02:00:00Z")).allowed).toBe(false);
});
it("selects the earliest upcoming window when several are configured", () => {
const mondayMorning = { startMinuteOfWeek: 8 * 60, endMinuteOfWeek: 9 * 60 };
const decision = evaluateGroupSchedule(
[fridayEvening, mondayMorning],
new Date("2026-08-08T01:00:00Z"),
);
expect(decision.nextWindow?.start).toEqual(new Date("2026-08-10T08:00:00.000Z"));
expect(decision.nextWindow?.end).toEqual(new Date("2026-08-10T09:00:00.000Z"));
});
it("fails closed for malformed persisted policy", () => {
expect(evaluateGroupSchedule([
{ startMinuteOfWeek: 100, endMinuteOfWeek: 200 },
{ startMinuteOfWeek: 150, endMinuteOfWeek: 250 },
], new Date("2026-08-03T02:30:00Z"))).toEqual({ allowed: false, nextWindow: null });
});
it("rejects malformed and overlapping submitted windows", () => {
const valid = new FormData();
valid.append("startMinuteOfWeek", "6960");
valid.append("endMinuteOfWeek", "7199");
valid.append("startMinuteOfWeek", "480");
valid.append("endMinuteOfWeek", "540");
expect(parseScheduleWindows(valid)).toEqual([
{ startMinuteOfWeek: 480, endMinuteOfWeek: 540 },
fridayEvening,
]);
const overlapping = new FormData();
overlapping.append("startMinuteOfWeek", "100");
overlapping.append("endMinuteOfWeek", "200");
overlapping.append("startMinuteOfWeek", "150");
overlapping.append("endMinuteOfWeek", "250");
expect(parseScheduleWindows(overlapping)).toBeNull();
const wrappingOverlap = new FormData();
wrappingOverlap.append("startMinuteOfWeek", String(6 * 1440 + 23 * 60));
wrappingOverlap.append("endMinuteOfWeek", String(2 * 60));
wrappingOverlap.append("startMinuteOfWeek", String(60));
wrappingOverlap.append("endMinuteOfWeek", String(3 * 60));
expect(parseScheduleWindows(wrappingOverlap)).toBeNull();
const mismatched = new FormData();
mismatched.append("startMinuteOfWeek", "100");
expect(parseScheduleWindows(mismatched)).toBeNull();
const invalid = new FormData();
invalid.append("startMinuteOfWeek", "10080");
invalid.append("endMinuteOfWeek", "0");
expect(parseScheduleWindows(invalid)).toBeNull();
for (const malformedValue of ["", " ", "+1", "0x10", "1e2", "1.5"]) {
const malformed = new FormData();
malformed.append("startMinuteOfWeek", malformedValue);
malformed.append("endMinuteOfWeek", "2");
expect(parseScheduleWindows(malformed)).toBeNull();
}
const tooMany = new FormData();
for (let index = 0; index < 51; index += 1) {
tooMany.append("startMinuteOfWeek", String(index * 2));
tooMany.append("endMinuteOfWeek", String(index * 2 + 1));
}
expect(parseScheduleWindows(tooMany)).toBeNull();
});
it("converts browser-local weekly values to authoritative UTC and back", () => {
const local = { startMinuteOfWeek: 4 * 1440 + 20 * 60, endMinuteOfWeek: 4 * 1440 + 23 * 60 };
const utc = localWindowToUtc(local, 420);
expect(utc).toEqual({
startMinuteOfWeek: 5 * 1440 + 3 * 60,
endMinuteOfWeek: 5 * 1440 + 6 * 60,
});
expect(utcWindowToLocal(utc, 420)).toEqual(local);
});
});