feat: add idle timeout and fix subscription race (#12093)

This commit is contained in:
Justin Starry
2020-09-07 23:12:22 +08:00
committed by GitHub
parent ee646aa7a2
commit cfe9b8b744
5 changed files with 115 additions and 54 deletions

View File

@ -1,10 +1,6 @@
// @flow
import {
Client as RpcWebSocketClient,
NodeWebSocketTypeOptions,
IWSClientAdditionalOptions,
} from 'rpc-websockets';
import {Client as LiveClient} from 'rpc-websockets';
// Define TEST_LIVE in the environment to test against the real full node
// identified by `url` instead of using the mock
@ -12,49 +8,24 @@ export const mockRpcEnabled = !process.env.TEST_LIVE;
let mockNotice = true;
export class Client {
client: RpcWebSocketClient;
constructor(
url: string,
options: NodeWebSocketTypeOptions & IWSClientAdditionalOptions,
) {
//console.log('MockClient', url, options);
if (!mockRpcEnabled) {
if (mockNotice) {
console.log(
'Note: rpc-websockets mock is disabled, testing live against',
url,
);
mockNotice = false;
}
this.client = new RpcWebSocketClient(url, options);
class MockClient {
constructor(url: string) {
if (mockNotice) {
console.log(
'Note: rpc-websockets mock is disabled, testing live against',
url,
);
mockNotice = false;
}
}
connect() {
if (!mockRpcEnabled) {
return this.client.connect();
}
}
close() {
if (!mockRpcEnabled) {
return this.client.close();
}
}
on(event: string, callback: Function) {
if (!mockRpcEnabled) {
return this.client.on(event, callback);
}
//console.log('on', event);
}
async call(method: string, params: Object): Promise<Object> {
if (!mockRpcEnabled) {
return await this.client.call(method, params);
}
connect() {}
close() {}
on() {}
call(): Promise<Object> {
throw new Error('call unsupported');
}
}
const Client = mockRpcEnabled ? MockClient : LiveClient;
export {Client};

View File

@ -0,0 +1,68 @@
// @flow
import bs58 from 'bs58';
import {Connection} from '../src';
import {url} from './url';
import {mockRpcEnabled} from './__mocks__/node-fetch';
import {sleep} from '../src/util/sleep';
describe('websocket', () => {
if (mockRpcEnabled) {
test('no-op', () => {});
console.log('non-live test skipped');
return;
}
const connection = new Connection(url);
test('connect and disconnect', async () => {
const testSignature = bs58.encode(Buffer.alloc(64));
const id = connection.onSignature(testSignature, () => {});
// wait for websocket to connect
await sleep(100);
expect(connection._rpcWebSocketConnected).toBe(true);
expect(connection._rpcWebSocketHeartbeat).not.toBe(null);
// test if socket is open
await connection._rpcWebSocket.notify('ping');
await connection.removeSignatureListener(id);
expect(connection._rpcWebSocketConnected).toBe(false);
expect(connection._rpcWebSocketHeartbeat).not.toBe(null);
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
// wait for websocket to disconnect
await sleep(1100);
expect(connection._rpcWebSocketConnected).toBe(false);
expect(connection._rpcWebSocketHeartbeat).toBe(null);
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
// test if socket is closed
await expect(connection._rpcWebSocket.notify('ping')).rejects.toThrow(
'socket not ready',
);
});
test('idle timeout', async () => {
const testSignature = bs58.encode(Buffer.alloc(64));
const id = connection.onSignature(testSignature, () => {});
// wait for websocket to connect
await sleep(100);
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
await connection.removeSignatureListener(id);
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
const nextId = connection.onSignature(testSignature, () => {});
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
await connection.removeSignatureListener(nextId);
expect(connection._rpcWebSocketIdleTimeout).not.toBe(null);
// wait for websocket to disconnect
await sleep(1100);
expect(connection._rpcWebSocketIdleTimeout).toBe(null);
});
});