| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
- import { WebSocketServer } from 'ws';
- import { PacificaWebSocket } from '../packages/connectors/pacifica/src/wsClient';
- import nacl from 'tweetnacl';
- import bs58 from 'bs58';
- import { signRequest } from '../packages/connectors/pacifica/src/signing';
- describe('PacificaWebSocket', () => {
- let server: WebSocketServer;
- let port: number;
- beforeEach(() => {
- server = new WebSocketServer({ port: 0 });
- const address = server.address();
- if (typeof address === 'string' || !address) {
- throw new Error('Failed to obtain server address');
- }
- port = address.port;
- });
- afterEach(async () => {
- for (const client of server.clients) {
- client.close();
- }
- await new Promise(resolve => server.close(resolve));
- vi.restoreAllMocks();
- });
- it('connects, subscribes, and receives heartbeat responses', async () => {
- const messages: string[] = [];
- const client = new PacificaWebSocket({
- url: `ws://127.0.0.1:${port}`,
- heartbeatIntervalMs: 10
- });
- server.on('connection', socket => {
- socket.on('message', data => {
- messages.push(data.toString());
- socket.send(JSON.stringify({ method: 'pong' }));
- });
- });
- const messagePromise = new Promise<string>(resolve => {
- client.once('message', data => resolve(data.toString()));
- });
- client.subscribe('book.BTC', { depth: 1 });
- client.connect();
- // wait for server to receive login & subscribe
- await new Promise(resolve => setTimeout(resolve, 50));
- expect(messages.length).toBeGreaterThanOrEqual(1);
- const subscribe = JSON.parse(messages[0]!);
- expect(subscribe.method).toBe('subscribe');
- expect(subscribe.params.channel).toBe('book.BTC');
- const serverMessage = await messagePromise;
- expect(serverMessage).toContain('pong');
- client.disconnect();
- });
- it('throws when subscribing to private channel without credentials', () => {
- const client = new PacificaWebSocket({
- url: `ws://127.0.0.1:${port}`
- });
- expect(() => client.subscribe('orders.sub-1')).toThrow(
- 'Missing credentials for private Pacifica channel orders.sub-1'
- );
- });
- it('injects auth params for private channel subscriptions', async () => {
- const seed = Uint8Array.from({ length: 32 }, (_, i) => i + 1);
- const { secretKey, publicKey } = nacl.sign.keyPair.fromSeed(seed);
- const secret = Buffer.from(secretKey).toString('base64');
- const apiKey = bs58.encode(publicKey);
- vi.spyOn(Date, 'now').mockReturnValue(1_700_000_000_000);
- const messages: string[] = [];
- const client = new PacificaWebSocket({
- url: `ws://127.0.0.1:${port}`,
- apiKey,
- secret,
- subaccount: 'sub-1',
- heartbeatIntervalMs: 10
- });
- server.on('connection', socket => {
- socket.on('message', data => {
- messages.push(data.toString());
- });
- });
- const basePayload = {
- method: 'subscribe' as const,
- params: { channel: 'orders.sub-1' }
- };
- const { headers, timestamp } = signRequest(
- { apiKey, secret, subaccount: 'sub-1' },
- 'SUBSCRIBE',
- '/ws',
- basePayload
- );
- const expectedSignature = headers['X-Pacific-Signature'];
- client.subscribe('orders.sub-1');
- client.connect();
- await new Promise(resolve => setTimeout(resolve, 50));
- expect(messages.length).toBeGreaterThanOrEqual(1);
- const subscribe = JSON.parse(messages[0]!);
- expect(subscribe.method).toBe('subscribe');
- expect(subscribe.params.channel).toBe('orders.sub-1');
- expect(subscribe.params.auth).toEqual({
- key: apiKey,
- timestamp,
- signature: expectedSignature,
- subaccount: 'sub-1'
- });
- client.disconnect();
- });
- });
|