Skip to content

Commit

Permalink
update e2e tests to inlcude subscription tests
Browse files Browse the repository at this point in the history
  • Loading branch information
shunjizhan committed Oct 9, 2023
1 parent 3f23af9 commit 0a5cf3d
Show file tree
Hide file tree
Showing 25 changed files with 468 additions and 128 deletions.
2 changes: 1 addition & 1 deletion examples/docker-compose-bodhi-stack.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
services:
mandala-node:
image: ghcr.io/acalanetwork/mandala-node:sha-3267408
image: ghcr.io/acalanetwork/mandala-node:sha-fe67fd1
container_name: mandala-node
ports:
- 9944:9944
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Binary file added scripts/e2e-tests/.yarn/install-state.gz
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Runtime Upgrade E2E Tests
# E2E Tests
## Setup
- install deps and build artifacts
```
yarn
yarn build
```

## For Runtime Upgrade
- start a karura/acala fork
```
npx @acala-network/chopsticks@latest -c configs/karura.yml
Expand All @@ -8,18 +15,22 @@ npx @acala-network/chopsticks@latest -c configs/acala.yml

- start a eth rpc adapter
```
npx @acala-network/eth-rpc-adapter@2.7.7 -e ws://localhost:8000
```

## Run Tests
- install deps and build artifacts
```
yarn
yarn build
npx @acala-network/eth-rpc-adapter@2.7.10 -e ws://localhost:8000
```

- run tests
```
yarn test:karura
yarn test:acala
```

## For Local Mandala Tests
- start a full normal-sealing local mandala stack with subql (node can't be instant sealing if working with subway)
```
docker compose
```

- run tests
```
yarn test:mandala
```
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
87 changes: 87 additions & 0 deletions scripts/e2e-tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
services:
mandala-node:
image: ghcr.io/acalanetwork/mandala-node:sha-fe67fd1
container_name: mandala-node
ports:
- 9944:9944
command:
- --dev
- -levm=debug
- --rpc-external
- --rpc-cors=all
- --rpc-methods=unsafe
- --pruning=archive
# - --instant-sealing

postgres:
image: postgres:12-alpine
container_name: postgres
ports:
- 5432:5432
environment:
POSTGRES_PASSWORD: postgres

subquery-node:
image: acala/evm-subql:2.7.10
container_name: subquery-node
ports:
- 3000:3000
depends_on:
- mandala-node
- postgres
restart: always
extra_hosts:
- host.docker.internal:host-gateway
environment:
DB_USER: postgres
DB_PASS: postgres
DB_DATABASE: postgres
DB_HOST: host.docker.internal
DB_PORT: 5432
command:
- -f=project.yaml # local mandala
- --network-endpoint=ws://mandala-node:9944
- --db-schema=acala_evm
- --log-level=debug
- --unsafe
- --disable-historical

graphql-engine:
image: onfinality/subql-query:v1.4.0
container_name: graphql-engine
ports:
- 3001:3001
depends_on:
- subquery-node
restart: always
extra_hosts:
- host.docker.internal:host-gateway
environment:
DB_USER: postgres
DB_PASS: postgres
DB_DATABASE: postgres
DB_HOST: host.docker.internal
DB_PORT: 5432
PORT: 3001
command:
- --name=acala_evm
- --playground
- --indexer=http://subquery-node:3000

eth-rpc-adapter-server:
image: acala/eth-rpc-adapter:2.7.10
container_name: eth-rpc-adapter-server
restart: always
depends_on:
- mandala-node
- graphql-engine
ports:
- 8546:8546
extra_hosts:
- host.docker.internal:host-gateway
environment:
# - LOCAL_MODE=1
- SUBQL_URL=http://graphql-engine:3001
- ENDPOINT_URL=ws://mandala-node:9944
- PORT=8546
command: yarn start
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@
"dlx": "^0.2.1",
"eslint": "^8.31.0",
"ethereum-waffle": "^3.4.4",
"ethers": "^5.4.7",
"hardhat": "^2.11.1",
"ethers": "~5.7.0",
"hardhat": "~2.13.1",
"hardhat-gas-reporter": "^1.0.8",
"mocha": "^10.2.0",
"solidity-coverage": "^0.8.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { parseEther, parseUnits } from 'ethers/lib/utils';

const one = parseEther('1');

describe('New Runtime', function () {
describe('evm operations', function () {
let deployer: SignerWithAddress;
let user: SignerWithAddress;
let deployerAddress: string;
Expand Down Expand Up @@ -44,6 +44,8 @@ describe('New Runtime', function () {
const tokenAddr = network.name === 'karura' ? KAR_ADDR : ACA_ADDR
const aca = ERC20__factory.connect(tokenAddr, deployer);

// await new Promise((resolve) => setTimeout(resolve, 3000));

const prevBalance = await user.getBalance();
const sendValue = '5.123';
await (await aca.transfer(userAddress, parseUnits(sendValue, 12))).wait();
Expand Down
225 changes: 225 additions & 0 deletions scripts/e2e-tests/test/subscription.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
import { expect } from 'chai';
import { ethers } from 'hardhat';
import { before, after } from 'mocha';
import { hexZeroPad, parseEther } from 'ethers/lib/utils';
import { AcalaJsonRpcProvider, sleep } from '@acala-network/eth-providers';
import WebSocket from 'ws';

import { ERC20, ERC20__factory } from '../typechain-types';
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers';
import { JsonRpcProvider } from '@ethersproject/providers';
import { Contract, Wallet } from 'ethers';

const one = parseEther('1');
const ACA_ADDR = '0x0000000000000000000100000000000000000000';

const ETH_RPC_URL = 'http://localhost:8545';
const ETH_RPC_URL_WS = 'ws://localhost:8545';

describe('eth subscription', () => {
let deployer: SignerWithAddress;
let user: SignerWithAddress;
let provider: JsonRpcProvider;
let aca: ERC20;

const notifications: any[] = [];
let subId0: string;
let subId1: string;
let subId2: string;
let subId3: string;
let ws: WebSocket;

before('setup subscription', async () => {
[deployer, user] = await ethers.getSigners();

provider = new AcalaJsonRpcProvider(ETH_RPC_URL);
aca = ERC20__factory.connect(ACA_ADDR, deployer);

ws = new WebSocket(ETH_RPC_URL_WS);
ws.on('open', () => {
ws.on('message', (data) => {
const parsedData = JSON.parse(data.toString());
notifications.push(parsedData);
});

ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 0,
method: 'eth_subscribe',
params: ['newHeads'],
})
);

ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_subscribe',
params: ['logs', {}],
})
);

const TRANSFER_SELECTOR = '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef';
const USER_ADDR_SELECTOR = hexZeroPad(user.address, 32);

ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 2,
method: 'eth_subscribe',
params: [
'logs',
{
topics: [
TRANSFER_SELECTOR,
null,
[USER_ADDR_SELECTOR],
],
},
],
})
);

ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: 3,
method: 'eth_subscribe',
params: [
'logs',
{
topics: [
'0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55aaaaaaaaaaa', // shouldn't match
],
},
],
})
);
});
});

after(() => {
ws.close();
});

it('get correct subscrption notification', async () => {
const receipt = await (await aca.transfer(user.address, 111222333444555)).wait();

await sleep(3000); // give ws some time to notify

subId0 = notifications.find((n) => n.id === 0).result;
subId1 = notifications.find((n) => n.id === 1).result;
subId2 = notifications.find((n) => n.id === 2).result;
subId3 = notifications.find((n) => n.id === 3).result;

const notification0 = notifications.find((n) => n.params?.subscription === subId0); // new block
const notification1 = notifications.find((n) => n.params?.subscription === subId1); // ACA transfer
const notification2 = notifications.find((n) => n.params?.subscription === subId2); // ACA transfer
const notification3 = notifications.find((n) => n.params?.subscription === subId3); // no match

const curBlockInfo = await provider.send('eth_getBlockByNumber', [receipt.blockNumber, false]);

expect(notification0).to.deep.contains({
jsonrpc: '2.0',
method: 'eth_subscription',
params: {
subscription: subId0,
result: curBlockInfo,
},
});

await sleep(10000); // give subql some time to index
const expectedLog = await provider.send('eth_getLogs', [{ blockHash: curBlockInfo.hash }]);

expect(expectedLog.length).to.equal(1);
delete (expectedLog[0] as any).removed;

expect(notification0).to.not.be.undefined;
expect(notification1).to.not.be.undefined;
expect(notification2).to.not.be.undefined;
expect(notification3).to.be.undefined;

expect(notification1).to.deep.contains({
jsonrpc: '2.0',
method: 'eth_subscription',
params: {
subscription: subId1,
result: expectedLog[0],
},
});

expect(notification2).to.deep.contains({
jsonrpc: '2.0',
method: 'eth_subscription',
params: {
subscription: subId2,
result: expectedLog[0],
},
});
});

it('unsubscribe works', async () => {
notifications.length = 0;

let reqId = 10;
const unsubscribe = async (id: string) => {
ws.send(
JSON.stringify({
jsonrpc: '2.0',
id: reqId++,
method: 'eth_unsubscribe',
params: [id],
})
);

await sleep(300); // delay each msg to make sure result order is correct
};

await unsubscribe(subId0);
await unsubscribe(subId1);
await unsubscribe(subId3);
await unsubscribe(Wallet.createRandom().address);

await sleep(3000); // give ws some time to notify

expect(notifications).to.deep.equal([
{ id: 10, jsonrpc: '2.0', result: true },
{ id: 11, jsonrpc: '2.0', result: true },
{ id: 12, jsonrpc: '2.0', result: true },
{ id: 13, jsonrpc: '2.0', result: false },
]);

// only sub2 is left
notifications.length = 0;
const receipt = await (await aca.transfer(user.address, 1234567654321)).wait();

await sleep(10000); // give ws some time to notify

const notification0 = notifications.find((n) => n.params?.subscription === subId0); // no match
const notification1 = notifications.find((n) => n.params?.subscription === subId1); // no match
const notification2 = notifications.find((n) => n.params?.subscription === subId2); // ACA transfer
const notification3 = notifications.find((n) => n.params?.subscription === subId3); // no match

// after unsubscribe they should not be notified anymore
expect(notification0).to.equal(undefined);
expect(notification1).to.equal(undefined);
expect(notification3).to.equal(undefined);

await sleep(10000); // give subql some time to index
const curBlockInfo = await provider.send('eth_getBlockByNumber', [receipt.blockNumber, false]);
const expectedLog = await provider.send('eth_getLogs', [{ blockHash: curBlockInfo.hash }]);

expect(expectedLog.length).to.equal(1);
delete (expectedLog[0] as any).removed;

expect(notification2).to.deep.contains({
jsonrpc: '2.0',
method: 'eth_subscription',
params: {
subscription: subId2,
result: expectedLog[0],
},
});
});
});
Loading

0 comments on commit 0a5cf3d

Please sign in to comment.