From 3afbf5306fdc3bdd9aa8bda2266f3caff4cb3bf9 Mon Sep 17 00:00:00 2001 From: oxade <93547199+oxade@users.noreply.github.com> Date: Mon, 20 Nov 2023 22:49:50 -0800 Subject: [PATCH] [graphql/rpc] testing infra: capture addrs variables (#14957) ## Description Maps resolved named addresses and published packages to graphql variables so they can be used in graphql queries. Two variables are made for each mapping. One for the required `SuiAddress!` type, and another for optional `SuiAddress` type (variable name is suffixed with `_opt`). To declare a variable for use in a query, one must add `--variables {space separated list of var names}` e.g Note: publishing `Test` as done below changes its address from 0x0. This new address is what shows up in graphql variable value. ``` //# init --addresses Test=0x0 A=0x42 --simulator //# publish module Test::M1 {} // Do some stuff //# run-graphql --variables Test A { address(address: $Test) { objectConnection{ edges { node { location digest kind } } } } second: address(address: $A) { objectConnection{ edges { node { location digest kind } } } } } ``` One can also list all the variable mappings with `view-graphql-variables`: ``` Name: A Type: SuiAddress! Value: "0x42" Name: A_opt Type: SuiAddress Value: "0x42" Name: Test Type: SuiAddress! Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" Name: Test_opt Type: SuiAddress Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" ``` ## Test Plan Included --- If your changes are not user-facing and not a breaking change, you can skip the following section. Otherwise, please indicate what changed, and then add to the Release Notes section as highlighted during the release process. ### Type of Change (Check all that apply) - [ ] protocol change - [ ] user-visible impact - [ ] breaking change for a client SDKs - [ ] breaking change for FNs (FN binary must upgrade) - [ ] breaking change for validators or node operators (must upgrade binaries) - [ ] breaking change for on-chain data layout - [ ] necessitate either a data wipe or data migration ### Release notes --- Cargo.lock | 1 + .../tests/call/simple.exp | 98 ++++++++++++++++++- .../tests/call/simple.move | 48 +++++++++ .../src/client/simple_client.rs | 1 + .../sui-transactional-test-runner/Cargo.toml | 1 + .../sui-transactional-test-runner/src/args.rs | 4 + .../src/test_adapter.rs | 71 +++++++++++++- 7 files changed, 220 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2e278df45a2e2..6688e78f5c61a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -12166,6 +12166,7 @@ dependencies = [ "once_cell", "rand 0.8.5", "rocksdb", + "serde_json", "simulacrum", "sui-config", "sui-core", diff --git a/crates/sui-graphql-e2e-tests/tests/call/simple.exp b/crates/sui-graphql-e2e-tests/tests/call/simple.exp index ee4c605251e3c..ba7646f760c5c 100644 --- a/crates/sui-graphql-e2e-tests/tests/call/simple.exp +++ b/crates/sui-graphql-e2e-tests/tests/call/simple.exp @@ -1,4 +1,4 @@ -processed 15 tasks +processed 18 tasks task 1 'publish'. lines 6-28: created: object(1,0) @@ -26,7 +26,7 @@ task 6 'advance-epoch'. lines 39-39: Epoch advanced: 5 task 7 'view-checkpoint'. lines 41-41: -CheckpointSummary { epoch: 5, seq: 10, content_digest: BVsPCtqsoq7tCkDUZeoFQkNsj23Bqjjt8khNPCnAFwRK, +CheckpointSummary { epoch: 5, seq: 10, content_digest: AYWnAH2mnV56mBixSbTBco6dqZRksmeHncHKJEEiAL7w, epoch_rolling_gas_cost_summary: GasCostSummary { computation_cost: 0, storage_cost: 0, storage_rebate: 0, non_refundable_storage_fee: 0 }} task 8 'run-graphql'. lines 43-49: @@ -82,5 +82,97 @@ task 13 'view-checkpoint'. lines 70-70: CheckpointSummary { epoch: 6, seq: 11, content_digest: D3oWLCcqoa1D15gxzvMaDemNNY8YYVspAkYkcmtQKWRt, epoch_rolling_gas_cost_summary: GasCostSummary { computation_cost: 0, storage_cost: 0, storage_rebate: 0, non_refundable_storage_fee: 0 }} -task 14 'advance-epoch'. lines 72-72: +task 14 'advance-epoch'. lines 72-75: Epoch advanced: 6 + +task 15 'run-graphql'. lines 77-90: +Response: { + "data": { + "address": { + "objectConnection": { + "edges": [ + { + "node": { + "location": "0x5dee0c408687d51a12f9edd765733887eb5d784bf38a8511195cf2bc64e3d801", + "digest": "8MCxnZGPBDtTuHSdZJrfEc4nAAmhxAMbXhRdAYYNGi26", + "kind": "OWNED" + } + } + ] + } + } + } +} + +task 16 'run-graphql'. lines 92-116: +Response: { + "data": { + "address": { + "objectConnection": { + "edges": [] + } + }, + "second": { + "objectConnection": { + "edges": [ + { + "node": { + "location": "0x5dee0c408687d51a12f9edd765733887eb5d784bf38a8511195cf2bc64e3d801", + "digest": "8MCxnZGPBDtTuHSdZJrfEc4nAAmhxAMbXhRdAYYNGi26", + "kind": "OWNED" + } + } + ] + } + } + } +} + +task 17 'view-graphql-variables'. lines 119-120: +Name: A +Type: SuiAddress! +Value: "0x42" + +Name: A_opt +Type: SuiAddress +Value: "0x42" + +Name: Test +Type: SuiAddress! +Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" + +Name: Test_opt +Type: SuiAddress +Value: "0xB214F63E7E7CEA32DC57EDBA9C924524F44A7BB50E72D0E102265219D4DE7D0E" + +Name: deepbook +Type: SuiAddress! +Value: "0xDEE9" + +Name: deepbook_opt +Type: SuiAddress +Value: "0xDEE9" + +Name: std +Type: SuiAddress! +Value: "0x1" + +Name: std_opt +Type: SuiAddress +Value: "0x1" + +Name: sui +Type: SuiAddress! +Value: "0x2" + +Name: sui_opt +Type: SuiAddress +Value: "0x2" + +Name: sui_system +Type: SuiAddress! +Value: "0x3" + +Name: sui_system_opt +Type: SuiAddress +Value: "0x3" diff --git a/crates/sui-graphql-e2e-tests/tests/call/simple.move b/crates/sui-graphql-e2e-tests/tests/call/simple.move index edf9497300dda..89c310021599a 100644 --- a/crates/sui-graphql-e2e-tests/tests/call/simple.move +++ b/crates/sui-graphql-e2e-tests/tests/call/simple.move @@ -70,3 +70,51 @@ module Test::M1 { //# view-checkpoint //# advance-epoch + +// Demonstrates using variables +// If the variable ends in _opt, this is the optional variant + +//# run-graphql --variables A +{ + address(address: $A) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } +} + +//# run-graphql --variables Test A +{ + address(address: $Test) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } + second: address(address: $A) { + objectConnection{ + edges { + node { + location + digest + kind + } + } + } + } +} + + +//# view-graphql-variables +// List all the graphql variables diff --git a/crates/sui-graphql-rpc/src/client/simple_client.rs b/crates/sui-graphql-rpc/src/client/simple_client.rs index 20f9d0eb81a96..31f8eb9fa0959 100644 --- a/crates/sui-graphql-rpc/src/client/simple_client.rs +++ b/crates/sui-graphql-rpc/src/client/simple_client.rs @@ -11,6 +11,7 @@ use std::collections::BTreeMap; use super::response::GraphqlResponse; +#[derive(Clone, Debug)] pub struct GraphqlQueryVariable { pub name: String, pub ty: String, diff --git a/crates/sui-transactional-test-runner/Cargo.toml b/crates/sui-transactional-test-runner/Cargo.toml index 29de4fd276745..7cb6d8f513a51 100644 --- a/crates/sui-transactional-test-runner/Cargo.toml +++ b/crates/sui-transactional-test-runner/Cargo.toml @@ -18,6 +18,7 @@ rand.workspace = true tempfile.workspace = true async-trait.workspace = true tokio.workspace = true +serde_json.workspace = true fastcrypto.workspace = true move-binary-format.workspace = true diff --git a/crates/sui-transactional-test-runner/src/args.rs b/crates/sui-transactional-test-runner/src/args.rs index 2f8d7c4c31ca2..2311ea3c96337 100644 --- a/crates/sui-transactional-test-runner/src/args.rs +++ b/crates/sui-transactional-test-runner/src/args.rs @@ -144,6 +144,8 @@ pub struct RunGraphqlCommand { pub show_headers: bool, #[clap(long = "show-service-version")] pub show_service_version: bool, + #[clap(long = "variables", num_args(1..))] + pub variables: Vec, } #[derive(Debug, clap::Parser)] @@ -182,6 +184,8 @@ pub enum SuiSubcommand { ViewCheckpoint, #[clap(name = "run-graphql")] RunGraphql(RunGraphqlCommand), + #[clap(name = "view-graphql-variables")] + ViewGraphqlVariables, } #[derive(Clone, Debug)] diff --git a/crates/sui-transactional-test-runner/src/test_adapter.rs b/crates/sui-transactional-test-runner/src/test_adapter.rs index a7e84d30824b4..2ffc97bc238cd 100644 --- a/crates/sui-transactional-test-runner/src/test_adapter.rs +++ b/crates/sui-transactional-test-runner/src/test_adapter.rs @@ -44,6 +44,7 @@ use std::{ use sui_core::authority::test_authority_builder::TestAuthorityBuilder; use sui_core::authority::AuthorityState; use sui_framework::DEFAULT_FRAMEWORK_PATH; +use sui_graphql_rpc::client::simple_client::GraphqlQueryVariable; use sui_graphql_rpc::config::ConnectionConfig; use sui_graphql_rpc::test_infra::cluster::serve_executor; use sui_graphql_rpc::test_infra::cluster::ExecutorCluster; @@ -133,6 +134,7 @@ pub(crate) struct StagedPackage { pub(crate) digest: Vec, } +#[derive(Debug)] struct TestAccount { address: SuiAddress, key_pair: AccountKeyPair, @@ -474,10 +476,24 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { }}; } match command { + SuiSubcommand::ViewGraphqlVariables => { + let variables = self.graphql_variables(); + let mut res = vec![]; + for (name, value) in variables { + res.push(format!( + "Name: {}\nType: {}\nValue: {}", + name, + value.ty, + serde_json::to_string_pretty(&value.value).unwrap() + )); + } + Ok(Some(res.join("\n\n"))) + } SuiSubcommand::RunGraphql(RunGraphqlCommand { show_usage, show_headers, show_service_version, + variables, }) => { let file = data.ok_or_else(|| anyhow::anyhow!("Missing GraphQL query"))?; let contents = std::fs::read_to_string(file.path())?; @@ -487,9 +503,15 @@ impl<'a> MoveTestAdapter<'a> for SuiTestAdapter<'a> { .wait_for_checkpoint_catchup(highest_checkpoint, Duration::from_secs(10)) .await; + let used_variables = self.resolve_graphql_variables(&variables)?; let resp = cluster .graphql_client - .execute_to_graphql(contents.trim().to_owned(), show_usage, vec![], vec![]) + .execute_to_graphql( + contents.trim().to_owned(), + show_usage, + used_variables, + vec![], + ) .await?; let mut output = vec![]; @@ -933,6 +955,53 @@ impl<'a> SuiTestAdapter<'a> { self.executor } + fn graphql_variables(&self) -> BTreeMap { + let mut variables = BTreeMap::new(); + for (name, addr) in &self.compiled_state.named_address_mapping { + let addr = addr.to_string(); + + // Required variant + variables.insert( + name.to_owned(), + GraphqlQueryVariable { + name: name.to_string(), + value: serde_json::json!(addr), + ty: "SuiAddress!".to_string(), + }, + ); + // Optional variant + let name = name.to_string() + "_opt"; + variables.insert( + name.clone(), + GraphqlQueryVariable { + name: name.to_string(), + value: serde_json::json!(addr), + ty: "SuiAddress".to_string(), + }, + ); + } + variables + } + fn resolve_graphql_variables( + &self, + declared: &[String], + ) -> anyhow::Result> { + let variables = self.graphql_variables(); + let mut res = vec![]; + for decl in declared { + if let Some(var) = variables.get(decl) { + res.push(var.clone()); + } else { + return Err(anyhow!( + "Unknown variable: {}\nAllowed variable mappings are are {:#?}", + decl, + variables + )); + } + } + Ok(res) + } + async fn upgrade_package( &mut self, before_upgrade: NumericalAddress,