Skip to content

Commit abc540d

Browse files
Ad96elntn-x2weichweich
authored
chore: integration tests chopsticks (#614)
## fixes [KILTprotocol/ticket#3098](KILTprotocol/ticket#3098) and fixes KILTprotocol/ticket#3239 and fixes KILTprotocol/ticket#3239. This PR introduces e2e tests based on chopsticks. Currently, only the `limitedReserAssetsTransfer` between KILT and HydraDx are tested. Due to the XCM configuration change, the XCM pallet is benchmarked. Otherwise, the extrinsic would exceed the block limits. --------- Co-authored-by: Antonio Antonino <antonio@kilt.io> Co-authored-by: Albrecht <albrecht@kilt.io>
1 parent e49a7c5 commit abc540d

24 files changed

+6213
-125
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,5 @@
2020
**/node_modules
2121

2222
runtimes/spiritnet/src/xcm_tests/e2e/out
23+
24+
*.db.sqlite*

.gitlab-ci.yml

+16
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ test-features:
3030
script:
3131
- cargo test --all --all-features --all-targets --locked
3232

33+
integration-tests:
34+
timeout: 30 minutes
35+
image: paritytech/ci-unified:bullseye-1.70.0
36+
stage: test
37+
variables:
38+
CI: "true"
39+
script:
40+
- cd ./integration-tests/chopsticks
41+
- curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
42+
- export NVM_DIR="$HOME/.nvm" && . "$NVM_DIR/nvm.sh" --no-use
43+
- eval "[ -f .nvmrc ] && nvm install" && nvm use
44+
- cargo build -p spiritnet-runtime
45+
- yarn --immutable
46+
- yarn lint
47+
- yarn test:CI
48+
3349
# TODO: The try-runtime-cli executable could be built as part of the Docker image directly, saving some time.
3450
test-try-runtime:
3551
parallel:
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
HYDRADX_WS=
2+
HYDRADX_PORT=
3+
POLKADOT_WS=
4+
POLKADOT_PORT=
5+
SPIRITNET_WS=
6+
SPIRITNET_PORT=
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"env": {
3+
"node": true,
4+
"es2021": true
5+
},
6+
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "prettier"],
7+
"parser": "@typescript-eslint/parser",
8+
"parserOptions": {
9+
"ecmaVersion": "latest",
10+
"sourceType": "module",
11+
"project": "./tsconfig.json"
12+
},
13+
"plugins": ["@typescript-eslint", "prettier"],
14+
"rules": {
15+
"quotes": ["warn", "single"]
16+
}
17+
}

