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: integrate runtime-tests as a helper library #549

Merged
merged 33 commits into from
May 18, 2021
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
c19211c
boilerplate for integrating runtime-tests
TarikGul May 11, 2021
1c1cca7
cleanup, pull master
TarikGul May 14, 2021
83cba6f
fix: ignore runtime-tests
TarikGul May 14, 2021
ff4dd7b
fix: add runtime-test scripts
TarikGul May 14, 2021
169120f
fix: add runtime-tests/build
TarikGul May 14, 2021
31761ba
fix: python script to automate the calls
TarikGul May 14, 2021
9ad3233
integrate runtime tests
TarikGul May 14, 2021
11a0c8e
fix: cleanup CLI script
TarikGul May 14, 2021
dbe4d1d
cleanup, and add necessary types
TarikGul May 14, 2021
18d8050
fix: scripts
TarikGul May 14, 2021
6631813
line break
TarikGul May 14, 2021
bd0333e
Update scripts/run_chain_tests.py
TarikGul May 14, 2021
8731c57
remove unused import
TarikGul May 17, 2021
77266a7
add notes and readme
TarikGul May 17, 2021
58ff145
Merge branch 'master' of github.com:paritytech/substrate-api-sidecar …
TarikGul May 17, 2021
6a4b149
remove paranthesis from `yarn build:runtime-tests`
TarikGul May 17, 2021
6f2c253
lint
TarikGul May 17, 2021
19b6f3e
revert
TarikGul May 17, 2021
5c2023f
Revert "lint"
TarikGul May 17, 2021
67896bb
Update scripts/run_chain_tests.py
TarikGul May 17, 2021
a876e73
Update runtime-tests/README.md
TarikGul May 17, 2021
d4fccec
Update runtime-tests/README.md
TarikGul May 17, 2021
73ea3dd
Update runtime-tests/README.md
TarikGul May 17, 2021
ab7833e
Update runtime-tests/README.md
TarikGul May 17, 2021
e098fb2
Update runtime-tests/README.md
TarikGul May 17, 2021
a6ccdad
rewrite the arg parser
TarikGul May 17, 2021
29d7f82
change env key to `__SAS_RUNTIME_TEST_CONFIGURATION`, and URL to HOST
TarikGul May 17, 2021
414b7ae
fix grumble in the readme
TarikGul May 17, 2021
ec9a028
add Parser type and lint
TarikGul May 17, 2021
de4184b
cleanup, add Kusama v9010
TarikGul May 18, 2021
491cee5
add westend v9010
TarikGul May 18, 2021
c9a914b
update docs, add requirements.txt
TarikGul May 18, 2021
7f73b6c
Merge branch 'master' of github.com:paritytech/substrate-api-sidecar …
TarikGul May 18, 2021
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 .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
**/build/*
calc
docs
runtime-tests
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/.vscode
**/node_modules
/build
/runtime-tests/build
/target
yarn-error.log
**/*.rs.bk
Expand All @@ -12,7 +13,6 @@ yarn-error.log
**.log
**/logs
.yarn/*
/docs/.yarn/*
!.yarn/releases
!.yarn/plugins
/docs/.yarn
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ All the commits in this repo follow the [Conventional Commits spec](https://www.
* @polkadot/apps-config [release notes](https://github.com/polkadot-js/apps/releases)
* @polkadot/util-crypto [release notes](https://github.com/polkadot-js/common/releases)

1. After updating the dependencies, the next step is making sure the release will work against all noted runtimes for Polkadot, Kusama, and Westend. This can be handled via the [sidecar-runtime-test](https://github.com/TarikGul/sidecar-runtime-test) helper library. Instructions for how to run it are in the repos README.md. Before moving forward ensure all tests pass, and if it warns of any missing types feel free to make an issue [here](https://github.com/paritytech/substrate-api-sidecar/issues).
1. After updating the dependencies, the next step is making sure the release will work against all noted runtimes for Polkadot, Kusama, and Westend. This can be handled by running `yarn test:init-runtime-tests`. You must have `python3`, and `psutil` installed to run the script. Before moving forward ensure all tests pass, and if it warns of any missing types feel free to make an issue [here](https://github.com/paritytech/substrate-api-sidecar/issues).

1. Update the version in the package.json (this is very important for releasing on NPM).

Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ const base = require('@substrate/dev/config/jest')

module.exports = {
...base,
testPathIgnorePatterns: ['/build/', '/node_modules/', '/docs/'],
testPathIgnorePatterns: ['/build/', '/node_modules/', '/docs/', '/runtime-tests'],
};
6 changes: 5 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@
"start": "yarn run main",
"start:log-rpc": "yarn run build && NODE_ENV=test yarn run main ",
"dev": "tsc-watch --onSuccess \"yarn run main\"",
"test": "substrate-exec-jest --silent"
"test": "substrate-exec-jest --silent",
"lint:runtime-tests": "cd runtime-tests && substrate-dev-run-lint",
"build:runtime-tests": "(cd runtime-tests && substrate-exec-tsc)",
"test:runtime-tests": "yarn build:runtime-tests && node ./runtime-tests/build/index.js --config=./runtime-tests/jest.config.js",
"test:init-runtime-tests": "python3 ./scripts/run_chain_tests.py"
},
"dependencies": {
"@polkadot/api": "beta",
Expand Down
1 change: 1 addition & 0 deletions runtime-tests/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
**/build/*
1 change: 1 addition & 0 deletions runtime-tests/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('@substrate/dev/config/eslint');
30 changes: 30 additions & 0 deletions runtime-tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
## Summary

This is a helper library for Sidecar to run runtime tests against specific chains, at certain blocks.

## Testing

The below instructions are specific to runtime-tests against one chain.
If you are looking to run the runtime-tests against all 3 chains (Polkadot, Kusama, Westend) then run `yarn test:init-runtime-tests` in
the root directory of sidecar.

### Polkadot

Lets first get sidecar ready in a seperate terminal.

```
$ cd substrate-api-sidecar
$ git checkout <your_branch>
$ export SAS_SUBSTRATE_WS_URL=<network archive node>
$ yarn
$ yarn build && yarn start
```

Sidecar should be now connected to the node, and running successfully. If you find a bug file an issue [here](https://github.com/paritytech/substrate-api-sidecar/issues).

Now lets run our runtime tests against polkadot. Go to a separate terminal and run:

`yarn test:runtime-tests --chain polkadot`

Thats it!
All the tests should come back with green checkmarks.
10 changes: 10 additions & 0 deletions runtime-tests/endpoints/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { ChainSpec } from '../types';
import { kusamaEndpoints } from './kusamaEndpoints';
import { polkadotEndpoints } from './polkadotEndpoints';
import { westendEndpoints } from './westendEndpoints';

export const endpoints: Record<ChainSpec, string[][]> = {
kusama: kusamaEndpoints,
polkadot: polkadotEndpoints,
westend: westendEndpoints,
};
20 changes: 20 additions & 0 deletions runtime-tests/endpoints/kusamaEndpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export const kusamaEndpoints = [
['/blocks/2350438', '2350438'], //v1062
['/blocks/2684767', '2684767'], //v2005
['/blocks/2713513', '2713513'], //v2007
['/blocks/2786879', '2786879'], //v2008
['/blocks/2863649', '2863649'], //v2011
['/blocks/3102680', '3102680'], //v2012
['/blocks/3305067', '3305067'], //v2015
['/blocks/3492347', '3492347'], //v2019
['/blocks/3573193', '3573193'], //v2022
['/blocks/4086065', '4086065'], //v2023
['/blocks/4178606', '4178606'], //v2024
['/blocks/4542697', '4542697'], //v2025
['/blocks/5265416', '5265416'], //v2026
['/blocks/6065416', '6065416'], //v2027
['/blocks/6396694', '6396694'], //v2028
['/blocks/6566865', '6566865'], //v2029
['/blocks/6819725', '6819725'], //v2029
['/blocks/7354817', '7354817'], //v2030
];
12 changes: 12 additions & 0 deletions runtime-tests/endpoints/polkadotEndpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export const polkadotEndpoints = [
['/blocks/943438', '943438'], //v17
emostov marked this conversation as resolved.
Show resolved Hide resolved
['/blocks/1603025', '1603025'], //v18
['/blocks/1662525', '1662525'], //v23
['/blocks/1993625', '1993625'], //v24
['/blocks/2392625', '2392625'], //v25
['/blocks/3592619', '3592619'], //v26
['/blocks/3892620', '3892620'], //v27
['/blocks/4092619', '4092619'], //v28
['/blocks/4392619', '4392619'], //v29
['/blocks/4947391', '4947391'], //v30
];
10 changes: 10 additions & 0 deletions runtime-tests/endpoints/westendEndpoints.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
export const westendEndpoints = [
['/blocks/3813629', '3813629'],
['/blocks/3032259', '3032259'],
['/blocks/3891809', '3891809'],
['/blocks/4781573', '4781573'],
['/blocks/5277929', '5277929'],
['/blocks/5480769', '5480769'], //v9000
['/blocks/5493461', '5493461'], //v9000
['/blocks/5495855', '5495855'], //v9000
];
9 changes: 9 additions & 0 deletions runtime-tests/helpers/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Port to Substrate-api-sidecar
*/
export const PORT = 8080;

