src/assets/names.test.ts
v0.3.0 · 3.3 KB
import { describe, expect, it } from 'vitest';
import {
dexOfCoin,
isSpotCoin,
isValidDexName,
MAX_COIN_NAME_LENGTH,
parseCoin,
qualifyCoin,
stripDexPrefix,
} from './names.js';
describe('parseCoin', () => {
it('parses main perps', () => {
expect(parseCoin('BTC')).toEqual({ market: 'perp', coin: 'BTC', dex: '', base: 'BTC' });
expect(parseCoin('kPEPE')).toMatchObject({ market: 'perp', dex: '', base: 'kPEPE' });
});
it('parses HIP-3 perps with the dex prefix', () => {
expect(parseCoin('xyz:TSLA')).toEqual({ market: 'perp', coin: 'xyz:TSLA', dex: 'xyz', base: 'TSLA' });
});
it('parses spot pair and index forms', () => {
expect(parseCoin('PURR/USDC')).toEqual({ market: 'spot', coin: 'PURR/USDC', form: 'pair', base: 'PURR', quote: 'USDC' });
expect(parseCoin('@107')).toEqual({ market: 'spot', coin: '@107', form: 'index', index: 107 });
expect(parseCoin('@0')).toMatchObject({ form: 'index', index: 0 });
});
it('rejects a doubled dex prefix (normalization bug upstream)', () => {
expect(parseCoin('xyz:xyz:TSLA')).toBeNull();
});
it('rejects malformed names', () => {
for (const bad of ['', ':TSLA', 'xyz:', '@', '@01', '@1a', '@-1', 'A/B/C', '/USDC', 'PURR/', 'xyz:A/B']) {
expect(parseCoin(bad), bad).toBeNull();
}
expect(parseCoin(`BTC${String.fromCharCode(1)}`)).toBeNull();
expect(parseCoin('A'.repeat(MAX_COIN_NAME_LENGTH + 1))).toBeNull();
expect(parseCoin(undefined as unknown as string)).toBeNull();
});
});
describe('isSpotCoin', () => {
it('detects spot by "/" or "@"', () => {
expect(isSpotCoin('PURR/USDC')).toBe(true);
expect(isSpotCoin('@85')).toBe(true);
expect(isSpotCoin('BTC')).toBe(false);
expect(isSpotCoin('xyz:TSLA')).toBe(false);
});
});
describe('qualifyCoin', () => {
it('adds the prefix to bare HIP-3 names (old meta format, position.coin quirk)', () => {
expect(qualifyCoin('xyz', 'TSLA')).toBe('xyz:TSLA');
});
it('is idempotent for already prefixed names (no xyz:xyz:)', () => {
expect(qualifyCoin('xyz', 'xyz:TSLA')).toBe('xyz:TSLA');
expect(qualifyCoin('xyz', qualifyCoin('xyz', 'SNDK'))).toBe('xyz:SNDK');
expect(qualifyCoin('xyz', 'xyz:xyz:SNDK')).toBe('xyz:SNDK');
});
it('leaves main-dex names unchanged', () => {
expect(qualifyCoin('', 'BTC')).toBe('BTC');
});
it('does not strip a different dex prefix', () => {
expect(qualifyCoin('xyz', 'flx:ABC')).toBe('xyz:flx:ABC');
expect(parseCoin(qualifyCoin('xyz', 'flx:ABC'))).toBeNull();
});
});
describe('dexOfCoin / stripDexPrefix', () => {
it('extracts the dex', () => {
expect(dexOfCoin('xyz:TSLA')).toBe('xyz');
expect(dexOfCoin('BTC')).toBe('');
expect(dexOfCoin('@85')).toBe('');
expect(dexOfCoin('xyz:xyz:A')).toBeNull();
});
it('strips the dex prefix of perps only', () => {
expect(stripDexPrefix('xyz:INTC')).toBe('INTC');
expect(stripDexPrefix('BTC')).toBe('BTC');
expect(stripDexPrefix('PURR/USDC')).toBe('PURR/USDC');
});
});
describe('isValidDexName', () => {
it('accepts plain dex names and rejects separators', () => {
expect(isValidDexName('xyz')).toBe(true);
expect(isValidDexName('mkts')).toBe(true);
for (const bad of ['', 'a:b', 'a/b', '@a', 'a b']) expect(isValidDexName(bad), bad).toBe(false);
});
});