Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: e2e tests #343

Merged
merged 30 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
1ece9b3
added e2e tests
bee344 Jan 8, 2024
96df8ae
removed injected registry
bee344 Jan 8, 2024
05710cb
merged main changes
bee344 Jan 9, 2024
a053088
linting
bee344 Jan 9, 2024
caf5037
added rococo
bee344 Jan 9, 2024
afca19f
Merge branch 'main' into anp-e2e
bee344 Jan 9, 2024
0206365
added rococo asset hub to registry
bee344 Jan 9, 2024
bb2caa8
removed .toLocaleLowerCase
bee344 Jan 9, 2024
ac66157
upload for testing
bee344 Jan 10, 2024
e0ea727
failing to decode poolAssets
bee344 Jan 11, 2024
7f41851
linting
bee344 Jan 11, 2024
729e799
added logger
bee344 Jan 12, 2024
6730c62
housekeeping and README
bee344 Jan 12, 2024
e1ac10d
Update medium-network.toml
bee344 Jan 13, 2024
0f9cd83
Update consts.ts
bee344 Jan 13, 2024
63df018
Added end of file newline to zombienet.sh
bee344 Jan 13, 2024
7559b41
modified configs to work with v1.6
bee344 Jan 29, 2024
6801ed5
rebase
bee344 Jan 29, 2024
2d19a0b
Update e2e-tests/README.md
bee344 Jan 29, 2024
d96e7c9
Update e2e-tests/README.md
bee344 Jan 29, 2024
5a8224a
Update e2e-tests/balance.ts
bee344 Jan 29, 2024
8b28df4
Update e2e-tests/executor.ts
bee344 Jan 29, 2024
9520902
Grammar
bee344 Jan 29, 2024
67f5ca3
Update ApiPromise import
bee344 Jan 30, 2024
993435c
updated balanceTracker's descriptions
bee344 Jan 30, 2024
e7ca12d
removed unused import
bee344 Jan 30, 2024
fe12443
linting
bee344 Jan 30, 2024
ede8f0c
Merge branch 'anp-e2e' of github.com:paritytech/asset-transfer-api in…
bee344 Jan 30, 2024
a27717a
Merge branch 'main' into anp-e2e
bee344 Jan 30, 2024
fa16b17
linting
bee344 Jan 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ zombienet-macos
zombienet-linux-arm64
zombienet-linux-x64
zombienet.log
zombienet/bin/*

# Binaries
/bin/*
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,7 @@ From the root directory run `./<zombienet_binary_name> -p native spawn ./zombien
### Create an asset

From the root directory run `yarn start:zombienet-post-script`. You can run this right after running your zombienet network.

## E2E Testing

You can access the E2E tests and its documentation [here](./e2e-tests/).
36 changes: 36 additions & 0 deletions e2e-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
## E2E Tests

End-to-end tests that run on a zombienet testnet.

**NOTE: tested using polkadot v1.4.0**

### Setup

To setup the testing environment you need to first download the `polkadot`, `polkadot-execute-worker`, `polkadot-prepare-worker` and `polkadot-parachain` from the `polkadot-sdk` [release page](https://github.com/paritytech/polkadot-sdk/releases/latest), as well as the `trappist-node` from its [release page](https://github.com/paritytech/trappist/releases/latest), and place them in the `../zombienet/bin/` folder.

You also need to have the latest `zombienet` executable in the `../zombienet/` folder, which you can download from [here](https://github.com/paritytech/zombienet/releases/latest).

### Launching zombienet

To launch the zombienet, all you need to do is run the following commands:
bee344 marked this conversation as resolved.
Show resolved Hide resolved
```bash
$ yarn build && yarn e2e:build
```
Then you need to run:
bee344 marked this conversation as resolved.
Show resolved Hide resolved
```bash
$ yarn e2e:zombienet
```
And this will launch the zombienet using the config file located in the `../zombienet/` directory. Once it finished its setup, you can proceed to the following step.

### Launching the tests

For testing, we provide 4 options:

* Testing liquidity tokens transfers with the command `yarn e2e:liquidity-assets`.
* Testing foreign assets transfers with the command `yarn e2e:foreign-assets`.
* Testing local transferss with the command `yarn e2e:local`.
* Testing assets transfers with the command `yarn e2e:assets`.

Each of these commands will run the appropiate script to setup the basics, located in `../scripts/`, wait for it to finish setting up the testing environment, and then go through the tests indicated in the `./tests/index.ts` file for the chosen option.
bee344 marked this conversation as resolved.
Show resolved Hide resolved

After each testing suite has been completed, it's recommended to restart the zombienet before running another test suite.
118 changes: 118 additions & 0 deletions e2e-tests/balance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

import { ApiPromise } from '@polkadot/api';
bee344 marked this conversation as resolved.
Show resolved Hide resolved
import type { FrameSystemAccountInfo, PalletAssetsAssetAccount } from '@polkadot/types/lookup';
import type { Option } from '@polkadot/types-codec';
bee344 marked this conversation as resolved.
Show resolved Hide resolved
export interface IBalance {
initial: [string, number][];
final: [string, number][];
}

/**
*
bee344 marked this conversation as resolved.
Show resolved Hide resolved
* @param api api instance
* @param test name of the test currently being ran
* @param address address of the account whose balance is being queried
* @param assetIds Ids of the assets being queried
* @param balance initial balance for the account queried
* @returns instance of IBalance containing the initial and optionally the final
* balance of the account queried
*/
export const balanceTracker = async (
api: ApiPromise,
test: string,
address: string,
assetIds: string[],
balance?: IBalance,
): Promise<IBalance> => {
let balances: IBalance = { initial: [], final: [] };
let accountInfo: FrameSystemAccountInfo | Option<PalletAssetsAssetAccount>;
switch (test) {
case '--foreign-assets':
if (!balance) {
for (const assetId of assetIds) {
accountInfo = (await api.query.foreignAssets.account(assetId, address)) as Option<PalletAssetsAssetAccount>;
if (accountInfo.valueOf() === null) {
balances.initial.push([assetId, 0]);
} else {
balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
} else {
balances = balance;
for (const assetId of assetIds) {
accountInfo = (await api.query.foreignAssets.account(assetId, address)) as Option<PalletAssetsAssetAccount>;
balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
return balances;
case '--liquidity-assets':
if (!balance) {
for (const assetId of assetIds) {
accountInfo = await api.query.poolAssets.account(assetId, address);
if (accountInfo.isNone) {
balances.initial.push([assetId, 0]);
} else {
balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
} else {
balances = balance;
for (const assetId of assetIds) {
accountInfo = await api.query.poolAssets.account(assetId, address);
balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
return balances;
case '--local':
if (!balance) {
accountInfo = await api.query.system.account(address);
if (accountInfo === null) {
balances.initial.push(['0', 0]);
} else {
balances.initial.push(['0', Number(accountInfo.data.free)]);
}
} else {
balances = balance;
accountInfo = await api.query.system.account(address);
balances.final.push(['0', Number(accountInfo.data.free)]);
}
return balances;
case '--assets':
if (!balance) {
for (const assetId of assetIds) {
accountInfo = await api.query.assets.account(assetId, address);
if (accountInfo.isNone) {
balances.initial.push([assetId, 0]);
} else {
balances.initial.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
} else {
balances = balance;
for (const assetId of assetIds) {
accountInfo = await api.query.assets.account(assetId, address);
balances.final.push([assetId, accountInfo.unwrap().balance.toBn().toNumber()]);
}
}
return balances;
default:
if (!balance) {
for (const assetId of assetIds) {
accountInfo = await api.query.system.account(address);
if (accountInfo) {
balances.initial.push([assetId, accountInfo.data.free.toBn().toNumber()]);
} else {
balances.initial.push([assetId, 0]);
}
}
} else {
balances = balance;
for (const assetId of assetIds) {
accountInfo = await api.query.system.account(address);
balances.final.push([assetId, accountInfo.data.free.toBn().toNumber()]);
}
}
return balances;
}
};
10 changes: 10 additions & 0 deletions e2e-tests/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright 2023 Parity Technologies (UK) Ltd.

export const KUSAMA_ASSET_HUB_WS_URL = 'ws://127.0.0.1:9911';
export const ROCOCO_ALICE_WS_URL = 'ws://127.0.0.1:9900';
export const TRAPPIST_WS_URL = 'ws://127.0.0.1:9921';

export const BOB_ADDR = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty';
export const FERDE_ADDR = '5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL';

export const EXTRINSIC_VERSION = 4;
166 changes: 166 additions & 0 deletions e2e-tests/executor.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// Copyright 2023 Parity Technologies (UK) Ltd.
import { ApiPromise, WsProvider } from '@polkadot/api';
import { Keyring } from '@polkadot/keyring';
// import { KeyringPair } from '@polkadot/keyring/types';
bee344 marked this conversation as resolved.
Show resolved Hide resolved
import { cryptoWaitReady } from '@polkadot/util-crypto';

import { delay } from '../scripts/util';
import { constructApiPromise } from '../src';
import { balanceTracker, IBalance } from './balance';
import { KUSAMA_ASSET_HUB_WS_URL, ROCOCO_ALICE_WS_URL, TRAPPIST_WS_URL } from './consts';
import { startProgressBar, startTestLogger, terminateProgressBar, testResultLogger, updateProgressBar } from './logger';
import { assetTests, foreignAssetsTests, IndividualTest, liquidPoolsTests, localTests, tests } from './tests';
import { verification } from './verification';

const executor = async (testCase: string) => {
let originWsUrl = '';
let destWsUrl = '';

bee344 marked this conversation as resolved.
Show resolved Hide resolved
let testData: IndividualTest[] = [];

await cryptoWaitReady();

const keyring = new Keyring({ type: 'sr25519' });

switch (testCase) {
bee344 marked this conversation as resolved.
Show resolved Hide resolved
case '--foreign-assets':
testData = tests.foreignAssets;
break;
case '--liquidity-assets':
testData = tests.liquidPools;
break;
case '--local':
testData = tests.local;
break;
case '--assets':
testData = tests.assets;
break;
}

let n: { [K: string]: Function } = {};

switch (testCase) {
case '--foreign-assets':
n = foreignAssetsTests;
break;
case '--liquidity-assets':
n = liquidPoolsTests;
break;
case '--local':
n = localTests;
break;
case '--assets':
n = assetTests;
break;
}

let counter: number = 0;

startTestLogger(testCase);

const progressBar = startProgressBar(testData, testCase);

const results: [string, string, string, boolean][] = [];

for (const t of testData) {
bee344 marked this conversation as resolved.
Show resolved Hide resolved
const originChainId: string = t.args[0];
const destChainId: string = t.args[1];
const originAddr: string = t.args[2];
const destAddr: string = t.args[3];
const assetIds: string[] = t.args[4].slice(1, -1).split(',');
const amounts: string[] = t.args[5].slice(1, -1).split(',');
const opts: object = JSON.parse(t.args[6], (key: string, value: string) => {
return key === 'paysWithFeeOrigin' ? JSON.stringify(value) : value;
}) as object;
let chainName: string = '';

switch (originChainId) {
case '0':
originWsUrl = ROCOCO_ALICE_WS_URL;
chainName = 'Rococo';
break;
case '1000':
originWsUrl = KUSAMA_ASSET_HUB_WS_URL;
chainName = 'Kusama Asset Hub';
break;
case '1836':
originWsUrl = TRAPPIST_WS_URL;
chainName = 'Trappist';
break;
}
if (originChainId == destChainId) {
destWsUrl = originWsUrl;
} else {
switch (destChainId) {
case '0':
destWsUrl = ROCOCO_ALICE_WS_URL;
chainName = 'Rococo';
break;
case '1000':
destWsUrl = KUSAMA_ASSET_HUB_WS_URL;
chainName = 'Kusama Asset Hub';
break;
case '1836':
destWsUrl = TRAPPIST_WS_URL;
chainName = 'Trappist';
break;
}
}

const { api, specName, safeXcmVersion } = await constructApiPromise(originWsUrl);

await api.isReady;

const originApi = api;
const destinationApi =
originChainId == destChainId
? originApi
: await ApiPromise.create({
provider: new WsProvider(destWsUrl),
noInitWarn: true,
});

await destinationApi.isReady;
const destInitialBalance: IBalance = await balanceTracker(destinationApi, testCase, destAddr, assetIds);
const originKeyring = keyring.addFromUri(originAddr);

//eslint-disable-next-line @typescript-eslint/no-unsafe-call
await n[t.test](originKeyring, destChainId, destAddr, assetIds, amounts, opts, api, specName, safeXcmVersion);

await delay(24000);

const destFinalBalance: IBalance = await balanceTracker(
destinationApi,
testCase,
destAddr,
assetIds,
destInitialBalance,
);

const verificationAssetIds: string[] = t.verification[0].slice(1, -1).split(',');
const verificationAmounts: string[] = t.verification[1].slice(1, -1).split(',');

const correctlyReceived = verification(verificationAssetIds, verificationAmounts, destFinalBalance);

await originApi.disconnect();
await destinationApi.disconnect();

counter += 1;

updateProgressBar(counter, progressBar);

for (let i = 0; i < assetIds.length; i++) {
results.push([t.test, assetIds[i], chainName, correctlyReceived[i][1]]);
}
}

for (let i = 0; i < results.length; i++) {
testResultLogger(results[i][0], results[i][1], results[i][2], results[i][3]);
}

terminateProgressBar(progressBar, testCase);
};

executor(process.argv[2])
.catch((err) => console.error(err))
.finally(() => process.exit());
Loading
Loading