/**
* Path to Substrate-api-sidecar
*/
export const URL_PATH = '127.0.0.1';
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
15 changes: 15 additions & 0 deletions runtime-tests/helpers/request.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import http from 'http';

export const request = (
path: string,
hostname: string,
port: number
): Promise<string> => {
return new Promise((resolve) => {
http.get({ path, hostname, port }, (response) => {
let data = '';
response.on('data', (_data) => (data += _data));
response.on('end', () => resolve(data));
});
});
};
39 changes: 39 additions & 0 deletions runtime-tests/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const config = {
chain: 'polkadot',
emostov marked this conversation as resolved.
Show resolved Hide resolved
};
const argv = process.argv.slice(0, 2);

/**
* We return empty string's as an indication of a false value. JS recognizes "" as falsey.
* NodeJS.Process.argv.reduce expects the return value of the accumulator to
* be of type string, this is a way of satisfying the compiler, but also
* returning the correct behavior.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I understand what this does. What is the expected input here? It walks all cli arguments, filters out anything that starts with "/", sets the config[cmd] as it goes? How is it used?

Copy link
Member Author

@TarikGul TarikGul May 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea this can be a bit confusing.

  1. Parses out all inputs that start with /. (This is so we dont save things such as the path of the node.js binary into the config)

  2. For everything else If the input doesn't start with --, then we return "" (This sets the accumulator in this case cmd to false). This will resolve to a falsey value which will stop the reducer from saving it into the config.

  3. argv.push(arg) is used to save any local config options in order to pass it into the jest binary such as --config=./runtime-tests/jest.config.js.

  4. If the arg is equal to -- and has the name of chain then it will set it equal to the accumulator or cmd and then the arg that follows will be the argument for the command itself.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I wonder if we're trying to be a bit too clever for our own good here; feels like this should be easier to do? ("no it's just you being thick" is a valid answer here!).

I was actually wondering something more basic: what is this args processing for? Is this right (or close to right)?

"Process the command line arguments used to launch the test session. Merge any config options provided at the command line with the on disk config."

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea so you actually make a great point and I was able to incredibly simplify it. This method is way too clever for what it is suppose to be. I basically just wrote out a custom parser which doesn't need to be done either.

I chose to do it this way because there is no straight forward way to pass custom CLI commands to jest other than setting an entry file, and calling the binary after adding a custom configuration to the disk. But that being said I also could have just used a arg-parser and then directly pass it in same as I did. Which is exactly what I changed it too. I added argparse now and have it just be the same as any other node argument parsing tool. No loops, or complicated logic anymore.

Good catch. Thanks

process.argv.reduce((cmd, arg) => {
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
if (cmd) {
if (cmd.startsWith('/')) return '';
TarikGul marked this conversation as resolved.
Show resolved Hide resolved
config[cmd] = arg;
return '';
}

if (arg.startsWith('--')) {
const sub = arg.substring('--'.length);
// Will only accept the flag if it's within the config
if (Object.keys(config).includes(sub)) {
// Set the sub to the next cmd
return sub;
}
}

argv.push(arg);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm used to seeing reduce() return the accumulator at every iteration (including the last) and assign the return value to a variable, e.g. let sum = [1,2,3].reduce((acc, x) => x*x). Instead here it seems like you're not using the return value of the whole reduce and relying instead on side-effects like this one. Should it be a for-loop then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in this case I am using the reducer a little differently than normal, the accumulator in this case is being used to identify falsey values, vs truthy commands. I stayed away from the for loop because I didnt want to introduce messy iterator manipulation in order to parse the args, and I think the reducer itself allows for more customization for the future in case we wanted to add arguments to the script.

return '';
});

// Store configuration on env
process.env.__CONFIGURATION = JSON.stringify(config);

// Setting real ARGV
process.argv = argv;
emostov marked this conversation as resolved.
Show resolved Hide resolved

// Calling jest runner
require('jest-cli/bin/jest');
6 changes: 6 additions & 0 deletions runtime-tests/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
const base = require('@substrate/dev/config/jest')

module.exports = {
...base,
testPathIgnorePatterns: ['/build/', '/node_modules/', '/src/'],
};
26 changes: 26 additions & 0 deletions runtime-tests/runtimeTests.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { endpoints } from './endpoints';
import { PORT, URL_PATH } from './helpers/consts';
import { request } from './helpers/request';
import { BlockResponse, ChainConfig, ChainSpec } from './types';

const config = JSON.parse(process.env.__CONFIGURATION as string) as ChainConfig;
const chain = config.chain as ChainSpec;

const polkadotEndpoints: string[][] = endpoints[chain];

describe('Runtime Tests for blocks', () => {
jest.setTimeout(15000);
emostov marked this conversation as resolved.
Show resolved Hide resolved

/**
* Test runtimes for `/blocks`
*/
test.each(polkadotEndpoints)(
'Given path %p, it should return block height %p',
async (blockPath, blockHeight) => {
const res = await request(blockPath, URL_PATH, PORT);
const responseJson = JSON.parse(res) as BlockResponse;

expect(responseJson['number']).toBe(blockHeight);
emostov marked this conversation as resolved.
Show resolved Hide resolved
}
);
});
9 changes: 9 additions & 0 deletions runtime-tests/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"extends": "@substrate/dev/config/tsconfig.json",
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"suppressImplicitAnyIndexErrors": true,
"resolveJsonModule": true,
},
}
11 changes: 11 additions & 0 deletions runtime-tests/types/chainSpec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* String literal for specific chains that are being tested for
*/
export type ChainSpec = 'polkadot' | 'kusama' | 'westend';

