Imported as a linear vendor commit so forge rebases do not replay the upstream root tree. git-subtree-dir: third_party/pi git-subtree-split: d981de1229ef899957bbe968bc8dcda02a21f477
58 lines
2.0 KiB
TypeScript
58 lines
2.0 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
import { uuidv7 } from "../src/utils/uuid.ts";
|
|
|
|
const UUID_V7_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-7[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
const TIMESTAMP = 0x0123456789ab;
|
|
|
|
function parseTimestamp(uuid: string): number {
|
|
return Number.parseInt(uuid.replaceAll("-", "").slice(0, 12), 16);
|
|
}
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.unstubAllGlobals();
|
|
});
|
|
|
|
describe("uuidv7", () => {
|
|
it("generates ordered UUIDv7s while preserving follower timestamps", () => {
|
|
vi.useFakeTimers();
|
|
vi.setSystemTime(TIMESTAMP);
|
|
|
|
const first = uuidv7();
|
|
const second = uuidv7();
|
|
vi.setSystemTime(TIMESTAMP - 1);
|
|
const afterRollback = uuidv7();
|
|
vi.setSystemTime(TIMESTAMP + 1);
|
|
const afterAdvance = uuidv7();
|
|
const ordinaryIds = [first, second, afterRollback, afterAdvance];
|
|
const followerTimestamp = TIMESTAMP - 1_000;
|
|
const followers = [uuidv7(followerTimestamp), uuidv7(followerTimestamp)];
|
|
|
|
for (const id of [...ordinaryIds, ...followers]) expect(id).toMatch(UUID_V7_RE);
|
|
expect(ordinaryIds).toEqual([...ordinaryIds].sort());
|
|
expect(new Set(ordinaryIds)).toHaveLength(ordinaryIds.length);
|
|
expect(ordinaryIds.map(parseTimestamp)).toEqual([TIMESTAMP, TIMESTAMP, TIMESTAMP, TIMESTAMP + 1]);
|
|
expect(followers.map(parseTimestamp)).toEqual([followerTimestamp, followerTimestamp]);
|
|
expect(new Set(followers)).toHaveLength(followers.length);
|
|
});
|
|
|
|
it("uses fresh randomness for every UUID tail", () => {
|
|
let randomByte = 0;
|
|
vi.stubGlobal("crypto", {
|
|
getRandomValues(bytes: Uint8Array) {
|
|
return bytes.fill(++randomByte);
|
|
},
|
|
});
|
|
|
|
expect([uuidv7(TIMESTAMP).slice(-8), uuidv7(TIMESTAMP).slice(-8)]).toEqual(["01010101", "02020202"]);
|
|
});
|
|
|
|
it.each([0, 2 ** 48 - 1])("accepts timestamp boundary %s", (timestamp) => {
|
|
expect(parseTimestamp(uuidv7(timestamp))).toBe(timestamp);
|
|
});
|
|
|
|
it.each([-1, 2 ** 48, 1.5, Number.NaN, Number.POSITIVE_INFINITY])("rejects invalid timestamp %s", (timestamp) => {
|
|
expect(() => uuidv7(timestamp)).toThrow(RangeError);
|
|
});
|
|
});
|