src/errors/statuses.test.ts
v0.3.0 · 11.6 KB
import { describe, expect, it } from 'vitest';
import {
isCancelConfirmed,
parseActionResponse,
parseCancelResponse,
parseCancelStatus,
parseCancelStatuses,
parseOrderResponse,
parseOrderStatus,
parseOrderStatuses,
parseTwapResponse,
placedOids,
} from './statuses.js';
const CLOID = '0x0000000000000000000000000000000A';
const orderBody = (statuses: unknown) => ({ status: 'ok', response: { type: 'order', data: { statuses } } });
describe('parseOrderStatus', () => {
it('parses resting, filled and error elements', () => {
expect(parseOrderStatus({ resting: { oid: 42 } })).toEqual({ status: 'resting', oid: 42 });
expect(parseOrderStatus({ filled: { oid: 43, totalSz: '1.2', avgPx: '99' } })).toEqual({
status: 'filled',
oid: 43,
totalSz: '1.2',
avgPx: '99',
});
expect(parseOrderStatus({ error: 'Order must have minimum value of $10.' })).toEqual({
status: 'error',
message: 'Order must have minimum value of $10.',
kind: 'minNotional',
});
});
it('keeps an optional cloid, lowercased; drops a malformed one', () => {
expect(parseOrderStatus({ resting: { oid: 1, cloid: CLOID } })).toEqual({
status: 'resting',
oid: 1,
cloid: CLOID.toLowerCase(),
});
expect(parseOrderStatus({ filled: { oid: 2, totalSz: '1', avgPx: '2', cloid: CLOID } })).toMatchObject({
cloid: CLOID.toLowerCase(),
});
expect(parseOrderStatus({ resting: { oid: 1, cloid: '0x1234' } })).toEqual({ status: 'resting', oid: 1 });
});
it('keeps fill sizes and prices as exact strings', () => {
const r = parseOrderStatus({ filled: { oid: 5, totalSz: '0.30000', avgPx: '100.500' } });
expect(r).toMatchObject({ totalSz: '0.30000', avgPx: '100.500' });
const n = parseOrderStatus({ filled: { oid: 5, totalSz: 1e-7, avgPx: 2 } });
expect(n).toMatchObject({ totalSz: '0.0000001', avgPx: '2' });
});
it('maps bare string statuses (positionTpsl, waitingForFill) to accepted without oid', () => {
expect(parseOrderStatus('waitingForTrigger')).toEqual({ status: 'accepted', detail: 'waitingForTrigger' });
expect(parseOrderStatus('resting')).toEqual({ status: 'accepted', detail: 'resting' });
expect(parseOrderStatus('waitingForFill')).toEqual({ status: 'accepted', detail: 'waitingForFill' });
expect(parseOrderStatus('success')).toMatchObject({ status: 'invalid', reason: 'unknown-shape' });
});
it('is fail-closed on unknown shapes', () => {
for (const raw of [{ futureShape: true }, null, 42, [], { resting: 1 }, { error: 123 }, { resting: { oid: 1 }, error: 'x' }]) {
expect(parseOrderStatus(raw).status).toBe('invalid');
}
});
it('rejects bad oids, including values beyond 2^53', () => {
expect(parseOrderStatus({ resting: { oid: 0 } })).toMatchObject({ status: 'invalid', reason: 'bad-oid' });
expect(parseOrderStatus({ resting: { oid: -1 } })).toMatchObject({ reason: 'bad-oid' });
expect(parseOrderStatus({ resting: { oid: 1.5 } })).toMatchObject({ reason: 'bad-oid' });
expect(parseOrderStatus({ resting: { oid: 2 ** 53 } })).toMatchObject({ reason: 'bad-oid' });
expect(parseOrderStatus({ resting: { oid: '9007199254740993' } })).toMatchObject({ reason: 'bad-oid' });
expect(parseOrderStatus({ resting: { oid: '77' } })).toEqual({ status: 'resting', oid: 77 });
expect(parseOrderStatus({ resting: {} })).toMatchObject({ reason: 'bad-oid' });
});
it('treats non-numeric, zero or negative fills as invalid, never as a fill', () => {
expect(parseOrderStatus({ filled: { oid: 1, totalSz: 'Infinity', avgPx: '1' } })).toMatchObject({ reason: 'bad-fill-size' });
expect(parseOrderStatus({ filled: { oid: 1, totalSz: '0', avgPx: '1' } })).toMatchObject({ reason: 'bad-fill-size' });
expect(parseOrderStatus({ filled: { oid: 1, totalSz: '-1', avgPx: '1' } })).toMatchObject({ reason: 'bad-fill-size' });
expect(parseOrderStatus({ filled: { oid: 1, totalSz: '1', avgPx: 'NaN' } })).toMatchObject({ reason: 'bad-fill-price' });
expect(parseOrderStatus({ filled: { oid: 1, totalSz: '1', avgPx: '0.0' } })).toMatchObject({ reason: 'bad-fill-price' });
expect(parseOrderStatus({ filled: { oid: 1, totalSz: '1' } })).toMatchObject({ reason: 'bad-fill-price' });
});
it('flags a fill above the requested size (exact decimal comparison)', () => {
const fill = { filled: { oid: 1, totalSz: '0.3', avgPx: '10' } };
expect(parseOrderStatus(fill, '0.29')).toMatchObject({ status: 'invalid', reason: 'overfill' });
expect(parseOrderStatus(fill, '0.3').status).toBe('filled');
expect(parseOrderStatus(fill, 0.1 + 0.2).status).toBe('filled'); // float noise above 0.3 is not an overfill
expect(parseOrderStatus(fill, '1').status).toBe('filled'); // partial IoC fill
});
});
describe('parseOrderStatuses', () => {
it('keeps batch order and pads missing elements', () => {
const res = parseOrderStatuses([{ resting: { oid: 1 } }], { expectedCount: 3 });
expect(res.map((r) => r.status)).toEqual(['resting', 'invalid', 'invalid']);
expect(res[1]).toMatchObject({ reason: 'missing' });
});
it('uses requestedSizes per index', () => {
const res = parseOrderStatuses(
[{ filled: { oid: 1, totalSz: '2', avgPx: '1' } }, { filled: { oid: 2, totalSz: '2', avgPx: '1' } }],
{ requestedSizes: ['1', '2'] },
);
expect(res.map((r) => r.status)).toEqual(['invalid', 'filled']);
});
it('returns [] for a non-array', () => {
expect(parseOrderStatuses(undefined)).toEqual([]);
expect(parseOrderStatuses({})).toEqual([]);
});
it('collects oids of placed orders only', () => {
const res = parseOrderStatuses([
{ resting: { oid: 1 } },
{ error: 'Order has invalid price.' },
{ filled: { oid: 3, totalSz: '1', avgPx: '2' } },
'waitingForTrigger',
]);
expect(placedOids(res)).toEqual([1, 3]);
});
});
describe('parseOrderResponse', () => {
it('confirms a clean batch', () => {
const r = parseOrderResponse(orderBody([{ resting: { oid: 1 } }, { filled: { oid: 2, totalSz: '1', avgPx: '3' } }]));
expect(r).toMatchObject({ ok: true, needsReconcile: false });
});
it('reports status err as a whole-action rejection', () => {
const r = parseOrderResponse({ status: 'err', response: 'User or API Wallet 0x0000000000000000000000000000000000000000 does not exist.' }); // privacy-allow: placeholder address
expect(r).toMatchObject({ ok: false, reason: 'rejected', kind: 'agentNotAuthorized' });
expect(parseOrderResponse({ status: 'err', response: { weird: 1 } })).toMatchObject({
ok: false,
reason: 'rejected',
message: '{"weird":1}',
});
});
it('treats {status:"ok", statuses:[]} and missing statuses as malformed, not success', () => {
expect(parseOrderResponse(orderBody([]))).toMatchObject({ ok: false, reason: 'malformed', detail: 'empty statuses' });
expect(parseOrderResponse({ status: 'ok', response: { type: 'order' } })).toMatchObject({ ok: false, reason: 'malformed' });
expect(parseOrderResponse({ status: 'weird' })).toMatchObject({ ok: false, reason: 'malformed' });
expect(parseOrderResponse(null)).toMatchObject({ ok: false, reason: 'malformed' });
expect(parseOrderResponse(orderBody([]), { expectedCount: 0 })).toMatchObject({ ok: true, results: [] });
});
it('sets needsReconcile on unknown elements or count mismatch', () => {
expect(parseOrderResponse(orderBody([{ futureShape: true }]))).toMatchObject({ ok: true, needsReconcile: true });
expect(parseOrderResponse(orderBody([{ resting: { oid: 1 } }, { resting: { oid: 2 } }]), { expectedCount: 1 })).toMatchObject({
ok: true,
needsReconcile: true,
});
const short = parseOrderResponse(orderBody([{ resting: { oid: 1 } }]), { expectedCount: 2 });
expect(short).toMatchObject({ ok: true, needsReconcile: true });
});
});
describe('cancel statuses', () => {
it('confirms only the literal "success"', () => {
expect(parseCancelStatus('success')).toEqual({ status: 'success' });
expect(parseCancelStatus('Success')).toMatchObject({ status: 'invalid' });
expect(parseCancelStatus({})).toMatchObject({ status: 'invalid' });
expect(parseCancelStatus(null)).toMatchObject({ status: 'invalid' });
});
it('maps "never placed / already canceled / filled" to alreadyGone', () => {
const r = parseCancelStatus({ error: 'Order was never placed, already canceled, or filled.' });
expect(r).toEqual({ status: 'alreadyGone', message: 'Order was never placed, already canceled, or filled.' });
expect(isCancelConfirmed(r)).toBe(true);
});
it('keeps other errors unconfirmed', () => {
const r = parseCancelStatus({ error: 'Something else' });
expect(r).toMatchObject({ status: 'error', kind: 'unknown' });
expect(isCancelConfirmed(r)).toBe(false);
expect(isCancelConfirmed({ status: 'invalid', reason: 'missing', raw: undefined })).toBe(false);
});
it('pads missing elements and parses whole responses fail-closed', () => {
expect(parseCancelStatuses(['success'], { expectedCount: 2 }).map((r) => r.status)).toEqual(['success', 'invalid']);
const body = (statuses: unknown) => ({ status: 'ok', response: { type: 'cancel', data: { statuses } } });
expect(parseCancelResponse(body(['success']))).toMatchObject({ ok: true, needsReconcile: false });
expect(parseCancelResponse(body([]))).toMatchObject({ ok: false, reason: 'malformed' });
expect(parseCancelResponse({ status: 'err', response: 'bad' })).toMatchObject({ ok: false, reason: 'rejected' });
});
});
describe('parseTwapResponse', () => {
it('parses running, success and inner errors under an outer ok', () => {
expect(parseTwapResponse({ status: 'ok', response: { type: 'twapOrder', data: { status: { running: { twapId: 77 } } } } })).toEqual({
status: 'running',
twapId: 77,
});
expect(parseTwapResponse({ status: 'ok', response: { type: 'twapCancel', data: { status: 'success' } } })).toEqual({ status: 'success' });
expect(
parseTwapResponse({ status: 'ok', response: { type: 'twapOrder', data: { status: { error: 'Invalid TWAP duration: 1 min(s)' } } } }),
).toMatchObject({ status: 'error', kind: 'invalidTwapDuration' });
expect(
parseTwapResponse({ status: 'ok', response: { type: 'twapCancel', data: { status: { error: 'TWAP was never placed, already canceled, or filled.' } } } }),
).toMatchObject({ status: 'error', kind: 'orderNotFound' });
});
it('is fail-closed on other shapes', () => {
expect(parseTwapResponse({ status: 'err', response: 'nope' })).toMatchObject({ status: 'error' });
expect(parseTwapResponse({ status: 'ok', response: { data: { status: { running: { twapId: 'x' } } } } })).toMatchObject({ status: 'invalid' });
expect(parseTwapResponse({ status: 'ok', response: {} })).toMatchObject({ status: 'invalid' });
expect(parseTwapResponse('ok')).toMatchObject({ status: 'invalid' });
});
});
describe('parseActionResponse', () => {
it('accepts only status ok without embedded errors', () => {
expect(parseActionResponse({ status: 'ok', response: { type: 'default' } })).toEqual({ ok: true });
expect(parseActionResponse({ status: 'err', response: 'Invalid leverage value' })).toMatchObject({ ok: false, kind: 'leverage' });
expect(parseActionResponse({ status: 'ok', response: { data: { status: { error: 'x' } } } })).toMatchObject({ ok: false, reason: 'rejected' });
expect(parseActionResponse({ status: 'ok', response: { data: { statuses: ['success', { error: 'y' }] } } })).toMatchObject({
ok: false,
message: 'y',
});
expect(parseActionResponse(undefined)).toMatchObject({ ok: false, reason: 'malformed' });
expect(parseActionResponse({ response: { type: 'default' } })).toMatchObject({ ok: false, reason: 'malformed' });
});
});