/**
*
*/
export interface ChainConfig {
chain: string;
}
2 changes: 2 additions & 0 deletions runtime-tests/types/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './chainSpec';
export * from './responses';
16 changes: 16 additions & 0 deletions runtime-tests/types/responses.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Block Responses
*/
export interface BlockResponse {
number: string;
hash: string;
parentHash: string;
stateRoot: string;
extrinsicsRoot: string;
authorId: string | undefined;
logs: Array<object>;
onInitialize: object;
extrinsics: Array<object>;
onFinalize: object;
finalized: boolean | undefined;
}
22 changes: 22 additions & 0 deletions scripts/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div style="text-align:center">
<h2>Sidecar Scripts</h2>
<div>
A set of notes and instructions for scripts used in Substrate-api-sidecar
</div>
</div>
<br></br>


## Script `run_chain_tests.py`

### Summary

This script calls the local runtime-tests helper library in order to test the current branch or development enviornment against
a collection of different blocks, across different runtimes. It does this for Polkadot, Kusama, and Westend.

### Requirements
TarikGul marked this conversation as resolved.
Show resolved Hide resolved

`python3` - required to run the script

`psutil` - package needed to run the script, available with `pip3 install psutil` or `pip install psutil`

62 changes: 62 additions & 0 deletions scripts/run_chain_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
#!/usr/bin/env python3

