Skip to content

Commit 02cd9e6

Browse files
chore: Cherry pick 0 46 #2340 (#2346)
feat: added acceptance test coverage for standard web socket (#2316) (#2340) feat: added acceptance test coverage for standard web socket Signed-off-by: Logan Nguyen <[email protected]> Signed-off-by: Alfredo Gutierrez <[email protected]> Co-authored-by: Logan Nguyen <[email protected]>
1 parent 881cc43 commit 02cd9e6

17 files changed

+771
-626
lines changed

packages/ws-server/src/webSocketServer.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ app.ws.use(async (ctx) => {
150150
)}.`,
151151
);
152152
ctx.websocket.send(JSON.stringify(jsonResp(request.id, error, undefined)));
153+
return;
153154
}
154155

155156
// Increment metrics for the received method

packages/ws-server/tests/acceptance/blockNumber.spec.ts

Lines changed: 33 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,50 +21,56 @@
2121
// external resources
2222
import { expect } from 'chai';
2323
import { ethers, WebSocketProvider } from 'ethers';
24-
import RelayClient from '@hashgraph/json-rpc-server/tests/clients/relayClient';
24+
import { WsTestConstant, WsTestHelper } from '../helper';
2525

2626
describe('@release @web-socket eth_blockNumber', async function () {
27-
const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`;
2827
const METHOD_NAME = 'eth_blockNumber';
2928
const INVALID_PARAMS = [
3029
['hedera', 'hbar'],
3130
['websocket', 'rpc', 'invalid'],
3231
];
3332

34-
let relayClient: RelayClient, wsProvider: WebSocketProvider;
35-
36-
before(async () => {
37-
// @ts-ignore
38-
const { relay } = global;
39-
relayClient = relay;
40-
});
33+
let ethersWsProvider: WebSocketProvider;
4134

4235
beforeEach(async () => {
43-
wsProvider = new ethers.WebSocketProvider(WS_RELAY_URL);
36+
ethersWsProvider = new ethers.WebSocketProvider(WsTestConstant.WS_RELAY_URL);
4437
});
4538

4639
afterEach(async () => {
47-
if (wsProvider) {
48-
await wsProvider.destroy();
49-
}
40+
if (ethersWsProvider) await ethersWsProvider.destroy();
41+
});
42+
43+
after(async () => {
44+
// expect all the connections to the WS server to be closed after all
45+
expect(global.socketServer._connections).to.eq(0);
5046
});
5147

52-
for (const params of INVALID_PARAMS) {
53-
it(`Should throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
54-
try {
55-
await wsProvider.send(METHOD_NAME, params);
56-
expect(true).to.eq(false);
57-
} catch (error) {
58-
expect(error.error).to.exist;
59-
expect(error.error.code).to.eq(-32602);
60-
}
48+
describe(WsTestConstant.STANDARD_WEB_SOCKET, () => {
49+
for (const params of INVALID_PARAMS) {
50+
it(`Should fail ${METHOD_NAME} on ${WsTestConstant.STANDARD_WEB_SOCKET} and throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
51+
await WsTestHelper.assertFailInvalidParamsStandardWebSocket(METHOD_NAME, params);
52+
});
53+
}
54+
55+
it(`Should execute ${METHOD_NAME} on ${WsTestConstant.STANDARD_WEB_SOCKET} and handle valid requests correctly`, async () => {
56+
const response = await WsTestHelper.sendRequestToStandardWebSocket(METHOD_NAME, []);
57+
WsTestHelper.assertJsonRpcObject(response);
58+
expect(Number(response.result)).to.gte(0);
59+
expect(response.result.startsWith('0x')).to.be.true;
6160
});
62-
}
61+
});
6362

