← Back
import { describe, it, expect, beforeEach } from 'vitest';

// state.js exports a singleton instance
const sm = require('../server/state.js');

describe('StateManager', () => {
  beforeEach(() => {
    // Clear state between tests
    sm.books.clear();
    sm.binHistory.clear();
  });

  describe('initBook', () => {
    it('creates a book entry for a symbol', () => {
      sm.initBook('BTCUSDT', [['50000', '1.5']], [['50100', '2.0']]);
      expect(sm.books.has('BTCUSDT')).toBe(true);
    });

    it('stores bids and asks with notional values', () => {
      sm.initBook('BTCUSDT', [['50000', '1.5']], [['50100', '2.0']]);
      const book = sm.books.get('BTCUSDT');

      expect(book.bids.has(50000)).toBe(true);
      expect(book.bids.get(50000).notional).toBe(75000); // 50000 * 1.5

      expect(book.asks.has(50100)).toBe(true);
      expect(book.asks.get(50100).notional).toBe(100200); // 50100 * 2.0
    });

    it('sets firstSeen and lastUpdate timestamps', () => {
      const before = Date.now();
      sm.initBook('ETHUSDT', [['3000', '10']], []);
      const after = Date.now();

      const book = sm.books.get('ETHUSDT');
      const bid = book.bids.get(3000);
      expect(bid.firstSeen).toBeGreaterThanOrEqual(before);
      expect(bid.firstSeen).toBeLessThanOrEqual(after);
    });

    it('does not overwrite existing book', () => {
      sm.initBook('BTCUSDT', [['50000', '1']], []);
      sm.initBook('BTCUSDT', [['51000', '2']], []);

      const book = sm.books.get('BTCUSDT');
      // Both should exist since initBook adds to existing
      expect(book.bids.has(50000)).toBe(true);
      expect(book.bids.has(51000)).toBe(true);
    });
  });

  describe('processDelta', () => {
    beforeEach(() => {
      sm.initBook('BTCUSDT', [['50000', '1']], [['50100', '1']]);
      sm.books.get('BTCUSDT').lastUpdateId = 100;
    });

    it('ignores deltas with u <= lastUpdateId', () => {
      sm.processDelta('BTCUSDT', { u: 100, b: [['49000', '5']], a: [] });
      const book = sm.books.get('BTCUSDT');
      expect(book.bids.has(49000)).toBe(false);
    });

    it('applies bid updates', () => {
      sm.processDelta('BTCUSDT', { u: 101, b: [['49000', '3']], a: [] });
      const book = sm.books.get('BTCUSDT');
      expect(book.bids.has(49000)).toBe(true);
      expect(book.bids.get(49000).notional).toBe(147000); // 49000 * 3
    });

    it('removes levels with qty=0', () => {
      sm.processDelta('BTCUSDT', { u: 101, b: [['50000', '0']], a: [] });
      const book = sm.books.get('BTCUSDT');
      expect(book.bids.has(50000)).toBe(false);
    });

    it('updates existing level notional without changing firstSeen', () => {
      const book = sm.books.get('BTCUSDT');
      const originalFirstSeen = book.bids.get(50000).firstSeen;

      sm.processDelta('BTCUSDT', { u: 101, b: [['50000', '5']], a: [] });

      expect(book.bids.get(50000).notional).toBe(250000); // 50000 * 5
      expect(book.bids.get(50000).firstSeen).toBe(originalFirstSeen);
    });

    it('ignores delta for unknown symbol', () => {
      // Should not throw
      sm.processDelta('UNKNOWN', { u: 200, b: [['100', '1']], a: [] });
    });
  });
});

📜 Git History

b873b03feat: add deploy/ops scripts, vitest config, and expanded test suite9 weeks ago
Show last diff
Loading...