feat: add idle timeout and fix subscription race (#12093)
This commit is contained in:
@ -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};
|
||||
|
68
web3.js/test/websocket.test.js
Normal file
68
web3.js/test/websocket.test.js
Normal 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);
|
||||
});
|
||||
});
|
Reference in New Issue
Block a user