Skip to content
markpaper

src/orders/cloid.test.ts

v0.3.0 · 3 KB

Download file
import { describe, expect, it } from 'vitest';
import { CLOID_HEX_LENGTH, cloidHasPrefix, createCloid, isCloid, normalizeCloid } from './cloid.js';
import { nonNegativeDiff, plain, dec } from './decimal.js';
import { HlOrderError } from './errors.js';

const fixed = (byte: number) => (bytes: Uint8Array) => bytes.fill(byte);

describe('cloid format: 0x + 32 hex', () => {
  it('isCloid accepts exactly 34 characters of hex, either case', () => {
    expect(isCloid(`0x${'a'.repeat(32)}`)).toBe(true);
    expect(isCloid(`0x${'A'.repeat(32)}`)).toBe(true);
    expect(isCloid('0x1234')).toBe(false); // SDK: "Invalid length: Expected 34"
    expect(isCloid(`0x${'a'.repeat(33)}`)).toBe(false);
    expect(isCloid(`${'a'.repeat(34)}`)).toBe(false);
    expect(isCloid(`0x${'g'.repeat(32)}`)).toBe(false);
    expect(isCloid(123)).toBe(false);
  });

  it('normalizeCloid lowercases (the SDK signs and the exchange echoes lowercase)', () => {
    expect(normalizeCloid(`0x${'AbCd'.repeat(8)}`)).toBe(`0x${'abcd'.repeat(8)}`);
    expect(() => normalizeCloid('0x1234')).toThrow(HlOrderError);
  });
});

describe('createCloid', () => {
  it('produces a valid lowercase cloid from the random source', () => {
    const c = createCloid({ random: fixed(0xab) });
    expect(c).toBe(`0x${'ab'.repeat(16)}`);
    expect(c.length).toBe(2 + CLOID_HEX_LENGTH);
  });

  it('uses crypto by default and does not repeat', () => {
    const seen = new Set(Array.from({ length: 200 }, () => createCloid()));
    expect(seen.size).toBe(200);
    for (const c of seen) expect(isCloid(c)).toBe(true);
  });

  it('places an ownership prefix at the start, keeping the length', () => {
    const c = createCloid({ prefix: '0xBEEF', random: fixed(0) });
    expect(c).toBe(`0xbeef${'0'.repeat(28)}`);
    expect(cloidHasPrefix(c, 'beef')).toBe(true);
    expect(cloidHasPrefix(c, 'dead')).toBe(false);
    expect(cloidHasPrefix('0x12', 'beef')).toBe(false);
  });

  it('rejects a prefix that is not hex or leaves fewer than 16 random characters', () => {
    expect(() => createCloid({ prefix: 'xyz' })).toThrow(HlOrderError);
    expect(() => createCloid({ prefix: 'a'.repeat(17) })).toThrow(/prefix/);
    expect(createCloid({ prefix: 'a'.repeat(16) })).toMatch(/^0xa{16}[0-9a-f]{16}$/);
  });

  it('rejects a random source that returns the wrong length', () => {
    expect(() => createCloid({ random: () => new Uint8Array(4) })).toThrow(RangeError);
  });
});

describe('exact decimal helpers', () => {
  it('remaining size has no float noise', () => {
    expect(0.3 - 0.1).not.toBe(0.2);
    expect(nonNegativeDiff('0.3', '0.1')).toBe('0.2');
    expect(nonNegativeDiff('1', '1.000')).toBe('0');
    expect(nonNegativeDiff('1', '2')).toBe('0');
    expect(nonNegativeDiff(0.29, '0.001')).toBe('0.289');
  });

  it('plain renders small, negative and integer values canonically', () => {
    expect(plain({ n: 5n, s: 4 })).toBe('0.0005');
    expect(plain({ n: -1234n, s: 2 })).toBe('-12.34');
    expect(plain({ n: 1200n, s: 2 })).toBe('12');
    expect(plain(dec('-0'))).toBe('0');
  });
});
All files