Update orderbook channel and factory tests

This commit is contained in:
Brandon Millman 2018-05-29 12:54:53 -07:00
parent c403dcdabf
commit af395eccda
5 changed files with 54 additions and 24 deletions

View File

@ -59,6 +59,7 @@
"isomorphic-fetch": "^2.2.1", "isomorphic-fetch": "^2.2.1",
"lodash": "^4.17.4", "lodash": "^4.17.4",
"query-string": "^5.0.1", "query-string": "^5.0.1",
"sinon": "^4.0.0",
"websocket": "^1.0.25" "websocket": "^1.0.25"
}, },
"devDependencies": { "devDependencies": {
@ -68,6 +69,7 @@
"@types/lodash": "4.14.104", "@types/lodash": "4.14.104",
"@types/mocha": "^2.2.42", "@types/mocha": "^2.2.42",
"@types/query-string": "^5.0.1", "@types/query-string": "^5.0.1",
"@types/sinon": "^2.2.2",
"@types/websocket": "^0.0.39", "@types/websocket": "^0.0.39",
"async-child-process": "^1.1.1", "async-child-process": "^1.1.1",
"chai": "^4.0.1", "chai": "^4.0.1",

View File

@ -1,21 +1,27 @@
import * as WebSocket from 'websocket'; import * as WebSocket from 'websocket';
import { OrderbookChannel } from './types'; import { OrderbookChannel, OrderbookChannelHandler } from './types';
import { assert } from './utils/assert'; import { assert } from './utils/assert';
import { WebSocketOrderbookChannel } from './ws_orderbook_channel'; import { WebSocketOrderbookChannel } from './ws_orderbook_channel';
export const orderbookChannelFactory = { export const orderbookChannelFactory = {
/** /**
* Instantiates a new WebSocketOrderbookChannel instance * Instantiates a new WebSocketOrderbookChannel instance
* @param url The relayer API base WS url you would like to interact with * @param url The relayer API base WS url you would like to interact with
* @param handler An OrderbookChannelHandler instance that responds to various
* channel updates
* @return An OrderbookChannel Promise * @return An OrderbookChannel Promise
*/ */
async createWebSocketOrderbookChannelAsync(url: string): Promise<OrderbookChannel> { async createWebSocketOrderbookChannelAsync(
url: string,
handler: OrderbookChannelHandler,
): Promise<OrderbookChannel> {
assert.isUri('url', url); assert.isUri('url', url);
assert.isOrderbookChannelHandler('handler', handler);
return new Promise<OrderbookChannel>((resolve, reject) => { return new Promise<OrderbookChannel>((resolve, reject) => {
const client = new WebSocket.w3cwebsocket(url); const client = new WebSocket.w3cwebsocket(url);
client.onopen = () => { client.onopen = () => {
const orderbookChannel = new WebSocketOrderbookChannel(client); const orderbookChannel = new WebSocketOrderbookChannel(client, handler);
resolve(orderbookChannel); resolve(orderbookChannel);
}; };
client.onerror = err => { client.onerror = err => {

View File

@ -11,7 +11,7 @@ export interface Client {
} }
export interface OrderbookChannel { export interface OrderbookChannel {
subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler) => void; subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts) => void;
close: () => void; close: () => void;
} }

View File

@ -9,18 +9,38 @@ import { orderbookChannelFactory } from '../src/orderbook_channel_factory';
chai.config.includeStack = true; chai.config.includeStack = true;
chai.use(dirtyChai); chai.use(dirtyChai);
const expect = chai.expect; const expect = chai.expect;
const emptyOrderbookChannelHandler = {
onSnapshot: () => {
_.noop();
},
onUpdate: () => {
_.noop();
},
onError: () => {
_.noop();
},
onClose: () => {
_.noop();
},
};
describe('orderbookChannelFactory', () => { describe('orderbookChannelFactory', () => {
const websocketUrl = 'ws://localhost:8080'; const websocketUrl = 'ws://localhost:8080';
describe('#createWebSocketOrderbookChannelAsync', () => { describe('#createWebSocketOrderbookChannelAsync', () => {
it('throws when input is not a url', () => { it('throws when input is not a url', () => {
const badInput = 54; const badUrlInput = 54;
const badSubscribeCall = orderbookChannelFactory.createWebSocketOrderbookChannelAsync.bind( expect(
orderbookChannelFactory, orderbookChannelFactory.createWebSocketOrderbookChannelAsync(
badInput, badUrlInput as any,
); emptyOrderbookChannelHandler,
expect(orderbookChannelFactory.createWebSocketOrderbookChannelAsync(badInput as any)).to.be.rejected(); ),
).to.be.rejected();
});
it('throws when handler has the incorrect members', () => {
const badHandlerInput = {};
expect(
orderbookChannelFactory.createWebSocketOrderbookChannelAsync(websocketUrl, badHandlerInput as any),
).to.be.rejected();
}); });
}); });
}); });

View File

@ -2,6 +2,7 @@ import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai'; import * as dirtyChai from 'dirty-chai';
import * as _ from 'lodash'; import * as _ from 'lodash';
import 'mocha'; import 'mocha';
import * as Sinon from 'sinon';
import * as WebSocket from 'websocket'; import * as WebSocket from 'websocket';
import { WebSocketOrderbookChannel } from '../src/ws_orderbook_channel'; import { WebSocketOrderbookChannel } from '../src/ws_orderbook_channel';
@ -26,8 +27,10 @@ const emptyOrderbookChannelHandler = {
describe('WebSocketOrderbookChannel', () => { describe('WebSocketOrderbookChannel', () => {
const websocketUrl = 'ws://localhost:8080'; const websocketUrl = 'ws://localhost:8080';
const client = new WebSocket.w3cwebsocket(websocketUrl); const openClient = new WebSocket.w3cwebsocket(websocketUrl);
const orderbookChannel = new WebSocketOrderbookChannel(client, emptyOrderbookChannelHandler); Sinon.stub(openClient, 'readyState').get(() => WebSocket.w3cwebsocket.OPEN);
Sinon.stub(openClient, 'send').callsFake(_.noop);
const openOrderbookChannel = new WebSocketOrderbookChannel(openClient, emptyOrderbookChannelHandler);
const subscriptionOpts = { const subscriptionOpts = {
baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990', quoteTokenAddress: '0xef7fff64389b814a946f3e92105513705ca6b990',
@ -36,22 +39,21 @@ describe('WebSocketOrderbookChannel', () => {
}; };
describe('#subscribe', () => { describe('#subscribe', () => {
it('throws when subscriptionOpts does not conform to schema', () => { it('throws when subscriptionOpts does not conform to schema', () => {
const badSubscribeCall = orderbookChannel.subscribe.bind( const badSubscribeCall = openOrderbookChannel.subscribe.bind(openOrderbookChannel, {});
orderbookChannel,
{},
emptyOrderbookChannelHandler,
);
expect(badSubscribeCall).throws( expect(badSubscribeCall).throws(
'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"', 'Expected subscriptionOpts to conform to schema /RelayerApiOrderbookChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseTokenAddress", instance requires property "quoteTokenAddress"',
); );
}); });
it('does not throw when inputs are of correct types', () => { it('does not throw when inputs are of correct types', () => {
const goodSubscribeCall = orderbookChannel.subscribe.bind( const goodSubscribeCall = openOrderbookChannel.subscribe.bind(openOrderbookChannel, subscriptionOpts);
orderbookChannel,
subscriptionOpts,
emptyOrderbookChannelHandler,
);
expect(goodSubscribeCall).to.not.throw(); expect(goodSubscribeCall).to.not.throw();
}); });
it('throws when client is closed', () => {
const closedClient = new WebSocket.w3cwebsocket(websocketUrl);
Sinon.stub(closedClient, 'readyState').get(() => WebSocket.w3cwebsocket.CLOSED);
const closedOrderbookChannel = new WebSocketOrderbookChannel(closedClient, emptyOrderbookChannelHandler);
const badSubscribeCall = closedOrderbookChannel.subscribe.bind(closedOrderbookChannel, subscriptionOpts);
expect(badSubscribeCall).throws('WebSocket connection is closed');
});
}); });
}); });