blob: c58a9882eb22ad73c6836e3619dc394657f5b7c5 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import { u128ToBuf } from "../number.utils.js";
import {
isValidMessageId,
serializeMessageId,
resolveMessageId,
mintMessageId,
} from "./message.utils.js";
const MESSAGE_ID_SIZE = 16;
const MAX_U128 = (1n << 128n) - 1n;
const NIL_UUID = "00000000-0000-0000-0000-000000000000";
const isZero = (b: Buffer) => b.every((byte) => byte === 0);
describe("isValidMessageId", () => {
it("accepts undefined, string, number, and bigint", () => {
assert.ok(isValidMessageId(undefined));
assert.ok(isValidMessageId("id"));
assert.ok(isValidMessageId(7));
assert.ok(isValidMessageId(7n));
});
it("rejects other types", () => {
assert.ok(!isValidMessageId(null));
assert.ok(!isValidMessageId({}));
});
});
describe("serializeMessageId", () => {
it("serializes undefined to a zero u128", () => {
assert.deepEqual(serializeMessageId(), Buffer.alloc(MESSAGE_ID_SIZE, 0));
});
it("serializes a number as little-endian u128", () => {
assert.deepEqual(serializeMessageId(7), u128ToBuf(7n));
});
it("serializes a bigint as little-endian u128", () => {
assert.deepEqual(serializeMessageId(8n), u128ToBuf(8n));
});
it("serializes a UUID string to the same bytes as its numeric value", () => {
const uuid = "00000000-0000-0000-0000-000000000007";
assert.deepEqual(serializeMessageId(uuid), u128ToBuf(7n));
});
it("accepts the largest u128", () => {
assert.deepEqual(serializeMessageId(MAX_U128), u128ToBuf(MAX_U128));
});
it("rejects a numeric id at or above 2^128", () => {
assert.throws(() => serializeMessageId(1n << 128n), /2\^128/);
});
it("rejects a negative numeric id", () => {
assert.throws(() => serializeMessageId(-1n), />= 0/);
});
it("rejects an unparsable string", () => {
assert.throws(() => serializeMessageId("not-a-uuid"), /invalid message id/);
});
it("rejects an invalid type", () => {
assert.throws(() => serializeMessageId({}), /invalid message id/);
});
});
describe("resolveMessageId", () => {
it("mints a non-zero id for undefined, 0, and 0n", () => {
for (const id of [undefined, 0, 0n]) {
const b = resolveMessageId(id);
assert.equal(b.length, MESSAGE_ID_SIZE);
assert.ok(!isZero(b));
}
});
it("mints for the all-zero nil UUID string", () => {
assert.ok(!isZero(resolveMessageId(NIL_UUID)));
});
it("passes a provided non-zero id through unchanged", () => {
assert.deepEqual(resolveMessageId(7n), serializeMessageId(7n));
});
});
describe("mintMessageId", () => {
it("returns a non-zero 16-byte buffer", () => {
const b = mintMessageId();
assert.equal(b.length, MESSAGE_ID_SIZE);
assert.ok(!isZero(b));
});
it("produces unique, non-zero ids across pool refills", () => {
const seen = new Set<string>();
for (let i = 0; i < 10_000; i++) {
const hex = mintMessageId().toString("hex");
assert.notEqual(hex, "0".repeat(MESSAGE_ID_SIZE * 2));
seen.add(hex);
}
assert.equal(seen.size, 10_000);
});
it("returns owned bytes that survive a later refill", () => {
const first = mintMessageId().toString("hex");
const held = mintMessageId();
const snapshot = held.toString("hex");
for (let i = 0; i < 10_000; i++) mintMessageId();
assert.equal(held.toString("hex"), snapshot);
assert.notEqual(snapshot, first);
});
});