Skip to content
markpaper

src/format/index.test.ts

v0.3.0 · 3.1 KB

Download file
import { describe, expect, it } from 'vitest';
import * as format from './index.js';
import { HlFormatError, normalizeDecimal } from './index.js';

describe('normalizeDecimal', () => {
  it('produces the canonical wire string without rounding', () => {
    expect(normalizeDecimal('83.2050')).toBe('83.205');
    expect(normalizeDecimal(1e-7)).toBe('0.0000001');
    expect(String(1e-7)).toBe('1e-7'); // what the SDK schema would reject
    expect(normalizeDecimal(1.5e21)).toBe('1500000000000000000000');
    expect(normalizeDecimal('-0.0')).toBe('0');
    expect(normalizeDecimal('007.50')).toBe('7.5');
    expect(normalizeDecimal('.5')).toBe('0.5');
    expect(normalizeDecimal('1e+3')).toBe('1000');
    expect(normalizeDecimal(-1.25)).toBe('-1.25');
    expect(normalizeDecimal('0.123456789123456789')).toBe('0.123456789123456789');
  });

  it('throws on non-finite or malformed input', () => {
    for (const bad of [Infinity, NaN, 'abc', '1.2.3', '']) {
      expect(() => normalizeDecimal(bad)).toThrow(HlFormatError);
    }
  });

  it('HlFormatError is a named Error', () => {
    try {
      normalizeDecimal('nope');
    } catch (e) {
      expect(e).toBeInstanceOf(Error);
      expect((e as Error).name).toBe('HlFormatError');
    }
  });
});

describe('public API surface', () => {
  it('exports every documented function and constant', () => {
    const names = [
      'normalizeDecimal',
      'assertSzDecimals',
      'szStep',
      'formatSize',
      'sizeFromNotional',
      'isValidSize',
      'isFullyFilled',
      'hasHiddenRemainder',
      'pxDecimals',
      'formatPrice',
      'priceTick',
      'isValidPrice',
      'shiftPriceTicks',
      'samePriceOnGrid',
      'notional',
      'meetsMinNotional',
      'bumpToMinNotional',
      'planOrderSize',
      'marketLimitPrice',
      'exitSlippage',
      'escalatingSlippage',
      'postOnlyPrice',
      'validateOrder',
      'triggerPxMatches',
      'quoteSizeFromNotional',
      'isFlatPosition',
      'positionMatchesExpected',
    ] as const;
    for (const name of names) expect(typeof format[name]).toBe('function');
    expect(format.MIN_ORDER_NOTIONAL_USD).toBe(10);
    expect(format.MAX_SZ_DECIMALS).toBe(8);
    expect(format.MAX_PRICE_SIG_FIGS).toBe(5);
    expect(typeof format.HlFormatError).toBe('function');
  });

  it('does not leak private decimal helpers', () => {
    expect('parseDec' in format).toBe(false);
    expect('roundRatio' in format).toBe(false);
    expect('roundPriceDec' in format).toBe(false);
  });
});

describe('end-to-end: market IoC entry', () => {
  it('limit price, size and validation agree', () => {
    const szDecimals = 2;
    const p = format.marketLimitPrice({ mid: '34.9', side: 'B', slippage: 0.01, szDecimals });
    const plan = format.planOrderSize({ intent: 'open', size: format.sizeFromNotional(12, p, szDecimals), px: p, szDecimals });
    expect(p).toBe('35.249');
    expect(plan).toMatchObject({ action: 'place', sz: '0.34' });
    if (plan.action !== 'place') throw new Error('unreachable');
    expect(format.validateOrder({ px: p, sz: plan.sz, szDecimals }).ok).toBe(true);
  });
});
All files