Skip to content
markpaper

src/ids/exact.test.ts

v0.2.1 · 4.5 KB

Download file
import { describe, expect, it } from 'vitest';
import { activeOrdersText, ORDER_ID_A, ORDER_ID_B, ORDER_ID_ROUNDED, ORDER_ID_SAFE } from '../testing/fixtures.js';
import {
  exactOrderId,
  isSafeOrderId,
  orderIdPrecision,
  orderIndexWasRounded,
  parseJsonExact,
  quoteIntegerFields,
} from './exact.js';

describe('the trap', () => {
  it('JSON.parse collapses two different ids into one number', () => {
    const a = JSON.parse(`{"order_index":${ORDER_ID_A}}`).order_index;
    const b = JSON.parse(`{"order_index":${ORDER_ID_B}}`).order_index;
    expect(a).toBe(b);
    expect(a).toBe(ORDER_ID_ROUNDED);
    // The naive precision check "proves" nothing: the value is already rounded.
    expect(String(Number(a)) === String(a)).toBe(true);
  });
});

describe('quoteIntegerFields / parseJsonExact', () => {
  it('quotes bare integers of the chosen keys only', () => {
    const text = `{"order_index": ${ORDER_ID_A}, "order_id": "${ORDER_ID_A}", "client_order_index": 1712345678, "price": "1650.00"}`;
    const parsed = parseJsonExact<Record<string, unknown>>(text);
    expect(parsed.order_index).toBe(ORDER_ID_A);
    expect(parsed.order_id).toBe(ORDER_ID_A);
    expect(parsed.client_order_index).toBe(1712345678);
    expect(parsed.price).toBe('1650.00');
  });

  it('keeps every id of a real-shaped response exact', () => {
    const parsed = parseJsonExact<{ orders: Array<{ order_id: string; order_index: string }> }>(activeOrdersText());
    expect(parsed.orders.map((o) => o.order_index)).toEqual([ORDER_ID_A, ORDER_ID_B, ORDER_ID_SAFE]);
    expect(parsed.orders.every((o) => o.order_id === o.order_index)).toBe(true);
    // and the same text through plain JSON.parse collapses the first two
    const plain = JSON.parse(activeOrdersText()) as { orders: Array<{ order_index: number }> };
    expect(plain.orders[0]?.order_index).toBe(plain.orders[1]?.order_index);
  });

  it('does not touch strings that look like keys, floats, negatives elsewhere, or nested values', () => {
    const text =
      '{"note":"\\"order_index\\": 5","order_index":-12,"x":{"order_index":1.5},"y":[{"order_index":7e2}],"order_index2":9}';
    const out = quoteIntegerFields(text);
    expect(out).toBe(
      '{"note":"\\"order_index\\": 5","order_index":"-12","x":{"order_index":1.5},"y":[{"order_index":7e2}],"order_index2":9}',
    );
    expect(() => JSON.parse(out)).not.toThrow();
  });

  it('handles whitespace and custom keys', () => {
    const out = quoteIntegerFields('{ "a" :  42 , "b":42}', ['a']);
    expect(out).toBe('{ "a" :  "42" , "b":42}');
    expect(quoteIntegerFields('{"a":null,"a":"s","a":{}}', ['a'])).toBe('{"a":null,"a":"s","a":{}}');
  });

  it('is a no-op on text without the keys', () => {
    const text = JSON.stringify({ accounts: [{ index: 1, total_asset_value: '3000.0' }] });
    expect(quoteIntegerFields(text)).toBe(text);
  });
});

describe('exactOrderId / orderIdPrecision', () => {
  it('prefers the string order_id', () => {
    expect(exactOrderId({ order_id: ORDER_ID_A, order_index: ORDER_ID_ROUNDED })).toBe(ORDER_ID_A);
    expect(orderIdPrecision({ order_id: ORDER_ID_A, order_index: ORDER_ID_ROUNDED })).toBe('exact');
  });

  it('accepts a string order_index (after parseJsonExact) and a safe integer', () => {
    expect(exactOrderId({ order_index: ORDER_ID_B })).toBe(ORDER_ID_B);
    expect(exactOrderId({ order_index: 123456 })).toBe('123456');
    expect(exactOrderId({ order_id: Number(ORDER_ID_SAFE) })).toBe(ORDER_ID_SAFE);
    expect(exactOrderId({ order_id: ' 0042 ' })).toBe('42');
  });

  it('refuses a rounded number by default and flags it', () => {
    expect(exactOrderId({ order_index: ORDER_ID_ROUNDED })).toBeNull();
    expect(orderIdPrecision({ order_index: ORDER_ID_ROUNDED })).toBe('rounded');
    expect(exactOrderId({ order_index: ORDER_ID_ROUNDED }, { allowRounded: true })).toBe(String(ORDER_ID_ROUNDED));
  });

  it('reports missing ids', () => {
    expect(exactOrderId({})).toBeNull();
    expect(exactOrderId({ order_id: 'abc', order_index: -1 })).toBeNull();
    expect(orderIdPrecision({ order_id: 'abc' })).toBe('missing');
  });

  it('isSafeOrderId and orderIndexWasRounded detect the lost bit', () => {
    expect(isSafeOrderId(ORDER_ID_SAFE)).toBe(true);
    expect(isSafeOrderId(ORDER_ID_A)).toBe(false);
    expect(orderIndexWasRounded({ order_id: ORDER_ID_A, order_index: ORDER_ID_ROUNDED })).toBe(true);
    expect(orderIndexWasRounded({ order_id: ORDER_ID_SAFE, order_index: Number(ORDER_ID_SAFE) })).toBe(false);
    expect(orderIndexWasRounded({ order_id: ORDER_ID_A })).toBe(false);
  });
});
All files