summaryrefslogtreecommitdiffstats
path: root/javascript/test/snowflake.test.ts
diff options
context:
space:
mode:
Diffstat (limited to 'javascript/test/snowflake.test.ts')
-rw-r--r--javascript/test/snowflake.test.ts196
1 files changed, 196 insertions, 0 deletions
diff --git a/javascript/test/snowflake.test.ts b/javascript/test/snowflake.test.ts
new file mode 100644
index 0000000..8a38d68
--- /dev/null
+++ b/javascript/test/snowflake.test.ts
@@ -0,0 +1,196 @@
+import {
+ Snowflake,
+ InvalidEncodingError,
+ InvalidIDLengthError,
+ ID_BYTE_SIZE,
+ ALPHABET,
+} from "../src/snowflake";
+import { describe, it, expect } from "vitest";
+
+describe("generate", () => {
+ it("New returns non-zero Snowflake", () => {
+ const s = Snowflake.generate();
+ expect(s.isZero()).toBe(false);
+ });
+
+ it("New returns unique IDs", () => {
+ const seen = new Set<string>();
+ for (let i = 0; i < 1000; i++) {
+ const s = Snowflake.generate();
+ const str = s.toString();
+ expect(seen.has(str)).toBe(false);
+ seen.add(str);
+ }
+ });
+});
+
+describe("zero", () => {
+ it("zero is zero", () => {
+ const z = Snowflake.zero();
+ expect(z.isZero()).toBe(true);
+ });
+
+ it("generated is not zero", () => {
+ const s = Snowflake.generate();
+ expect(s.isZero()).toBe(false);
+ });
+});
+
+describe("base62 round-trip", () => {
+ it("single round-trip", () => {
+ const original = Snowflake.generate();
+ const str = original.toString();
+ const parsed = Snowflake.fromString(str);
+ expect(parsed).toEqual(original);
+ });
+
+ it("many round-trips", () => {
+ for (let i = 0; i < 100; i++) {
+ const original = Snowflake.generate();
+ const str = original.toString();
+ const parsed = Snowflake.fromString(str);
+ expect(parsed).toEqual(original);
+ }
+ });
+});
+
+describe("base62 errors", () => {
+ it("invalid characters", () => {
+ expect(() => Snowflake.fromString("!!invalid!!")).toThrow(InvalidEncodingError);
+ });
+
+ it("empty string produces zero", () => {
+ const s = Snowflake.fromString("");
+ expect(s.isZero()).toBe(true);
+ });
+
+ it("too long string throws", () => {
+ const tooLong =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
+ expect(() => Snowflake.fromString(tooLong)).toThrow(InvalidEncodingError);
+ });
+});
+
+describe("zero stringification", () => {
+ it("zero stringifies to '0'", () => {
+ const z = Snowflake.zero();
+ expect(z.toString()).toBe("0");
+ });
+
+ it("zero round-trips", () => {
+ const z = Snowflake.zero();
+ const str = z.toString();
+ const parsed = Snowflake.fromString(str);
+ expect(parsed.isZero()).toBe(true);
+ });
+});
+
+describe("hex encoding", () => {
+ it("hex has correct length and characters", () => {
+ const s = Snowflake.generate();
+ const hex = s.toHex();
+ expect(hex.length).toBe(ID_BYTE_SIZE * 2);
+ expect(hex).toMatch(/^[0-9a-f]+$/);
+ });
+
+ it("hex round-trip", () => {
+ const original = Snowflake.generate();
+ const hex = original.toHex();
+ const parsed = Snowflake.fromHex(hex);
+ expect(parsed).toEqual(original);
+ });
+
+ it("invalid hex throws", () => {
+ expect(() => Snowflake.fromHex("xyz")).toThrow(InvalidEncodingError);
+ });
+
+ it("wrong length hex throws", () => {
+ expect(() => Snowflake.fromHex("01")).toThrow(InvalidIDLengthError);
+ });
+});
+
+describe("JSON serialization", () => {
+ it("toJSON returns base62 string", () => {
+ const s = Snowflake.generate();
+ const json = JSON.stringify(s);
+ const parsed = JSON.parse(json);
+ expect(parsed).toBe(s.toString());
+ });
+
+ it("fromJSON round-trip", () => {
+ const s = Snowflake.generate();
+ const json = JSON.stringify(s);
+ const parsed = Snowflake.fromJSON(json);
+ expect(parsed).toEqual(s);
+ });
+
+ it("fromJSON rejects missing quotes", () => {
+ expect(() => Snowflake.fromJSON("invalid")).toThrow(InvalidEncodingError);
+ });
+
+ it("fromJSON empty string produces zero", () => {
+ const s = Snowflake.fromJSON('""');
+ expect(s.isZero()).toBe(true);
+ });
+
+ it("fromJSON invalid inner content throws", () => {
+ expect(() => Snowflake.fromJSON('"!!!"')).toThrow(InvalidEncodingError);
+ });
+});
+
+describe("bytes", () => {
+ it("toBytes returns correct length", () => {
+ const s = Snowflake.generate();
+ const bytes = s.toBytes();
+ expect(bytes.length).toBe(ID_BYTE_SIZE);
+ });
+
+ it("fromBytes round-trip with Uint8Array", () => {
+ const original = Snowflake.generate();
+ const bytes = original.toBytes();
+ const parsed = Snowflake.fromBytes(bytes);
+ expect(parsed).toEqual(original);
+ });
+
+ it("fromBytes round-trip with string", () => {
+ const original = Snowflake.generate();
+ const bytes = original.toBytes();
+ const str = String.fromCharCode(...bytes);
+ const parsed = Snowflake.fromBytes(str);
+ expect(parsed).toEqual(original);
+ });
+
+ it("fromBytes nil-like (empty) throws", () => {
+ expect(() => Snowflake.fromBytes(new Uint8Array(0))).toThrow(InvalidIDLengthError);
+ });
+
+ it("fromBytes wrong length throws", () => {
+ expect(() => Snowflake.fromBytes(new Uint8Array([1, 2, 3]))).toThrow(InvalidIDLengthError);
+ });
+
+ it("fromBytes zero bytes produces zero", () => {
+ const zeroBytes = new Uint8Array(ID_BYTE_SIZE);
+ const s = Snowflake.fromBytes(zeroBytes);
+ expect(s.isZero()).toBe(true);
+ });
+});
+
+describe("JSON embedding", () => {
+ it("embedded in struct round-trips", () => {
+ const s = Snowflake.generate();
+ const wrapper = { name: "test", id: s, ref: s };
+ const data = JSON.stringify(wrapper);
+ const decoded = JSON.parse(data);
+ expect(decoded.id).toBe(s.toString());
+ expect(decoded.ref).toBe(s.toString());
+ });
+});
+
+describe("cross-encoding consistency", () => {
+ it("toString and toHex encode the same bytes", () => {
+ const s = Snowflake.generate();
+ const fromBase62 = Snowflake.fromString(s.toString());
+ const fromHex = Snowflake.fromHex(s.toHex());
+ expect(fromBase62.bytes).toEqual(fromHex.bytes);
+ });
+});