integration-tests/chopsticks/.nvmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
v20.11.0
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "singleQuote": true, "trailingComma": "es5", "semi": false }
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"name": "@kiltprotocol/e2e-tests",
3+
"version": "0.0.1",
4+
"description": "chopsticks integration tests",
5+
"private": "true",
6+
"type": "module",
7+
"repository": "git@github.com:KILTprotocol/kilt-node.git",
8+
"author": "[\"KILT <info@kilt.io>\"]",
9+
"license": "MIT",
10+
"devDependencies": {
11+
"@acala-network/chopsticks": "0.10.0",
12+
"@acala-network/chopsticks-testing": "0.10.1",
13+
"@polkadot/api": "^10.11.2",
14+
"@types/node": "^20.11.30",
15+
"@typescript-eslint/eslint-plugin": "^7.7.0",
16+
"@typescript-eslint/parser": "^7.7.0",
17+
"eslint": "^8.0.1",
18+
"eslint-config-airbnb": "^19.0.4",
19+
"eslint-config-prettier": "^9.1.0",
20+
"eslint-config-standard-with-typescript": "^43.0.1",
21+
"eslint-plugin-import": "^2.25.2",
22+
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
23+
"eslint-plugin-prettier": "^5.1.3",
24+
"eslint-plugin-promise": "^6.0.0",
25+
"prettier": "^3.2.5",
26+
"ts-node": "^10.9.2",
27+
"tsx": "^4.7.1",
28+
"typescript": "*",
29+
"vitest": "^1.4.0",
30+
"eslint-plugin-jsx-a11y": "^6.8.0"
31+
},
32+
"scripts": {
33+
"lint": "eslint src && prettier --check src",
34+
"lint:fix": "eslint --fix src && prettier --write src",
35+
"clean": "rm -rf ./db && cargo build -p spiritnet-runtime",
36+
"test": "LOG_LEVEL=error vitest",
37+
"test:CI": "vitest --bail 0 --no-file-parallelism"
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { setupContext, SetupOption } from '@acala-network/chopsticks-testing'
2+
import type { Config } from './types.js'
3+
import * as SpiritnetConfig from './spiritnet.js'
4+
import { initialBalanceHDX, initialBalanceKILT, toNumber } from '../utils.js'
5+
6+
/// Options used to create the HydraDx context
7+
export const options: SetupOption = {
8+
endpoint: process.env.HYDRADX_WS || ['wss://hydradx-rpc.dwellir.com', 'wss://rpc.hydradx.cloud'],
9+
db: './db/hydradx.db.sqlite',
10+
port: toNumber(process.env.HYDRADX_PORT) || 9001,
11+
}
12+
13+
export const kiltTokenId = 60
14+
15+
/// Sets the [TechnicalCommittee] and [Council] governance to the given accounts
16+
export function setGovernance(addr: string[]) {
17+
return {
18+
TechnicalCommittee: { Members: addr },
19+
Council: { Members: addr },
20+
}
21+
}
22+
23+
/// Assigns the native tokens to an accounts
24+
export function assignNativeTokensToAccounts(addr: string[], balance: bigint = initialBalanceHDX) {
25+
return {
26+
System: {
27+
Account: addr.map((address) => [[address], { providers: 1, data: { free: balance } }]),
28+
},
29+
}
30+
}
31+
32+
/// Assigns KILT tokens to an accounts
33+
export function assignKiltTokensToAccounts(addr: string[], balance: bigint = initialBalanceKILT) {
34+
return {
35+
Tokens: {
36+
Accounts: addr.map((address) => [[address, kiltTokenId], { free: balance }]),
37+
},
38+
}
39+
}
40+
41+
/// Register KILT into HydraDX and allow KILT as payment
42+
export function registerKilt() {
43+
return {
44+
assetRegistry: {
45+
assetLocations: [[[kiltTokenId], { parents: 1, interior: { X1: { Parachain: SpiritnetConfig.paraId } } }]],
46+
assetIds: [[['KILT'], kiltTokenId]],
47+
locationAssets: [[[{ parents: 1, interior: { X1: { Parachain: SpiritnetConfig.paraId } } }], kiltTokenId]],
48+
assets: [
49+
[
50+
[kiltTokenId],
51+
{
52+
name: 'KILT',
53+
assetType: 'Token',
54+
existentialDeposit: 500,
55+
symbol: 'KILT',
56+
decimals: 18,
57+
xcmRateLimit: null,
58+
isSufficient: true,
59+
},
60+
],
61+
],
62+
},
63+
multiTransactionPayment: {
64+
acceptedCurrencies: [[[kiltTokenId], 100_000]],
65+
},
66+
}
67+
}
68+
69+
/// HydraDX ParaId
70+
export const paraId = 2034
71+
72+
/// OmniPool account
73+
export const omnipoolAccount = '7L53bUTBbfuj14UpdCNPwmgzzHSsrsTWBHX5pys32mVWM3C1'
74+
75+
export async function getContext(): Promise<Config> {
76+
return setupContext(options)
77+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { setupContext, SetupOption } from '@acala-network/chopsticks-testing'
2+
import type { Config } from './types.js'
3+
import { initialBalanceDOT, toNumber } from '../utils.js'
4+
5+
/// Options used to create the HydraDx context
6+
export const options: SetupOption = {
7+
endpoint: process.env.POLKADOT_WS || [
8+
'wss://rpc.polkadot.io',
9+
'wss://polkadot-rpc.dwellir.com',
10+
'wss://rpc.ibp.network/polkadot',
11+
],
12+
db: './db/polkadot.db.sqlite',
13+
port: toNumber(process.env.POLKADOT_PORT) || 9000,
14+
}
15+
16+
/// Assigns the native tokens to an accounts
17+
export function assignNativeTokensToAccounts(addr: string[], balance: bigint = initialBalanceDOT) {
18+
return {
19+
System: {
20+
Account: addr.map((address) => [[address], { providers: 1, data: { free: balance } }]),
21+
},
22+
}
23+
}
24+
25+
export function removeDisputesAndMessageQueues() {
26+
return {
27+
ParasDisputes: {
28+
// those can makes block building super slow
29+
$removePrefix: ['disputes'],
30+
},
31+
Dmp: {
32+
// clear existing dmp to avoid impact test result
33+
$removePrefix: ['downwardMessageQueues'],
34+
},
35+
}
36+
}
37+
38+
export async function getContext(): Promise<Config> {
39+
return setupContext(options)
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { setupContext, SetupOption } from '@acala-network/chopsticks-testing'
2+
import type { Config } from './types.js'
3+
import { initialBalanceKILT, toNumber } from '../utils.js'
4+
5+
/// Options used to create the Spiritnet context
6+
const options: SetupOption = {
7+
endpoint: process.env.SPIRITNET_WS || 'wss://kilt-rpc.dwellir.com',
8+
db: './db/spiritnet.db.sqlite',
9+
port: toNumber(process.env.SPIRITNET_PORT) || 9002,
10+
wasmOverride: '../../target/debug/wbuild/spiritnet-runtime/spiritnet_runtime.wasm',
11+
// Whether to allow WASM unresolved imports when using a WASM to build the parachain. This Flag is needed otherwise, the runtime can not be built from the WASM. Chopsticks throws an error when it encounters an unresolved import.
12+
allowUnresolvedImports: true,
13+
}
14+
15+
/// Assigns the native tokens to an accounts
16+
export function assignNativeTokensToAccounts(addr: string[], balance: bigint = initialBalanceKILT) {
17+
return {
18+
System: {
19+
Account: addr.map((address) => [[address], { providers: 1, data: { free: balance } }]),
20+
},
21+
}
22+
}
23+
24+
/// Sets the [technicalCommittee] and [council] governance to the given accounts
25+
export function setGovernance(addr: string[]) {
26+
return {
27+
technicalCommittee: { Members: addr },
28+
council: { Members: addr },
29+
}
30+
}
31+
32+
/// Sets the [safeXcmVersion] to the given version
33+
export function setSafeXcmVersion(version: number) {
34+
return {
35+
polkadotXcm: {
36+
safeXcmVersion: version,
37+
},
38+
}
39+
}
40+
41+
/// Spiritnet ParaId
42+
export const paraId = 2086
43+
44+
/// The sovereign account of HydraDx in Spiritnet
45+
export const hydraDxSovereignAccount = '4qXPdpioJ6D8cgdeYXaukV2Y2oAQUHaX1VnGhdbSRqJn2CBt'
46+
47+
/// Returns the Spiritnet context for the given options
48+
export async function getContext(): Promise<Config> {
49+
return setupContext(options)
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import type { setupContext } from '@acala-network/chopsticks-testing'
2+
3+
export type Config = Awaited<ReturnType<typeof setupContext>>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
export function getSiblingLocation(paraId: number) {
2+
return {
3+
parents: 1,
4+
interior: {
5+
X1: { Parachain: paraId },
6+
},
7+
}
8+
}
9+
10+
export function getParentLocation() {
11+
return {
12+
parents: 1,
13+
interior: 'Here',
14+
}
15+
}
16+
17+
export function getAccountLocationV2(addr: string) {
18+
return {
19+
V2: {
20+
parents: 0,
21+
interior: {
22+
X1: {
23+
AccountId32: {
24+
network: 'Any',
25+
id: addr,
26+
},
27+
},
28+
},
29+
},
30+
}
31+
}
32+
33+
export function getAccountLocationV3(addr: string) {
34+
return {
35+
V3: {
36+
parents: 0,
37+
interior: {
38+
X1: {
39+
AccountId32: {
40+
id: addr,
41+
},
42+
},
43+
},
44+
},
45+
}
46+
}
47+
48+
export function getNativeAssetIdLocation(amount: bigint) {
49+
return {
50+
id: { Concrete: { parents: 0, interior: 'Here' } },
51+
fun: { Fungible: amount },
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { beforeAll, afterAll } from 'vitest'
2+
import { connectParachains, connectVertical } from '@acala-network/chopsticks'
3+
import { setTimeout } from 'timers/promises'
4+
5+
import * as SpiritnetConfig from '../network/spiritnet.js'
6+
import * as PolkadotConfig from '../network/polkadot.js'
7+
import * as HydraDxConfig from '../network/hydraDx.js'
8+
import type { Config } from '../network/types.js'
9+
import { setStorage } from './utils.js'
10+
11+
export let spiritnetContext: Config
12+
export let hydradxContext: Config
13+
export let polkadotContext: Config
14+
15+
beforeAll(async () => {
16+
spiritnetContext = await SpiritnetConfig.getContext()
17+
hydradxContext = await HydraDxConfig.getContext()
18+
polkadotContext = await PolkadotConfig.getContext()
19+
20+
// Setup network
21+
await connectVertical(polkadotContext.chain, spiritnetContext.chain)
22+
await connectVertical(polkadotContext.chain, hydradxContext.chain)
23+
await connectParachains([spiritnetContext.chain, hydradxContext.chain])
24+
25+
const newBlockConfig = { count: 2 }
26+
// fixes api runtime disconnect warning
27+
await setTimeout(50)
28+
// Perform runtime upgrade and establish xcm connections.
29+
await Promise.all([
30+
polkadotContext.dev.newBlock(newBlockConfig),
31+
spiritnetContext.dev.newBlock(newBlockConfig),
32+
hydradxContext.dev.newBlock(newBlockConfig),
33+
])
34+
35+
console.info('Runtime Upgrade completed')
36+
37+
// set SafeXcmVersion to 3
38+
await setStorage(spiritnetContext, SpiritnetConfig.setSafeXcmVersion(3))
39+
40+
// register Kilt in HydraDX
41+
await setStorage(hydradxContext, HydraDxConfig.registerKilt())
42+
}, 300_000)
43+
44+
afterAll(async () => {
45+
// fixes api runtime disconnect warning
46+
await setTimeout(50)
47+
await Promise.all([spiritnetContext.teardown(), hydradxContext.teardown(), polkadotContext.teardown()])
48+
})
49+
50+
export async function getFreeBalanceSpiritnet(account: string): Promise<bigint> {
51+
const accountInfo = await spiritnetContext.api.query.system.account(account)
52+
return accountInfo.data.free.toBigInt()
53+
}
54+
55+
export async function getFreeBalanceHydraDxKilt(account: string): Promise<bigint> {
56+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
57+
const accountInfo: any = await hydradxContext.api.query.tokens.accounts(account, HydraDxConfig.kiltTokenId)
58+
return accountInfo.free.toBigInt()
59+
}

0 commit comments

Comments
 (0)