64-
it('Should handle valid requests correctly', async () => {
65-
const result = await wsProvider.send(METHOD_NAME, []);
66-
const expectedResult = await relayClient.call(METHOD_NAME, []);
63+
describe(WsTestConstant.ETHERS_WS_PROVIDER, () => {
64+
for (const params of INVALID_PARAMS) {
65+
it(`Should fail ${METHOD_NAME} on ${WsTestConstant.ETHERS_WS_PROVIDER} and throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
66+
await WsTestHelper.assertFailInvalidParamsEthersWsProvider(ethersWsProvider, METHOD_NAME, params);
67+
});
68+
}
6769

68-
expect(result).to.eq(expectedResult);
70+
it(`Should execute ${METHOD_NAME} on ${WsTestConstant.ETHERS_WS_PROVIDER} and handle valid requests correctly`, async () => {
71+
const response = await ethersWsProvider.send(METHOD_NAME, []);
72+
expect(Number(response)).to.gte(0);
73+
expect(response.startsWith('0x')).to.be.true;
74+
});
6975
});
7076
});

packages/ws-server/tests/acceptance/call.spec.ts

Lines changed: 88 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,16 @@
2121
// external resources
2222
import { expect } from 'chai';
2323
import { ethers, WebSocketProvider } from 'ethers';
24+
import { WsTestConstant, WsTestHelper } from '../helper';
2425
import { Utils } from '@hashgraph/json-rpc-server/tests/helpers/utils';
2526
import ERC20MockJson from '@hashgraph/json-rpc-server/tests/contracts/ERC20Mock.json';
2627
import { AliasAccount } from '@hashgraph/json-rpc-server/tests/clients/servicesClient';
2728

2829
describe('@release @web-socket eth_call', async function () {
29-
const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`;
3030
const METHOD_NAME = 'eth_call';
31-
const FAKE_TX_HASH = `0x${'00'.repeat(20)}`;
3231
const INVALID_PARAMS = [
3332
['{}', false, '0x0'],
3433
["{ to: '0xabcdef', data: '0x1a2b3c4d' }", 36, ''],
35-
[{ to: FAKE_TX_HASH, data: 36 }, 'latest'],
3634
];
3735

3836
const INVALID_TX_INFO = [
@@ -60,76 +58,111 @@ describe('@release @web-socket eth_call', async function () {
6058
},
6159
];
6260

63-
let accounts: AliasAccount[] = [];
64-
let requestId: string, wsProvider: WebSocketProvider;
65-
let erc20TokenAddr: string, erc20EtherInterface: ethers.Interface;
61+
let requestId: string,
62+
erc20TokenAddr: string,
63+
accounts: AliasAccount[] = [],
64+
ethersWsProvider: WebSocketProvider,
65+
erc20EtherInterface: ethers.Interface;
6666

6767
before(async () => {
68-
// @ts-ignore
69-
const { servicesNode, relay } = global;
70-
71-
accounts[0] = await servicesNode.createAliasAccount(100, relay.provider, requestId);
72-
await new Promise((r) => setTimeout(r, 3000));
68+
accounts[0] = await global.servicesNode.createAliasAccount(100, global.relay.provider, requestId);
69+
await new Promise((r) => setTimeout(r, 1000)); // wait for accounts[0] to propagate
7370

7471
const erc20Contract = await Utils.deployContractWithEthers(
7572
[TOKEN_NAME, TOKEN_SYMBOL, accounts[0].address, TOKEN_INIT_SUPPLY],
7673
ERC20MockJson,
7774
accounts[0].wallet,
78-
relay,
75+
global.relay,
7976
);
8077

8178
erc20TokenAddr = await erc20Contract.getAddress();
8279
erc20EtherInterface = new ethers.Interface(ERC20MockJson.abi);
8380
});
8481

8582
beforeEach(async () => {
86-
wsProvider = new ethers.WebSocketProvider(WS_RELAY_URL);
87-
await new Promise((resolve) => setTimeout(resolve, 1000));
83+
ethersWsProvider = new ethers.WebSocketProvider(WsTestConstant.WS_RELAY_URL);
8884
});
8985

9086
afterEach(async () => {
91-
if (wsProvider) {
92-
await wsProvider.destroy();
93-
await new Promise((resolve) => setTimeout(resolve, 1000));
87+
if (ethersWsProvider) await ethersWsProvider.destroy();
88+
});
89+
90+
after(async () => {
91+
// expect all the connections to be closed after all
92+
expect(global.socketServer._connections).to.eq(0);
93+
});
94+
95+
describe(WsTestConstant.STANDARD_WEB_SOCKET, () => {
96+
for (const params of INVALID_PARAMS) {
97+
it(`Should fail ${METHOD_NAME} on ${WsTestConstant.STANDARD_WEB_SOCKET} and throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
98+
await WsTestHelper.assertFailInvalidParamsStandardWebSocket(METHOD_NAME, params);
99+
});
100+
}
101+
102+
for (const params of INVALID_TX_INFO) {
103+
it(`Should fail ${METHOD_NAME} on ${
104+
WsTestConstant.STANDARD_WEB_SOCKET
105+
} and handle invalid TX_INFO. params=[${JSON.stringify(params)}]`, async () => {
106+
await WsTestHelper.assertFailInvalidParamsStandardWebSocket(METHOD_NAME, params);
107+
});
108+
}
109+
110+
for (const data of VALID_DATA) {
111+
it(`Should execute ${METHOD_NAME} on ${WsTestConstant.STANDARD_WEB_SOCKET} and handle valid requests correctly`, async () => {
112+
const tx = {
113+
to: erc20TokenAddr,
114+
data: data.sighash,
115+
};
116+
117+
const response = await WsTestHelper.sendRequestToStandardWebSocket(METHOD_NAME, [tx, 'latest']);
118+
WsTestHelper.assertJsonRpcObject(response);
119+
120+
const outputASCII = erc20EtherInterface.decodeFunctionResult(
121+
erc20EtherInterface.getFunction(data.sighash)!,
122+
response.result,
123+
);
124+
expect(outputASCII[0]).to.eq(data.output);
125+
});
94126
}
95127
});
96128

97-
for (const params of INVALID_PARAMS) {
98-
it(`Should throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
99-
try {
100-
await wsProvider.send(METHOD_NAME, params);
101-
expect(true).to.eq(false);
102-
} catch (error) {
103-
expect(error.info).to.exist;
104-
expect(error.info.error.code).to.eq(-32602);
105-
}
106-
});
107-
}
108-
109-
for (const params of INVALID_TX_INFO) {
110-
it(`Should handle invalid TX_INFO A. params=[${JSON.stringify(params)}]`, async () => {
111-
try {
112-
await wsProvider.send(METHOD_NAME, [...params]);
113-
expect(true).to.eq(false);
114-
} catch (error) {
115-
expect(error).to.exist;
116-
expect(error.argument).to.eq('address');
117-
expect(error.code).to.eq('INVALID_ARGUMENT');
118-
expect(error.shortMessage).to.eq('invalid address');
119-
}
120-
});
121-
}
122-
123-
for (const data of VALID_DATA) {
124-
it(`Should handle valid requests correctly`, async () => {
125-
const tx = {
126-
to: erc20TokenAddr,
127-
data: data.sighash,
128-
};
129-
130-
const output = await wsProvider.send(METHOD_NAME, [tx, 'latest']);
131-
const result = erc20EtherInterface.decodeFunctionResult(erc20EtherInterface.getFunction(data.sighash)!, output);
132-
expect(result[0]).to.eq(data.output);
133-
});
134-
}
129+
describe(WsTestConstant.ETHERS_WS_PROVIDER, () => {
130+
for (const params of INVALID_PARAMS) {
131+
it(`Should fail ${METHOD_NAME} on ${WsTestConstant.ETHERS_WS_PROVIDER} and throw predefined.INVALID_PARAMETERS if the request's params variable is invalid. params=[${params}]`, async () => {
132+
await WsTestHelper.assertFailInvalidParamsEthersWsProvider(ethersWsProvider, METHOD_NAME, params);
133+
});
134+
}
135+
136+
for (const params of INVALID_TX_INFO) {
137+
it(`Should fail ${METHOD_NAME} on ${
138+
WsTestConstant.ETHERS_WS_PROVIDER
139+
} and handle invalid TX_INFO. params=[${JSON.stringify(params)}]`, async () => {
140+
try {
141+
await ethersWsProvider.send(METHOD_NAME, params);
142+
expect(true).to.eq(false);
143+
} catch (error) {
144+
expect(error).to.exist;
145+
expect(error.argument).to.eq('address');
146+
expect(error.code).to.eq('INVALID_ARGUMENT');
147+
expect(error.shortMessage).to.eq('invalid address');
148+
}
149+
});
150+
}
151+
152+
for (const data of VALID_DATA) {
153+
it(`Should execute ${METHOD_NAME} on ${WsTestConstant.ETHERS_WS_PROVIDER} and handle valid requests correctly`, async () => {
154+
const tx = {
155+
to: erc20TokenAddr,
156+
data: data.sighash,
157+
};
158+
159+
const output = await ethersWsProvider.send(METHOD_NAME, [tx, 'latest']);
160+
const outputASCII = erc20EtherInterface.decodeFunctionResult(
161+
erc20EtherInterface.getFunction(data.sighash)!,
162+
output,
163+
);
164+
expect(outputASCII[0]).to.eq(data.output);
165+
});
166+
}
167+
});
135168
});

packages/ws-server/tests/acceptance/estimateGas.spec.ts

Lines changed: 28 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,51 +19,52 @@
1919
*/
2020

2121
// external resources
22-
import WebSocket from 'ws';
2322
import { expect } from 'chai';
24-
import { Contract, ethers, WebSocketProvider } from 'ethers';
23+
import { ethers, WebSocketProvider } from 'ethers';
24+
import { TransactionReceipt } from '@hashgraph/sdk';
25+
import { WsTestConstant, WsTestHelper } from '../helper';
2526
import basicContractJson from '@hashgraph/json-rpc-server/tests/contracts/Basic.json';
2627
import { AliasAccount } from '@hashgraph/json-rpc-server/tests/clients/servicesClient';
2728

2829
describe('@release @web-socket eth_estimateGas', async function () {
29-
// @ts-ignore
30-
const { servicesNode, relay } = global;
31-
32-
const WS_RELAY_URL = `${process.env.WS_RELAY_URL}`;
33-
const BASIC_CONTRACT_PING_CALL_DATA = '0x5c36b186';
30+
const METHOD_NAME = 'eth_estimateGas';
3431
const PING_CALL_ESTIMATED_GAS = '0x6122';
32+
const BASIC_CONTRACT_PING_CALL_DATA = '0x5c36b186';
3533

3634
let accounts: AliasAccount[] = [],
37-
basicContract: Contract,
35+
basicContract: TransactionReceipt,
3836
currentPrice: number,
3937
expectedGas: number,
4038
gasPriceDeviation: number,
41-
requestId: string,
42-
wsProvider: WebSocketProvider,
43-
webSocket: WebSocket;
39+
ethersWsProvider: WebSocketProvider;
4440

4541
before(async () => {
46-
accounts[0] = await servicesNode.createAliasAccount(100, relay.provider, requestId);
47-
basicContract = await servicesNode.deployContract(basicContractJson);
48-
wsProvider = await new ethers.WebSocketProvider(WS_RELAY_URL);
42+
accounts[0] = await global.servicesNode.createAliasAccount(100, global.relay.provider);
43+
basicContract = await global.servicesNode.deployContract(basicContractJson);
4944

50-
webSocket = new WebSocket(WS_RELAY_URL);
51-
currentPrice = await relay.gasPrice(requestId);
45+
currentPrice = await global.relay.gasPrice();
5246
expectedGas = parseInt(PING_CALL_ESTIMATED_GAS, 16);
5347

5448
// handle deviation in gas price of 20%. On testnet gas price can vary depending on the network congestion
5549
gasPriceDeviation = parseFloat(expectedGas.toString() ?? '0.2');
5650
});
5751

52+
beforeEach(async () => {
53+
ethersWsProvider = new ethers.WebSocketProvider(WsTestConstant.WS_RELAY_URL);
54+
});
55+
5856
afterEach(async () => {
59-
if (wsProvider) {
60-
await wsProvider.destroy();
61-
}
57+
if (ethersWsProvider) await ethersWsProvider.destroy();
58+
});
59+
60+
after(async () => {
61+
// expect all the connections to be closed after all
62+
expect(global.socketServer._connections).to.eq(0);
6263
});
6364

6465
it('@release should execute "eth_estimateGas" for contract call, using a websocket provider', async function () {
65-
const estimatedGas = await wsProvider.estimateGas({
66-
to: `0x${basicContract.contractId.toSolidityAddress()}`,
66+
const estimatedGas = await ethersWsProvider.estimateGas({
67+
to: `0x${basicContract.contractId?.toSolidityAddress()}`,
6768
data: BASIC_CONTRACT_PING_CALL_DATA,
6869
});
6970

@@ -72,35 +73,11 @@ describe('@release @web-socket eth_estimateGas', async function () {
7273
expect(Number(estimatedGas)).to.be.greaterThan(currentPrice * (1 - gasPriceDeviation));
7374
});
7475

75-
it('should return the code through a websocket', (done) => {
76-
webSocket.on('open', function open() {
77-
webSocket.send(
78-
JSON.stringify({
79-
id: 1,
80-
jsonrpc: '2.0',
81-
method: 'eth_estimateGas',
82-
params: [
83-
{
84-
to: `0x${basicContract.contractId.toSolidityAddress()}`,
85-
data: BASIC_CONTRACT_PING_CALL_DATA,
86-
},
87-
],
88-
}),
89-
);
90-
});
91-
let responseCounter = 0;
92-
webSocket.on('message', function incoming(data) {
93-
const response = JSON.parse(data);
94-
if (response.result) {
95-
expect(Number(response.result)).to.be.lessThan(currentPrice * (1 + gasPriceDeviation));
96-
expect(Number(response.result)).to.be.greaterThan(currentPrice * (1 - gasPriceDeviation));
97-
}
98-
99-
responseCounter++;
100-
if (responseCounter > 1) {
101-
webSocket.close();
102-
done();
103-
}
104-
});
76+
it('should return the code through a websocket', async () => {
77+
const tx = { to: `0x${basicContract.contractId?.toSolidityAddress()}`, data: BASIC_CONTRACT_PING_CALL_DATA };
78+
const response = await WsTestHelper.sendRequestToStandardWebSocket(METHOD_NAME, [tx]);
79+
WsTestHelper.assertJsonRpcObject(response);
80+
expect(Number(response.result)).to.be.lessThan(currentPrice * (1 + gasPriceDeviation));
81+
expect(Number(response.result)).to.be.greaterThan(currentPrice * (1 - gasPriceDeviation));
10582
});
10683
});

0 commit comments

Comments
 (0)