src/markets/parse.test.ts
v0.2.1 · 4.8 KB
import { describe, expect, it } from 'vitest';
import { orderBookDetailsPayload, orderBooksPayload } from '../testing/fixtures.js';
import { markPrices, parseOrderBookDetails, parseOrderBooks, symbolByMarketId } from './parse.js';
import { LighterMetaError } from './types.js';
describe('parseOrderBookDetails', () => {
it('decodes perps, skips other market types, keeps raw strings', () => {
const m = parseOrderBookDetails(orderBookDetailsPayload());
expect([...m.keys()]).toEqual(['ETH', 'ETH/USDG', 'LIT', 'CRWV']);
const eth = m.get('ETH');
expect(eth).toMatchObject({
marketId: 32,
sizeDecimals: 4,
priceDecimals: 2,
minBaseAmount: 0.01,
minBaseAmountRaw: '0.0100',
minQuoteUsd: 10,
minQuoteUsdRaw: '10.000000',
markPrice: 1605.34,
markPriceRaw: '1605.34',
lastTradePrice: 1606.1,
minInitialMarginFraction: 1000,
defaultInitialMarginFraction: 5000,
maxLeverage: 10,
status: 'active',
isUsdgDuplicate: false,
});
expect(m.get('ETH/USDG')?.isUsdgDuplicate).toBe(true);
expect(m.get('LIT')).toMatchObject({ sizeDecimals: 0, minBaseAmount: 5, maxLeverage: 5 });
});
it('can include spot markets on request', () => {
const m = parseOrderBookDetails(orderBookDetailsPayload(), { marketTypes: ['perp', 'spot'] });
expect(m.get('PURR')?.marketType).toBe('spot');
});
it('fails closed on a missing array', () => {
expect(() => parseOrderBookDetails({})).toThrow(LighterMetaError);
expect(() => parseOrderBookDetails(null)).toThrow(LighterMetaError);
expect(() => parseOrderBookDetails({ order_book_details: 'nope' })).toThrow(LighterMetaError);
});
it('fails closed on a duplicate symbol or market_id', () => {
const dupSymbol = orderBookDetailsPayload();
const rows = dupSymbol.order_book_details as Array<Record<string, unknown>>;
rows.push({ ...(rows[0] as Record<string, unknown>), market_id: 777 });
expect(() => parseOrderBookDetails(dupSymbol)).toThrow(/duplicate symbol/);
const dupId = orderBookDetailsPayload();
const rows2 = dupId.order_book_details as Array<Record<string, unknown>>;
rows2.push({ ...(rows2[0] as Record<string, unknown>), symbol: 'OTHER' });
expect(() => parseOrderBookDetails(dupId)).toThrow(/duplicate market_id/);
});
it('fails closed on a record without decimals or with a broken id', () => {
const noDecimals = orderBookDetailsPayload();
const rows = noDecimals.order_book_details as Array<Record<string, unknown>>;
delete (rows[0] as Record<string, unknown>).supported_size_decimals;
expect(() => parseOrderBookDetails(noDecimals)).toThrow(/decimals/);
const badId = orderBookDetailsPayload();
(badId.order_book_details as Array<Record<string, unknown>>)[0] = {
symbol: 'X',
market_id: 'abc',
market_type: 'perp',
};
expect(() => parseOrderBookDetails(badId)).toThrow(/broken record/);
});
it('fails closed when no market of the requested type exists', () => {
expect(() => parseOrderBookDetails({ order_book_details: [] })).toThrow(LighterMetaError);
expect(() => parseOrderBookDetails(orderBookDetailsPayload(), { marketTypes: ['option'] })).toThrow(
LighterMetaError,
);
});
it('treats missing minimums / mark as zero, not as NaN', () => {
const m = parseOrderBookDetails({
order_book_details: [
{ symbol: 'X', market_id: 1, market_type: 'perp', supported_size_decimals: 1, supported_price_decimals: 1 },
],
});
expect(m.get('X')).toMatchObject({ minBaseAmount: 0, minQuoteUsd: 0, markPrice: 0, maxLeverage: 1, status: '' });
});
it('symbolByMarketId and markPrices derive lookup maps', () => {
const m = parseOrderBookDetails(orderBookDetailsPayload());
expect(symbolByMarketId(m.values()).get(41)).toBe('CRWV');
expect(markPrices(m.values()).get('LIT')).toBe(0.912);
});
});
describe('parseOrderBooks', () => {
it('decodes the flat list and flags duplicates of the quote currency', () => {
const rows = parseOrderBooks(orderBooksPayload());
expect(rows.map((r) => r.symbol)).toEqual(['ETH', 'ETH/USDG', 'LIT', 'CRWV']);
expect(rows[1]?.isUsdgDuplicate).toBe(true);
expect(rows[0]?.raw.taker_fee).toBe('0.0000');
});
it('fails closed on a missing array, a broken row or duplicates', () => {
expect(() => parseOrderBooks({})).toThrow(LighterMetaError);
expect(() => parseOrderBooks({ order_books: [{ market_id: 1 }] })).toThrow(/broken row/);
expect(() =>
parseOrderBooks({
order_books: [
{ symbol: 'A', market_id: 1 },
{ symbol: 'A', market_id: 2 },
],
}),
).toThrow(/duplicate symbol/);
expect(() =>
parseOrderBooks({
order_books: [
{ symbol: 'A', market_id: 1 },
{ symbol: 'B', market_id: 1 },
],
}),
).toThrow(/duplicate market_id/);
});
});