import os
import psutil
import signal
import subprocess
import time

def run_process(args):
return subprocess.check_output(args, stderr=subprocess.STDOUT,
encoding="utf-8")

def run_chain_test(chain):
run_process(["yarn"])

if chain == "polkadot":
url = "wss://rpc.polkadot.io"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be cool if we had our own nodes - but I supposed you could not check the addresses into source control.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could input the URLs as either CLI args or set env variables for them instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea I agree having our own nodes would be very nice. Would be a nice addition for the tools team in general.

chain = "polkadot"
elif chain == "kusama":
url = "wss://kusama-rpc.polkadot.io"
chain = "kusama"
elif chain == "westend":
url = "wss://westend-rpc.polkadot.io"
chain = "westend"
else:
return -1

os.environ["SAS_SUBSTRATE_WS_URL"] = url
proc = subprocess.Popen(["yarn", "dev"], stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)

print('Sidecar is loading in development mode...')
time.sleep(20)

print('Running `yarn`, and checking/loading cache...')
run_process(["yarn"])

print('Running runtime tests for {}'.format(chain))
res = run_process(["yarn", "test:runtime-tests", "--chain", chain])

print('Killing all processes...')
for child in psutil.Process(proc.pid).children(recursive=True):
child.kill()
proc.kill()

print(res)
return res.find("PASS")


def main():
polka_test = run_chain_test("polkadot")
kusama_test = run_chain_test("kusama")
westend_test = run_chain_test("westend")

if polka_test == 0 and kusama_test == 0 and westend_test == 0:
return 0
else:
return -1


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"compilerOptions": {
"baseUrl": ".",
"outDir": "build",
"rootDirs": ["./src", "runtime-tests"],
"suppressImplicitAnyIndexErrors": true,
"resolveJsonModule": true,
},
Expand Down