Skip to content
markpaper

src/orders/resting.test.ts

v0.2.1 · 3.5 KB

Download file
import { describe, expect, it } from 'vitest';
import { placeKey } from '../pending/memory.js';
import { ORDER_ID_A, ORDER_ID_B } from '../testing/fixtures.js';
import { resolveResting, sendClassOf, sortForSending, waitForOrderKeyInBook } from './resting.js';

const noSleep = async () => {};

function clock(start = 0) {
  let t = start;
  return { now: () => t, tick: (ms: number) => (t += ms) };
}

describe('resolveResting', () => {
  it('finds the order by client_order_index and market after the rollup catches up', async () => {
    const c = clock();
    let reads = 0;
    const r = await resolveResting({
      readOpenOrders: async () => {
        reads++;
        return reads < 3 ? [] : [{ orderId: ORDER_ID_A, clientOrderIndex: 42, marketIndex: 32 }];
      },
      clientOrderIndex: 42,
      marketIndex: 32,
      timeoutMs: 5000,
      intervalMs: 700,
      sleep: async (ms) => {
        c.tick(ms);
      },
      now: c.now,
    });
    expect(r).toMatchObject({ found: true, orderId: ORDER_ID_A, reads: 3 });
  });

  it('does not match the same index on another market or a null index', async () => {
    const c = clock();
    const r = await resolveResting({
      readOpenOrders: async () => [
        { orderId: ORDER_ID_A, clientOrderIndex: 42, marketIndex: 41 },
        { orderId: ORDER_ID_B, clientOrderIndex: null, marketIndex: 32 },
      ],
      clientOrderIndex: 42,
      marketIndex: 32,
      timeoutMs: 1000,
      intervalMs: 500,
      sleep: async (ms) => {
        c.tick(ms);
      },
      now: c.now,
    });
    expect(r).toEqual({ found: false, reads: 3 }); // t=0, 500, 1000 -> deadline reached
  });

  it('reads at least once even with a zero timeout', async () => {
    const r = await resolveResting({
      readOpenOrders: async () => [],
      clientOrderIndex: 1,
      marketIndex: 1,
      timeoutMs: 0,
      sleep: noSleep,
    });
    expect(r).toEqual({ found: false, reads: 1 });
  });
});

describe('waitForOrderKeyInBook', () => {
  it('returns true and calls onFound when the key appears, false on timeout', async () => {
    const key = placeKey('ETH', true, '1650.00');
    const c = clock();
    let n = 0;
    const found: string[] = [];
    const ok = await waitForOrderKeyInBook({
      readKeys: async () => new Set(++n >= 2 ? [key] : []),
      key,
      timeoutMs: 5000,
      intervalMs: 700,
      sleep: async (ms) => {
        c.tick(ms);
      },
      now: c.now,
      onFound: (k) => found.push(k),
    });
    expect(ok).toBe(true);
    expect(found).toEqual([key]);

    const c2 = clock();
    const missing = await waitForOrderKeyInBook({
      readKeys: async () => new Set<string>(),
      key,
      timeoutMs: 1400,
      intervalMs: 700,
      sleep: async (ms) => {
        c2.tick(ms);
      },
      now: c2.now,
    });
    expect(missing).toBe(false);
  });
});

describe('sortForSending', () => {
  it('orders cancels, IoC, reduceOnly GTC, ordinary GTC; stable within a class', () => {
    type A = { kind: 'cancel'; id: string } | { kind: 'place'; tif: 'Gtc' | 'Ioc'; reduceOnly: boolean; id: string };
    const actions: A[] = [
      { kind: 'place', tif: 'Gtc', reduceOnly: false, id: 'g1' },
      { kind: 'place', tif: 'Gtc', reduceOnly: true, id: 'r1' },
      { kind: 'cancel', id: 'c1' },
      { kind: 'place', tif: 'Ioc', reduceOnly: false, id: 'i1' },
      { kind: 'place', tif: 'Gtc', reduceOnly: false, id: 'g2' },
      { kind: 'cancel', id: 'c2' },
    ];
    expect(sortForSending(actions, sendClassOf).map((a) => a.id)).toEqual(['c1', 'c2', 'i1', 'r1', 'g1', 'g2']);
  });
});
All files