import { expect, it } from "vitest"; import type { JsonValue } from "../jsonValue.js"; import { containsDelimited, delimitedOffsets, formatClock, integerOf, isUuid, readClock, replaceDelimited, respellClock, seededToken, seededUuid, sequenceNumber, slugOf, spelledLike, type ClockFormat, } from "./environmentStateWriteValues.js"; const WHOLE_SECOND = Date.UTC(2026, 2, 4, 5, 6, 7); const WITH_MS = WHOLE_SECOND + 123; const V4_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/u; const UUID_EXEMPLAR = "123e4567-e89b-12d3-a456-426614174000"; const iso = ( separator: "T" | " ", fraction: number, zone: string, ): ClockFormat => ({ kind: "iso", separator, fraction, zone }); const seeds = (count: number): string[] => Array.from({ length: count }, (_, index) => `seed-${String(index)}`); it.each<[string, number, ClockFormat, number?]>([ ["2026-03-04T05:06:07Z", WHOLE_SECOND, iso("T", 0, "Z")], ["2026-03-04 05:06:07.123Z", WITH_MS, iso(" ", 3, "Z")], ["2026-03-04T05:06:07.123456Z", WITH_MS, iso("T", 6, "Z"), 456000], ["2026-03-04T07:06:07.123+02:00", WITH_MS, iso("T", 3, "+02:00")], ["2026-03-03T23:36:07-0530", WHOLE_SECOND, iso("T", 0, "-0530")], [ "2000-01-01T12:00:00+23:59", Date.UTC(1999, 11, 31, 12, 1), iso("T", 0, "+23:59"), ], ["2026-03-04T05:06:07", WHOLE_SECOND, iso("T", 0, "")], ["2024-02-29 00:00:00Z", Date.UTC(2024, 1, 29), iso(" ", 0, "Z")], ])( "reads the ISO clock %s with its spelling", (text, ms, format, subMillisecond = 0) => { expect(readClock(text)).toEqual({ ms, subMillisecond, format }); }, ); it.each([ "2026-02-30T00:00:00Z", "2026-02-29T00:00:00Z", "2026-13-01T00:00:00Z", "2026-03-04T24:00:00Z", "2026-03-04T05:60:00Z", "2026-03-04T05:06:60Z", "2026-03-04T05:06:07+00:60", "2026-03-04T05:06:07+24:00", "2026-03-04T05:06:07+99:99", "2026-03-04", "2026-03-04T05:06Z", "2026-03-04T05:06:07+05", "2026-03-04T05:06:07.1234567890Z", "2026-03-04t05:06:07Z", "next tuesday", ])("refuses %s as a clock", (text) => { expect(readClock(text)).toBeUndefined(); }); it.each<[JsonValue | undefined, ReturnType]>([ [ WHOLE_SECOND / 1000, { ms: WHOLE_SECOND, subMillisecond: 0, format: { kind: "epoch", unit: "s" }, }, ], [ WITH_MS, { ms: WITH_MS, subMillisecond: 0, format: { kind: "epoch", unit: "ms" } }, ], [999_999_999, undefined], [99_999_999_999, undefined], [10_000_000_000_000, undefined], [WHOLE_SECOND / 1000 + 0.5, undefined], [String(WHOLE_SECOND / 1000), undefined], [null, undefined], [undefined, undefined], ])("reads the epoch value %j", (value, expected) => { expect(readClock(value)).toEqual(expected); }); it.each([ "2026-03-04T05:06:07Z", "2026-03-04 05:06:07.123Z", "2026-03-04T07:06:07.123+02:00", "2026-03-03T23:36:07-0530", "2026-03-04T05:06:07.1", "2026-03-04T05:06:07.123000000", WHOLE_SECOND / 1000, WITH_MS, ])("formats %j back to its own spelling", (value) => { const clock = readClock(value)!; expect(formatClock(clock.ms, clock.format)).toBe(value); }); it.each<[ClockFormat, JsonValue]>([ [iso("T", 6, "Z"), "2026-03-04T05:06:07.123000Z"], [iso(" ", 9, ""), "2026-03-04 05:06:07.123000000"], [iso("T", 1, "Z"), "2026-03-04T05:06:07.1Z"], [iso("T", 0, "+02:00"), "2026-03-04T07:06:07+02:00"], [iso("T", 3, "-0530"), "2026-03-03T23:36:07.123-0530"], [{ kind: "epoch", unit: "s" }, WHOLE_SECOND / 1000], [{ kind: "epoch", unit: "ms" }, WITH_MS], ])("formats an instant as %j", (format, expected) => { expect(formatClock(WITH_MS, format)).toBe(expected); }); it.each<[JsonValue | undefined, boolean]>([ [UUID_EXEMPLAR, true], [UUID_EXEMPLAR.toUpperCase(), true], [UUID_EXEMPLAR.replaceAll("-", ""), false], [UUID_EXEMPLAR.slice(1), false], [`g${UUID_EXEMPLAR.slice(1)}`, false], [` ${UUID_EXEMPLAR}`, false], [42, false], [undefined, false], ])("isUuid(%j) is %s", (value, expected) => { expect(isUuid(value)).toBe(expected); }); it.each<[JsonValue | undefined, number | undefined]>([ [42, 42], [-3, -3], ["42", 42], ["007", 7], ["123456789012345", 123_456_789_012_345], ["1234567890123456", undefined], ["-3", undefined], ["4.2", undefined], [4.2, undefined], [2 ** 53, undefined], ["", undefined], [" 4", undefined], [true, undefined], [null, undefined], [undefined, undefined], ])("integerOf(%j) is %j", (value, expected) => { expect(integerOf(value)).toBe(expected); }); it("spells an integer as the recorded value spells integers", () => { expect(spelledLike("7", 8)).toBe("8"); expect(spelledLike(7, 8)).toBe(8); expect(spelledLike(null, 8)).toBe(8); }); it.each([ ["Hello World", "hello-world"], ["Héllo, Wörld!", "hello-world"], [" Crème brûlée -- recipe__v2 ", "creme-brulee-recipe-v2"], ["Fullwidth 2", "fullwidth-2"], ["already-a-slug", "already-a-slug"], ["--Edge--", "edge"], ["!!!", ""], ])("slugOf(%j) is %j", (text, slug) => { expect(slugOf(text)).toBe(slug); }); it.each<[string, string, number[]]>([ ["/issues/2", "2", [8]], ["2026", "2", []], ["2 of 22, page 2", "2", [0, 14]], ["x2/2y", "2", []], ["é2", "2", []], ["https://x.test/items/abc123?ref=abc123", "abc123", [21, 32]], ["anything", "", []], ])("finds %j's delimited %j at %j", (text, token, offsets) => { expect(delimitedOffsets(text, token)).toEqual(offsets); }); it("replaces only delimited occurrences", () => { expect(replaceDelimited("/issues/2 in 2026", "2", "9")).toBe( "/issues/9 in 2026", ); expect(replaceDelimited("a-2-b-2", "2", "10")).toBe("a-10-b-10"); expect(replaceDelimited("2026", "2", "9")).toBe("2026"); }); // Known bug: a rejected match skips its whole length, hiding an overlapping delimited one. it("finds a delimited match that overlaps an undelimited one", () => { expect(delimitedOffsets("11-1-1", "1-1")).toEqual([3]); }); it("finds a delimited token in any string nested in objects and arrays", () => { expect( containsDelimited({ a: [1, { b: ["x", "see /issues/2"] }] }, "2"), ).toBe(true); expect(containsDelimited("2", "2")).toBe(true); expect(containsDelimited({ c: "2026", n: 2, d: null }, "2")).toBe(false); expect(containsDelimited(["2026", true, [{ e: "x2" }]], "2")).toBe(false); }); it("searches arrays and objects too wide to spread into one call", () => { const wide = Array.from({ length: 500_000 }, () => 0); expect(containsDelimited(wide, "2")).toBe(false); expect(containsDelimited({ wide: [...wide, "see /issues/2"] }, "2")).toBe( true, ); expect( containsDelimited( Object.fromEntries(wide.map((value, index) => [String(index), value])), "2", ), ).toBe(false); }); it("derives a deterministic version 4 UUID per seed", () => { const first = seededUuid("seed-a", UUID_EXEMPLAR); expect(first).toMatch(V4_UUID); expect(seededUuid("seed-a", UUID_EXEMPLAR)).toBe(first); const derived = seeds(64).map((seed) => seededUuid(seed, UUID_EXEMPLAR)); expect(new Set(derived).size).toBe(derived.length); for (const uuid of derived) expect(uuid).toMatch(V4_UUID); }); it("keeps upper case only when the recorded UUID is upper case", () => { const upper = seededUuid("seed-a", UUID_EXEMPLAR.toUpperCase()); expect(upper).toBe(seededUuid("seed-a", UUID_EXEMPLAR).toUpperCase()); expect(upper).toMatch(/[A-F]/u); const digits = "12345678-1234-4234-8234-123456789012"; expect(seededUuid("seed-a", digits)).toMatch(V4_UUID); const mixed = "123e4567-E89B-12d3-a456-426614174000"; expect(seededUuid("seed-a", mixed)).toMatch(V4_UUID); }); it.each<[string, RegExp]>([ ["a1b2c3d4e5f6", /^[0-9a-f]{12}$/u], ["A1B2C3D4E5F6", /^[0-9A-F]{12}$/u], ["k9x2m7q4z1w8", /^[0-9a-z]{12}$/u], ["AbCdEfGhIjKl", /^[A-Za-z]{12}$/u], ["deadbeefcafe", /^[a-z]{12}$/u], [ "tok_9Xa-7bQ.2", /^[0-9A-Za-z]{3}_[0-9A-Za-z]{3}-[0-9A-Za-z]{3}\.[0-9A-Za-z]$/u, ], ["____", /^____$/u], ])("keeps the length and character classes of %j", (exemplar, shape) => { for (const seed of seeds(16)) { const token = seededToken(seed, exemplar); expect(token).toHaveLength(exemplar.length); expect(token).toMatch(shape); expect(seededToken(seed, exemplar)).toBe(token); } }); it("draws tokens from the whole alphabet the recorded token uses", () => { const characters = (exemplar: string): string => seeds(64) .map((seed) => seededToken(seed, exemplar)) .join(""); expect(characters("a1b2c3d4e5f6")).toMatch(/^[0-9a-f]+$/u); expect(characters("k9x2m7q4z1w8")).toMatch(/[g-z]/u); const mixed = characters("AbCdEfGhIjKl"); expect(mixed).toMatch(/[a-z]/u); expect(mixed).toMatch(/[A-Z]/u); expect(seededToken("seed-a", "k9x2m7q4z1w8")).not.toBe( seededToken("seed-b", "k9x2m7q4z1w8"), ); }); it("reads a sequence written as digit groups of one width, but not a date, version or address", () => { expect(sequenceNumber("000-005")).toBe(5); expect(sequenceNumber("00-01-12")).toBe(112); expect(sequenceNumber(7)).toBe(7); for (const other of ["2026-10-21", "2026-08", "12.04", "1.2.3", "10.0.0.9"]) expect(sequenceNumber(other)).toBeUndefined(); expect(sequenceNumber("A-005")).toBeUndefined(); expect(sequenceNumber("000-")).toBeUndefined(); }); it("reads a count that outgrew its mask only as the exemplar's spelling of it", () => { expect(sequenceNumber("100-00", "99-99")).toBe(10_000); expect(sequenceNumber("1000-000", "000-005")).toBe(1_000_000); expect(sequenceNumber("100-00")).toBeUndefined(); for (const other of ["2026-10-21", "20261-02", "100-0"]) expect(sequenceNumber(other, "000-005")).toBeUndefined(); expect(sequenceNumber("100-00", "2026-10")).toBeUndefined(); }); it("spells a sequence value in the recorded value's digit groups, the overflow leading", () => { expect(spelledLike("000-005", 6)).toBe("000-006"); expect(spelledLike("000-999", 1000)).toBe("001-000"); expect(spelledLike("999-999", 1_000_000)).toBe("1000-000"); expect(spelledLike("12", 13)).toBe("13"); expect(spelledLike(12, 13)).toBe(13); }); it("respells a clock in another format, padding its fraction digits to the format's width but dropping none", () => { const utc = iso("T", 6, "+00:00"); expect(respellClock("2026-10-21T17:06:13.710410Z", utc)).toBe( "2026-10-21T17:06:13.710410+00:00", ); expect(respellClock("2026-10-21 17:06:13Z", utc)).toBe( "2026-10-21T17:06:13.000000+00:00", ); expect(respellClock("2026-10-21T17:06:13.710410Z", iso("T", 0, "Z"))).toBe( "2026-10-21T17:06:13.710410Z", ); // An epoch spelling is formatted from the instant. expect( respellClock("2026-10-21T17:06:13Z", { kind: "epoch", unit: "s" }), ).toBe(Date.UTC(2026, 9, 21, 17, 6, 13) / 1000); expect(respellClock("not a clock", utc)).toBe("not a clock"); }); it("respells a clock at another offset with its own fraction digits, across a date change", () => { expect( respellClock("2026-10-21T17:06:13.710410Z", iso("T", 6, "+02:00")), ).toBe("2026-10-21T19:06:13.710410+02:00"); expect( respellClock("2026-10-21T23:30:00.000321Z", iso(" ", 6, "+05:30")), ).toBe("2026-10-22 05:00:00.000321+05:30"); expect( respellClock("2026-10-21T01:00:00.5+01:00", iso("T", 3, "-03:00")), ).toBe("2026-10-20T21:00:00.500-03:00"); // An epoch value's milliseconds are its fraction digits. const epochMs = Date.UTC(2026, 9, 21, 17, 6, 13, 250); expect(respellClock(epochMs, iso("T", 0, "Z"))).toBe( "2026-10-21T17:06:13.25Z", ); expect(respellClock(epochMs, iso("T", 3, "+00:00"))).toBe( "2026-10-21T17:06:13.250+00:00", ); });