119 lines
4.1 KiB
TypeScript
119 lines
4.1 KiB
TypeScript
import { describe, expect, it } from "@suaveplan/testing/runner";
|
|
import type { Clock } from "@suaveplan/types";
|
|
import { TimeEncodingType } from "../types/license.js";
|
|
import { checkExpiration, dayNumberToDate, getDayNumber } from "./time.js";
|
|
|
|
describe("time helpers", () => {
|
|
it("NONE is never expired", { timeout: 5000 }, () => {
|
|
const result = checkExpiration(TimeEncodingType.NONE, 0);
|
|
expect(result.isExpired).toBe(false);
|
|
expect(result.expirationDate).toBeUndefined();
|
|
});
|
|
|
|
it("ABSOLUTE_DAYS expires after its day number", { timeout: 5000 }, () => {
|
|
const day = 20000; // 2024-10-04ish
|
|
const refAfter = new Date((day + 1) * 86_400_000);
|
|
const refBefore = new Date((day - 1) * 86_400_000);
|
|
const after = checkExpiration(
|
|
TimeEncodingType.ABSOLUTE_DAYS,
|
|
day,
|
|
refAfter
|
|
);
|
|
const before = checkExpiration(
|
|
TimeEncodingType.ABSOLUTE_DAYS,
|
|
day,
|
|
refBefore
|
|
);
|
|
expect(after.isExpired).toBe(true);
|
|
expect(after.expirationDate?.getTime()).toBe(day * 86_400_000);
|
|
expect(before.isExpired).toBe(false);
|
|
});
|
|
|
|
it("RELATIVE_DAYS uses creationDate baseline", { timeout: 5000 }, () => {
|
|
const creation = new Date("2026-01-01T00:00:00Z");
|
|
const refAfter = new Date("2026-01-15T00:00:01Z"); // 14 days + 1s
|
|
const refBefore = new Date("2026-01-10T00:00:00Z");
|
|
const after = checkExpiration(
|
|
TimeEncodingType.RELATIVE_DAYS,
|
|
14,
|
|
refAfter,
|
|
creation
|
|
);
|
|
const before = checkExpiration(
|
|
TimeEncodingType.RELATIVE_DAYS,
|
|
14,
|
|
refBefore,
|
|
creation
|
|
);
|
|
expect(after.isExpired).toBe(true);
|
|
expect(before.isExpired).toBe(false);
|
|
});
|
|
|
|
it("RELATIVE_DAYS falls back to referenceDate when no creationDate", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const ref = new Date("2026-01-01T00:00:00Z");
|
|
const r = checkExpiration(TimeEncodingType.RELATIVE_DAYS, 5, ref);
|
|
expect(r.isExpired).toBe(false);
|
|
expect(r.expirationDate?.getTime()).toBe(
|
|
ref.getTime() + 5 * 86_400_000
|
|
);
|
|
});
|
|
|
|
it("ABSOLUTE_TIMESTAMP expires after its second", { timeout: 5000 }, () => {
|
|
const ts = 1_700_000_000; // a moment in 2023
|
|
const before = checkExpiration(
|
|
TimeEncodingType.ABSOLUTE_TIMESTAMP,
|
|
ts,
|
|
new Date((ts - 10) * 1000)
|
|
);
|
|
const after = checkExpiration(
|
|
TimeEncodingType.ABSOLUTE_TIMESTAMP,
|
|
ts,
|
|
new Date((ts + 10) * 1000)
|
|
);
|
|
expect(before.isExpired).toBe(false);
|
|
expect(after.isExpired).toBe(true);
|
|
});
|
|
|
|
it("uses injected Clock when no referenceDate supplied", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const fixed = 1_000_000_000_000; // 2001-09-09T01:46:40Z
|
|
const clock: Clock = () => fixed;
|
|
// ABSOLUTE_DAYS at day 11574 (which equals fixed/MS_PER_DAY=11574)
|
|
const day = Math.floor(fixed / 86_400_000);
|
|
const exactNow = checkExpiration(
|
|
TimeEncodingType.ABSOLUTE_DAYS,
|
|
day - 1,
|
|
undefined,
|
|
undefined,
|
|
clock
|
|
);
|
|
expect(exactNow.isExpired).toBe(true);
|
|
});
|
|
|
|
it("default unknown encoding falls through to non-expired", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const r = checkExpiration(99 as TimeEncodingType, 0);
|
|
expect(r.isExpired).toBe(false);
|
|
});
|
|
|
|
it("getDayNumber uses provided date", { timeout: 5000 }, () => {
|
|
expect(getDayNumber(new Date(0))).toBe(0);
|
|
expect(getDayNumber(new Date(86_400_000))).toBe(1);
|
|
});
|
|
|
|
it("getDayNumber uses injected clock when no date", {
|
|
timeout: 5000,
|
|
}, () => {
|
|
const clock: Clock = () => 5 * 86_400_000;
|
|
expect(getDayNumber(undefined, clock)).toBe(5);
|
|
});
|
|
|
|
it("dayNumberToDate inverts getDayNumber", { timeout: 5000 }, () => {
|
|
expect(dayNumberToDate(10).getTime()).toBe(10 * 86_400_000);
|
|
});
|
|
});
|