2024-01-01 21:11:32 +00:00
|
|
|
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
2023-09-12 11:31:03 +00:00
|
|
|
// Copyright 2023 Yoshiya Hinosawa. All rights reserved. MIT license.
|
|
|
|
// Copyright 2017 Alizain Feerasta. All rights reserved. MIT license.
|
|
|
|
|
2024-04-29 02:57:30 +00:00
|
|
|
import { FakeTime } from "@std/testing/time";
|
|
|
|
import { assertEquals, assertStrictEquals, assertThrows } from "@std/assert";
|
2023-09-12 11:31:03 +00:00
|
|
|
|
|
|
|
import { decodeTime, monotonicUlid, ulid } from "./mod.ts";
|
|
|
|
import {
|
|
|
|
encodeRandom,
|
|
|
|
encodeTime,
|
|
|
|
incrementBase32,
|
|
|
|
monotonicFactory,
|
2024-06-17 03:11:11 +00:00
|
|
|
RANDOM_LEN,
|
2023-09-12 11:31:03 +00:00
|
|
|
} from "./_util.ts";
|
|
|
|
|
2024-01-02 23:41:23 +00:00
|
|
|
Deno.test("incrementBase32()", async (t) => {
|
2023-09-12 11:31:03 +00:00
|
|
|
await t.step("increments correctly", () => {
|
|
|
|
assertEquals("A109D", incrementBase32("A109C"));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("carries correctly", () => {
|
|
|
|
assertEquals("A1Z00", incrementBase32("A1YZZ"));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("double increments correctly", () => {
|
|
|
|
assertEquals(
|
|
|
|
"A1Z01",
|
|
|
|
incrementBase32(incrementBase32("A1YZZ")),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("throws when it cannot increment", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
incrementBase32("ZZZ");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("throws when given an invalid string", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
incrementBase32("./_--=-`,.");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-01-02 23:41:23 +00:00
|
|
|
Deno.test("encodeTime()", async (t) => {
|
2023-09-12 11:31:03 +00:00
|
|
|
await t.step("should return expected encoded result", () => {
|
2024-06-17 03:11:11 +00:00
|
|
|
assertEquals("01ARYZ6S41", encodeTime(1469918176385));
|
2023-09-12 11:31:03 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("should throw an error", async (t) => {
|
|
|
|
await t.step("if time greater than (2 ^ 48) - 1", () => {
|
|
|
|
assertThrows(() => {
|
2024-06-17 03:11:11 +00:00
|
|
|
encodeTime(Math.pow(2, 48));
|
2023-09-12 11:31:03 +00:00
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("if time is not a number", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
// deno-lint-ignore no-explicit-any
|
2024-06-17 03:11:11 +00:00
|
|
|
encodeTime("test" as any);
|
2023-09-12 11:31:03 +00:00
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("if time is infinity", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
encodeTime(Infinity);
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("if time is negative", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
encodeTime(-1);
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("if time is a float", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
encodeTime(100.1);
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-01-02 23:41:23 +00:00
|
|
|
Deno.test("encodeRandom()", async (t) => {
|
2023-09-12 11:31:03 +00:00
|
|
|
await t.step("should return correct length", () => {
|
2024-06-17 03:11:11 +00:00
|
|
|
assertEquals(RANDOM_LEN, encodeRandom().length);
|
2023-09-12 11:31:03 +00:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-01-02 23:41:23 +00:00
|
|
|
Deno.test("decodeTime()", async (t) => {
|
2023-09-12 11:31:03 +00:00
|
|
|
await t.step("should return correct timestamp", () => {
|
|
|
|
const timestamp = Date.now();
|
|
|
|
const id = ulid(timestamp);
|
|
|
|
assertEquals(timestamp, decodeTime(id));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("should accept the maximum allowed timestamp", () => {
|
|
|
|
assertEquals(
|
|
|
|
281474976710655,
|
|
|
|
decodeTime("7ZZZZZZZZZZZZZZZZZZZZZZZZZ"),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("should reject", async (t) => {
|
|
|
|
await t.step("malformed strings of incorrect length", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
decodeTime("FFFF");
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("strings with timestamps that are too high", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
decodeTime("80000000000000000000000000");
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("invalid character", () => {
|
|
|
|
assertThrows(() => {
|
|
|
|
decodeTime("&1ARZ3NDEKTSV4RRFFQ69G5FAV");
|
|
|
|
}, Error);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2024-01-02 23:41:23 +00:00
|
|
|
Deno.test("ulid()", async (t) => {
|
2023-09-12 11:31:03 +00:00
|
|
|
await t.step("should return correct length", () => {
|
|
|
|
assertEquals(26, ulid().length);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step(
|
|
|
|
"should return expected encoded time component result",
|
|
|
|
() => {
|
|
|
|
assertEquals("01ARYZ6S41", ulid(1469918176385).substring(0, 10));
|
|
|
|
},
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2024-01-03 18:17:02 +00:00
|
|
|
Deno.test("monotonicUlid() handles monotonicity", async (t) => {
|
2024-06-17 03:11:11 +00:00
|
|
|
function encodeRandom(): string {
|
|
|
|
return "YYYYYYYYYYYYYYYY";
|
2023-09-12 11:31:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
await t.step("without seedTime", async (t) => {
|
|
|
|
const stubbedUlid = monotonicFactory(encodeRandom);
|
|
|
|
|
2023-12-14 20:47:50 +00:00
|
|
|
using _time = new FakeTime(1469918176385);
|
2023-09-12 11:31:03 +00:00
|
|
|
|
|
|
|
await t.step("first call", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYYY", stubbedUlid());
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("second call", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYYZ", stubbedUlid());
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("third call", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYZ0", stubbedUlid());
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("fourth call", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYZ1", stubbedUlid());
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("with seedTime", async (t) => {
|
|
|
|
const stubbedUlid = monotonicFactory(encodeRandom);
|
|
|
|
|
|
|
|
await t.step("first call", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYYY", stubbedUlid(1469918176385));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("second call with the same", () => {
|
|
|
|
assertStrictEquals(
|
|
|
|
"01ARYZ6S41YYYYYYYYYYYYYYYZ",
|
|
|
|
stubbedUlid(1469918176385),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("third call with less than", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYZ0", stubbedUlid(100000000));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("fourth call with even more less than", () => {
|
|
|
|
assertEquals("01ARYZ6S41YYYYYYYYYYYYYYZ1", stubbedUlid(10000));
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("fifth call with 1 greater than", () => {
|
|
|
|
assertEquals("01ARYZ6S42YYYYYYYYYYYYYYYY", stubbedUlid(1469918176386));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
await t.step("seed time is decoded correctly", () => {
|
|
|
|
assertEquals(
|
|
|
|
decodeTime(monotonicUlid(1000)),
|
|
|
|
1000,
|
|
|
|
);
|
|
|
|
assertEquals(
|
|
|
|
decodeTime(monotonicUlid(1469918176386)),
|
|
|
|
1469918176386,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|