From dd771c3512846650c987e1033e305fac8761ca91 Mon Sep 17 00:00:00 2001 From: Nikos Kontakis Date: Fri, 12 May 2023 17:32:01 +0300 Subject: [PATCH] Add ts support in parser (#1031) * Add ts support in parser * Simplify solution * Fix the rust tests * remove unecessery tests * update documentation * Rename enums for JS, TS, SH * Fix the TS convertion * Address some comments * Address some comments * Bump versions of zombienet/dsl-parser-wrapper --- Cargo.lock | 2 +- crates/parser-wrapper/Cargo.toml | 2 +- crates/parser/src/ast.rs | 1 + crates/parser/src/lib.rs | 44 ++++++++++--- crates/parser/src/tests.rs | 65 ++++++++++++++++++- crates/parser/src/zombienet.pest | 2 + docs/src/cli/test-dsl-definition-spec.md | 7 +- javascript/package-lock.json | 8 +-- javascript/packages/cli/package.json | 2 +- .../src/test-runner/assertions.ts | 22 ++++++- javascript/packages/orchestrator/src/types.ts | 1 + tests/0008-custom-scripts.toml | 10 +-- tests/0008-custom-scripts.zndsl | 5 +- tests/0008-custom-ts.ts | 6 ++ tests/0013-db-snapshot.zndsl | 7 +- tests/check-balance-ts.ts | 14 ++++ 16 files changed, 166 insertions(+), 32 deletions(-) create mode 100644 tests/0008-custom-ts.ts create mode 100644 tests/check-balance-ts.ts diff --git a/Cargo.lock b/Cargo.lock index 89cb24031..1a4993698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -285,7 +285,7 @@ dependencies = [ [[package]] name = "dsl-parser-wrapper" -version = "0.1.7" +version = "0.1.8" dependencies = [ "parser", "serde_json", diff --git a/crates/parser-wrapper/Cargo.toml b/crates/parser-wrapper/Cargo.toml index 0e629f45c..0c691d4cc 100644 --- a/crates/parser-wrapper/Cargo.toml +++ b/crates/parser-wrapper/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dsl-parser-wrapper" -version = "0.1.7" +version = "0.1.8" edition = "2021" description = "Zombienet DSL parser: produces a test definition, in json format, that can be used with the ZombieNet's test-runnner." license = "GPL-3.0-or-later" diff --git a/crates/parser/src/ast.rs b/crates/parser/src/ast.rs index d6b31b7cf..47336fb4a 100644 --- a/crates/parser/src/ast.rs +++ b/crates/parser/src/ast.rs @@ -111,6 +111,7 @@ pub enum AssertionKind { cmp: Option, #[serde(with = "optional_timeout")] timeout: Option, + is_ts: bool, }, CustomSh { node_name: NodeName, diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 148c1c40d..ad07be5a5 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -16,6 +16,12 @@ use ast::{Assertion, AssertionKind, Comparison, NodeName, ParaId, TestDefinition #[cfg(test)] mod tests; +enum ScriptType { + Javascript, + Typescript, + Shellscript, +} + // This include forces recompiling this source file if the grammar file changes. // Uncomment it when doing changes to the .pest file const _GRAMMAR: &str = include_str!("zombienet.pest"); @@ -155,7 +161,10 @@ fn parse_lines_count_match_pattern_rule( Ok((name, match_type, pattern, comparison, timeout)) } -fn parse_custom_script_rule(record: Pair, is_js: bool) -> Result { +fn parse_custom_script_rule( + record: Pair, + script_type: ScriptType, +) -> Result { let mut pairs = record.into_inner(); let node_name = parse_name(get_pair(&mut pairs, "name")?)?; let file_path_str = get_pair(&mut pairs, "file_path")?.as_str(); @@ -186,22 +195,30 @@ fn parse_custom_script_rule(record: Pair, is_js: bool) -> Result Ok(AssertionKind::CustomJs { node_name, file_path, custom_args: args, cmp, timeout, - }) - } else { - Ok(AssertionKind::CustomSh { + is_ts: false, + }), + ScriptType::Typescript => Ok(AssertionKind::CustomJs { + node_name, + file_path, + custom_args: args, + cmp, + timeout, + is_ts: true, + }), + ScriptType::Shellscript => Ok(AssertionKind::CustomSh { node_name, file_path, custom_args: args, cmp, timeout, - }) + }), } } @@ -510,7 +527,16 @@ pub fn parse(unparsed_file: &str) -> Result { - let parsed = parse_custom_script_rule(record, true)?; + let parsed = parse_custom_script_rule(record, ScriptType::Javascript)?; + let assertion = Assertion { + parsed, + original_line, + }; + + assertions.push(assertion); + } + Rule::custom_ts => { + let parsed = parse_custom_script_rule(record, ScriptType::Typescript)?; let assertion = Assertion { parsed, original_line, @@ -519,7 +545,7 @@ pub fn parse(unparsed_file: &str) -> Result { - let parsed = parse_custom_script_rule(record, false)?; + let parsed = parse_custom_script_rule(record, ScriptType::Shellscript)?; let assertion = Assertion { parsed, original_line, diff --git a/crates/parser/src/tests.rs b/crates/parser/src/tests.rs index 74657bd3b..fa87288dc 100644 --- a/crates/parser/src/tests.rs +++ b/crates/parser/src/tests.rs @@ -503,7 +503,8 @@ fn custom_js_parse_ok() { "node_name": "alice", "file_path": "./0008-custom.js", "custom_args": null, - "timeout": 200 + "timeout": 200, + "is_ts": false } } } @@ -532,7 +533,67 @@ fn custom_js_with_args_parse_ok() { "node_name": "alice", "file_path": "./0008-custom.js", "custom_args": "dave,2000-1,eve", - "timeout": 200 + "timeout": 200, + "is_ts": false + } + } + } + ] + }"#; + let t: TestDefinition = serde_json::from_str(data).unwrap(); + + let result = parse(&[NETWORK, CREDS, line].join("\n")).unwrap(); + assert_eq!(result, t); +} + +#[test] +fn custom_ts_parse_ok() { + let line: &str = r#"alice: ts-script ./0008-custom-ts.ts within 200 seconds"#; + let data = r#"{ + "description": null, + "network": "./a.toml", + "creds": "config", + "assertions": [ + { + "original_line": "alice: ts-script ./0008-custom-ts.ts within 200 seconds", + "parsed": { + "fn": "CustomJs", + "args": { + "node_name": "alice", + "file_path": "./0008-custom-ts.ts", + "custom_args": null, + "timeout": 200, + "is_ts": true + } + } + } + ] + }"#; + let t: TestDefinition = serde_json::from_str(data).unwrap(); + + let result = parse(&[NETWORK, CREDS, line].join("\n")).unwrap(); + assert_eq!(result, t); +} + +#[test] +fn custom_ts_with_args_parse_ok() { + let line: &str = + r#"alice: ts-script ./0008-custom-ts.ts with "dave,2000-1,eve" within 200 seconds"#; + let data = r#"{ + "description": null, + "network": "./a.toml", + "creds": "config", + "assertions": [ + { + "original_line": "alice: ts-script ./0008-custom-ts.ts with \"dave,2000-1,eve\" within 200 seconds", + "parsed": { + "fn": "CustomJs", + "args": { + "node_name": "alice", + "file_path": "./0008-custom-ts.ts", + "custom_args": "dave,2000-1,eve", + "timeout": 200, + "is_ts": true } } } diff --git a/crates/parser/src/zombienet.pest b/crates/parser/src/zombienet.pest index a907bdaf7..c61194439 100644 --- a/crates/parser/src/zombienet.pest +++ b/crates/parser/src/zombienet.pest @@ -68,6 +68,7 @@ count_log_match = { node_name ~ "count of log lines" ~ ("containing"|"matching") trace = { node_name ~ "trace with traceID" ~ span_id ~ "contains" ~ square_brackets_strings ~ within? } system_event = { node_name ~ "system event" ~ ("contains"|"matches") ~ match_type? ~ double_quoted_string ~ within? } custom_js = { node_name ~ "js-script" ~ file_path ~ ("with" ~ double_quoted_string)? ~ ( "return" ~ comparison )? ~ within? } +custom_ts = { node_name ~ "ts-script" ~ file_path ~ ("with" ~ double_quoted_string)? ~ ( "return" ~ comparison )? ~ within? } custom_sh = { node_name ~ "run" ~ file_path ~ ("with" ~ double_quoted_string)? ~ ( "return" ~ comparison )? ~ within? } /// COMMANDS @@ -98,6 +99,7 @@ file = { SOI ~ ( trace | system_event | custom_js | + custom_ts | custom_sh | sleep | pause | diff --git a/docs/src/cli/test-dsl-definition-spec.md b/docs/src/cli/test-dsl-definition-spec.md index 630334138..a6337bdc5 100644 --- a/docs/src/cli/test-dsl-definition-spec.md +++ b/docs/src/cli/test-dsl-definition-spec.md @@ -70,11 +70,16 @@ The first lines are used to define the **header fields**: - `node-name`: trace with traceID contains ["name", "name2",...] - alice: trace with traceID 94c1501a78a0d83c498cc92deec264d9 contains ["answer-chunk-request", "answer-chunk-request"] -- Custom js scripts: Allow to run a defined script and assert on the completeness or return value. +- Custom js scripts: Allow to run a defined JS script and assert on the completeness or return value. - `node-name`: js-script _script_relative_path_ [ return is *comparator target_value*] [within x seconds] - alice: js-script ./0008-custom.js return is greater than 1 within 200 seconds +- Custom ts scripts: Allow to run a defined TS script and assert on the completeness or return value. + + - `node-name`: ts-script _script_relative_path_ [ return is *comparator target_value*] [within x seconds] + - alice: ts-script ./0008-custom-ts.ts return is greater than 1 within 200 seconds + - Backchannel wait for value and register to use - node-name: wait for `var name` and use as `X` [within 30 seconds] - alice: wait for name and use as X within 30 seconds diff --git a/javascript/package-lock.json b/javascript/package-lock.json index 4b606a418..ba377b29d 100644 --- a/javascript/package-lock.json +++ b/javascript/package-lock.json @@ -1598,9 +1598,9 @@ "link": true }, "node_modules/@zombienet/dsl-parser-wrapper": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/@zombienet/dsl-parser-wrapper/-/dsl-parser-wrapper-0.1.7.tgz", - "integrity": "sha512-pJqo1LZbNqnL6Of1h67eYtTTu0BLcf+U/nGQtj8pYr0V4XVDWdCYvT4C9kXkrh5964nKz3KGB7Eky43NqRJZqg==" + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/@zombienet/dsl-parser-wrapper/-/dsl-parser-wrapper-0.1.8.tgz", + "integrity": "sha512-9xl7iqaSQbA/QzrjmLN9Ioab0MTOJox80qKKO1B5nCqUOzmwKCvVXOv267mXJodfs1vSc0j1BS2rtQ5cuSj0vA==" }, "node_modules/@zombienet/orchestrator": { "resolved": "packages/orchestrator", @@ -6517,7 +6517,7 @@ "version": "1.3.52", "license": "GPL-3.0-or-later", "dependencies": { - "@zombienet/dsl-parser-wrapper": "^0.1.7", + "@zombienet/dsl-parser-wrapper": "^0.1.8", "@zombienet/orchestrator": "^0.0.41", "@zombienet/utils": "^0.0.20", "cli-progress": "^3.12.0", diff --git a/javascript/packages/cli/package.json b/javascript/packages/cli/package.json index c02221cc5..53bc783e1 100644 --- a/javascript/packages/cli/package.json +++ b/javascript/packages/cli/package.json @@ -52,7 +52,7 @@ "url": "https://github.com/paritytech/zombienet/issues" }, "dependencies": { - "@zombienet/dsl-parser-wrapper": "^0.1.7", + "@zombienet/dsl-parser-wrapper": "^0.1.8", "@zombienet/orchestrator": "^0.0.41", "@zombienet/utils": "^0.0.20", "cli-progress": "^3.12.0", diff --git a/javascript/packages/orchestrator/src/test-runner/assertions.ts b/javascript/packages/orchestrator/src/test-runner/assertions.ts index 15449f4d9..37932549b 100644 --- a/javascript/packages/orchestrator/src/test-runner/assertions.ts +++ b/javascript/packages/orchestrator/src/test-runner/assertions.ts @@ -1,9 +1,12 @@ import { ApiPromise, Keyring } from "@polkadot/api"; import { decorators, isValidHttpUrl } from "@zombienet/utils"; import { assert, expect } from "chai"; +import execa from "execa"; +import fs from "fs/promises"; import { JSDOM } from "jsdom"; import { makeRe } from "minimatch"; import path from "path"; +import ts from "typescript"; import { BackchannelMap } from "."; import { chainCustomSectionUpgrade, @@ -189,6 +192,7 @@ const CustomJs = ({ op, target_value, timeout, + is_ts, }: FnArgs) => { timeout = timeout || DEFAULT_INDIVIDUAL_TEST_TIMEOUT; const comparatorFn = comparators[op!]; @@ -232,7 +236,20 @@ const CustomJs = ({ : custom_args.split(",") : []; - const resolvedJsFilePath = path.resolve(configBasePath, file_path!); + let resolvedJsFilePath = path.resolve(configBasePath, file_path!); + + if (is_ts) { + const source = (await fs.readFile(resolvedJsFilePath)).toString(); + const result = ts.transpileModule(source, { + compilerOptions: { module: ts.ModuleKind.CommonJS }, + }); + + resolvedJsFilePath = path.resolve( + configBasePath, + path.parse(file_path!).name + ".js", + ); + await fs.writeFile(resolvedJsFilePath, result.outputText); + } // shim with jsdom const dom = new JSDOM( @@ -278,6 +295,9 @@ const CustomJs = ({ } // remove shim + if (is_ts) { + await execa.command(`rm -rf ${resolvedJsFilePath}`); + } (global as any).window = undefined; (global as any).document = undefined; (global as any).zombie = undefined; diff --git a/javascript/packages/orchestrator/src/types.ts b/javascript/packages/orchestrator/src/types.ts index d126bf23d..317582650 100644 --- a/javascript/packages/orchestrator/src/types.ts +++ b/javascript/packages/orchestrator/src/types.ts @@ -328,6 +328,7 @@ export interface FnArgs { file_or_uri?: string; after?: number; seconds?: number; + is_ts?: boolean; } export interface RegisterParachainOptions { diff --git a/tests/0008-custom-scripts.toml b/tests/0008-custom-scripts.toml index d7806dd24..83f3ca1d4 100644 --- a/tests/0008-custom-scripts.toml +++ b/tests/0008-custom-scripts.toml @@ -10,12 +10,4 @@ chain = "rococo-local" [[relaychain.nodes]] name = "bob" - -[[parachains]] -id = 100 -add_to_genesis = true - - [parachains.collator] - name = "collator01" - image = "{{COL_IMAGE}}" - command = "adder-collator" \ No newline at end of file + \ No newline at end of file diff --git a/tests/0008-custom-scripts.zndsl b/tests/0008-custom-scripts.zndsl index 0dff15aa1..1e4536f97 100644 --- a/tests/0008-custom-scripts.zndsl +++ b/tests/0008-custom-scripts.zndsl @@ -9,4 +9,7 @@ alice: reports node_roles is 4 alice: js-script ./0008-custom.js return is greater than 1 within 200 seconds alice: js-script ./0008-custom.js within 200 seconds alice: js-script ./0008-custom.js with "alice" within 200 seconds -alice: run ./0008-custom.sh within 200 seconds \ No newline at end of file +alice: run ./0008-custom.sh within 200 seconds +alice: ts-script ./0008-custom-ts.ts return is greater than 1 within 200 seconds +alice: ts-script ./0008-custom-ts.ts within 200 seconds +alice: ts-script ./0008-custom-ts.ts with "alice" within 200 seconds \ No newline at end of file diff --git a/tests/0008-custom-ts.ts b/tests/0008-custom-ts.ts new file mode 100644 index 000000000..54e6dbb96 --- /dev/null +++ b/tests/0008-custom-ts.ts @@ -0,0 +1,6 @@ +export const run = async (nodeName: any, networkInfo: any) => { + const { wsUri, userDefinedTypes } = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + const validator = await api.query.session.validators(); + return validator.length; +}; diff --git a/tests/0013-db-snapshot.zndsl b/tests/0013-db-snapshot.zndsl index 57163ec0c..848a065ef 100644 --- a/tests/0013-db-snapshot.zndsl +++ b/tests/0013-db-snapshot.zndsl @@ -2,5 +2,8 @@ Description: Db Snapshot test Network: ./0013-db-snapshot.toml Creds: config -# check balance -alice: js-script ./check-balance.js with "alice" return is 900000999940423862 within 200 seconds \ No newline at end of file +# check balance with js script +alice: js-script ./check-balance.js with "alice" return is 900000999940423862 within 200 seconds + +# check balance with ts script +alice: ts-script ./check-balance-ts.ts with "alice" return is 900000999940423862 within 200 seconds \ No newline at end of file diff --git a/tests/check-balance-ts.ts b/tests/check-balance-ts.ts new file mode 100644 index 000000000..ec3ed73fe --- /dev/null +++ b/tests/check-balance-ts.ts @@ -0,0 +1,14 @@ +// Our address for Alice on the dev chain +const ALICE = '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY'; +const BOB = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'; + +export const run = async (nodeName: any, networkInfo: any, args: any) { + const {wsUri, userDefinedTypes} = networkInfo.nodesByName[nodeName]; + const api = await zombie.connect(wsUri, userDefinedTypes); + + const acc = args[0] === "alice" ? ALICE : BOB; + const balance = await api.query.system.account(acc); + + console.log(`Current balances for ${args[0]} is ${balance.data.free}`); + return balance.data.free; +} \ No newline at end of file