Files

123 lines
3.0 KiB
TypeScript

import { test, expect } from 'vitest';
import {
clearRegistry,
enumOf,
f64,
flags,
list,
oneOf,
str,
type,
u32,
u53,
u8,
} from '../plugin/index.ts';
function rng(seed: number): () => number {
let a = seed >>> 0;
return () => {
a = (a + 0x6d2b79f5) >>> 0;
let t = a;
t = Math.imul(t ^ (t >>> 15), t | 1);
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
};
}
const r = rng(0xc0ffee);
function randFloat(): number {
const bucket = Math.floor(r() * 6);
switch (bucket) {
case 0: return 0;
case 1: return r() * 100;
case 2: return r() * 1e10;
case 3: return -r() * 100;
case 4: return (r() - 0.5) * 1e-6;
default: return r() * 1000;
}
}
function randInt(maxBits = 32): number {
return Math.floor(r() * 2 ** maxBits) >>> 0;
}
function randString(): string {
const len = Math.floor(r() * 30);
let s = '';
for (let i = 0; i < len; i++) s += String.fromCharCode(32 + Math.floor(r() * 95));
return s;
}
test('fuzz: 2000 random ticker round-trips', () => {
clearRegistry();
const Ticker = type('FuzzTicker', {
symbol: str,
last: f64,
volume: f64,
count: u32,
asks: list(f64),
});
for (let i = 0; i < 2000; i++) {
const v = {
symbol: randString(),
last: randFloat(),
volume: randFloat(),
count: randInt(32),
asks: Array.from({ length: Math.floor(r() * 10) }, randFloat),
};
expect(Ticker.decode(Ticker.encode(v)), `iteration ${i}`).toEqual(v);
}
});
test('fuzz: 1000 random nested orders', () => {
clearRegistry();
const Price = type('FuzzPrice', { value: f64, scale: u8 });
const Order = type('FuzzOrder', {
id: u53,
symbol: str,
price: Price,
qty: f64,
side: enumOf(['buy', 'sell'] as const),
tags: list(str),
flags: flags(['ioc', 'post_only', 'reduce_only'] as const),
});
for (let i = 0; i < 1000; i++) {
const v = {
id: Math.floor(r() * 2 ** 40),
symbol: randString(),
price: { value: randFloat(), scale: randInt(8) & 0xff },
qty: randFloat(),
side: (r() < 0.5 ? 'buy' : 'sell') as 'buy' | 'sell',
tags: Array.from({ length: Math.floor(r() * 5) }, randString),
flags: {
ioc: r() < 0.5,
post_only: r() < 0.5,
reduce_only: r() < 0.5,
},
};
expect(Order.decode(Order.encode(v)), `iteration ${i}`).toEqual(v);
}
});
test('fuzz: 500 random unions', () => {
clearRegistry();
const Event = oneOf('FuzzEvent', 'kind', {
fill: { price: f64, qty: f64 },
cancel: { reason: str },
expire: { at: u53 },
});
for (let i = 0; i < 500; i++) {
const which = Math.floor(r() * 3);
let v: unknown;
if (which === 0) v = { kind: 'fill', price: randFloat(), qty: randFloat() };
else if (which === 1) v = { kind: 'cancel', reason: randString() };
else v = { kind: 'expire', at: Math.floor(r() * 2 ** 40) };
expect(Event.decode(Event.encode(v as never)), `iteration ${i}`).toEqual(v);
}
});