From b31131d21dd2db35721c3f916583225437014954 Mon Sep 17 00:00:00 2001 From: Alexandru Vasile <60601340+lexnv@users.noreply.github.com> Date: Mon, 8 Apr 2024 11:34:20 +0300 Subject: [PATCH] testing: Prepare light client testing with substrate binary and add subxt-test macro (#1507) * testing: Add long running light client flag and cfg aliases Signed-off-by: Alexandru Vasile * testing: Expose clients depending on feature flags Signed-off-by: Alexandru Vasile * subxt: Use unstable backend for light client Signed-off-by: Alexandru Vasile * testing: Disable flaky lightclient tests Signed-off-by: Alexandru Vasile * ci: Add long runnnig step Signed-off-by: Alexandru Vasile * Revert "subxt: Use unstable backend for light client" This reverts commit ea6f3cc58b43cbd568c981aa131e0e119684a7df. * ci: Long running tests for 60 mins Signed-off-by: Alexandru Vasile * ci: Use 16 cores for light-client testing Signed-off-by: Alexandru Vasile * ci: Isolate light-client testing to save CI minutes Signed-off-by: Alexandru Vasile * testing: Retry on Tx::Dropped for lightclinet only Signed-off-by: Alexandru Vasile * testing: Wait for more blocks for the lightclient init Signed-off-by: Alexandru Vasile * subxt: Use unstable backend for light client Signed-off-by: Alexandru Vasile * testing: Disable legacy RPC tests Signed-off-by: Alexandru Vasile * testing: Disable sudo and contracts tests Signed-off-by: Alexandru Vasile * testing: Retry constructing lightclient on read-proof errors Signed-off-by: Alexandru Vasile * testing: Disable tx dynamic test Signed-off-by: Alexandru Vasile * proc-macro: Timeout for tests Signed-off-by: Alexandru Vasile * testing: Add timeout 800 seconds Signed-off-by: Alexandru Vasile * proc-macro/tests: Adjust subxt-test proc-macro Signed-off-by: Alexandru Vasile * proc-macro: Rename crate to subxt-test-proc-macro Signed-off-by: Alexandru Vasile * Use default subxt-proc-macro timeout Signed-off-by: Alexandru Vasile * light-client: Remove println Signed-off-by: Alexandru Vasile * subxt: Remove tokio as dependency, use it only for testing Signed-off-by: Alexandru Vasile * testing: Chagne default timeout to 6 seconds Signed-off-by: Alexandru Vasile * proc-macro: Add env timeout variable Signed-off-by: Alexandru Vasile * ci: Add subxt env var for controling test timeouts Signed-off-by: Alexandru Vasile * tests/tx-retries: Retry on `Non node available` error Signed-off-by: Alexandru Vasile * testing: Use unstable backend for testing lightclient Signed-off-by: Alexandru Vasile * testing: Remove old lightclient object Signed-off-by: Alexandru Vasile * testing: Adjust for the new interface Signed-off-by: Alexandru Vasile * backend/rpc: Allow older version of the initialized event Signed-off-by: Alexandru Vasile * rpc/tests: Check initialized decodes correctly Signed-off-by: Alexandru Vasile * ci: Reset workflow Signed-off-by: Alexandru Vasile * Apply cargo fmt Signed-off-by: Alexandru Vasile * Remove unused dep Signed-off-by: Alexandru Vasile * Remove gitmerge old file Signed-off-by: Alexandru Vasile * Remove unused dep Signed-off-by: Alexandru Vasile * rename proc-macro to subxt-test-macro Signed-off-by: Alexandru Vasile * tests: Remove txretries for lightclient Signed-off-by: Alexandru Vasile * tests: Wait for 5 blocks for the lightclient full testing suite Signed-off-by: Alexandru Vasile * tests: Group imports Signed-off-by: Alexandru Vasile * macro: Rename const value Signed-off-by: Alexandru Vasile --------- Signed-off-by: Alexandru Vasile --- Cargo.lock | 16 +++ Cargo.toml | 1 + subxt/src/backend/unstable/rpc_methods.rs | 55 ++++++++- testing/integration-tests/Cargo.toml | 7 ++ testing/integration-tests/build.rs | 9 ++ .../src/full_client/blocks/mod.rs | 38 +++++-- .../src/full_client/client/legacy_rpcs.rs | 30 ++--- .../src/full_client/client/mod.rs | 42 ++++--- .../src/full_client/client/unstable_rpcs.rs | 27 +++-- .../src/full_client/frame/balances.rs | 82 ++++++++++---- .../src/full_client/frame/contracts.rs | 50 +++++--- .../src/full_client/frame/mod.rs | 7 +- .../src/full_client/frame/staking.rs | 107 ++++++++++++------ .../src/full_client/frame/sudo.rs | 22 ++-- .../src/full_client/frame/system.rs | 14 ++- .../src/full_client/frame/timestamp.rs | 4 +- .../src/full_client/metadata/validation.rs | 10 +- .../src/full_client/runtime_api/mod.rs | 15 ++- .../src/full_client/storage/mod.rs | 23 ++-- testing/integration-tests/src/lib.rs | 5 +- .../integration-tests/src/utils/context.rs | 5 + testing/integration-tests/src/utils/mod.rs | 13 ++- .../integration-tests/src/utils/node_proc.rs | 21 ++-- .../src/utils/wait_for_blocks.rs | 17 ++- .../subxt-test-macro/Cargo.toml | 20 ++++ .../subxt-test-macro/src/lib.rs | 95 ++++++++++++++++ 26 files changed, 554 insertions(+), 181 deletions(-) create mode 100644 testing/integration-tests/build.rs create mode 100644 testing/integration-tests/subxt-test-macro/Cargo.toml create mode 100644 testing/integration-tests/subxt-test-macro/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 11bdfe7449..a43fdcdc00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -758,6 +758,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cfg_aliases" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e53693616d3075149f4ead59bdeecd204ac6b8192d8969757601b74bddf00f" + [[package]] name = "chacha20" version = "0.9.1" @@ -2270,6 +2276,7 @@ name = "integration-tests" version = "0.35.0" dependencies = [ "assert_matches", + "cfg_aliases", "frame-metadata 16.0.0", "futures", "hex", @@ -2283,6 +2290,7 @@ dependencies = [ "subxt-codegen", "subxt-metadata", "subxt-signer", + "subxt-test-macro", "syn 2.0.53", "test-runtime", "tokio", @@ -4818,6 +4826,14 @@ dependencies = [ "zeroize", ] +[[package]] +name = "subxt-test-macro" +version = "0.35.0" +dependencies = [ + "quote", + "syn 2.0.53", +] + [[package]] name = "syn" version = "1.0.109" diff --git a/Cargo.toml b/Cargo.toml index 2f50bc5ceb..549c713a02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "testing/substrate-runner", "testing/test-runtime", "testing/integration-tests", + "testing/integration-tests/subxt-test-macro", "testing/ui-tests", "testing/generate-custom-metadata", "macro", diff --git a/subxt/src/backend/unstable/rpc_methods.rs b/subxt/src/backend/unstable/rpc_methods.rs index 9c1307a326..e4d4b63bab 100644 --- a/subxt/src/backend/unstable/rpc_methods.rs +++ b/subxt/src/backend/unstable/rpc_methods.rs @@ -11,7 +11,7 @@ use crate::config::BlockHash; use crate::{Config, Error}; use derive_where::derive_where; use futures::{Stream, StreamExt}; -use serde::{Deserialize, Serialize}; +use serde::{Deserialize, Deserializer, Serialize}; use std::collections::{HashMap, VecDeque}; use std::task::Poll; @@ -377,8 +377,7 @@ pub enum FollowEvent { /// /// This is the first event generated by the `follow` subscription /// and is submitted only once. -#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] -#[serde(rename_all = "camelCase")] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Initialized { /// The hashes of the last finalized blocks. pub finalized_block_hashes: Vec, @@ -391,6 +390,30 @@ pub struct Initialized { pub finalized_block_runtime: Option, } +impl<'de, Hash: Deserialize<'de>> Deserialize<'de> for Initialized { + fn deserialize>(deserializer: D) -> Result { + // Custom struct that can deserialize both `finalizedBlockHash` and `finalizedBlockHashes`. + #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] + #[serde(rename_all = "camelCase")] + struct InitializedIR { + finalized_block_hashes: Option>, + finalized_block_hash: Option, + finalized_block_runtime: Option, + } + + let ir = InitializedIR::deserialize(deserializer)?; + let finalized_block_hashes = ir + .finalized_block_hashes + .or_else(|| ir.finalized_block_hash.map(|hash| vec![hash])) + .ok_or_else(|| serde::de::Error::custom("Missing finalized block hashes"))?; + + Ok(Initialized { + finalized_block_hashes, + finalized_block_runtime: ir.finalized_block_runtime, + }) + } +} + /// The runtime event generated if the `follow` subscription /// has set the `with_runtime` flag. #[derive(Debug, Clone, PartialEq, Eq, Deserialize)] @@ -973,4 +996,30 @@ mod test { let _ = serde_json::from_value::(from_err) .expect_err("can't deser invalid num into u32"); } + + #[test] + fn chain_head_initialized() { + // Latest format version. + let event = serde_json::json!({ + "finalizedBlockHashes": ["0x1", "0x2"], + }); + let decoded: Initialized = serde_json::from_value(event).unwrap(); + assert_eq!( + decoded.finalized_block_hashes, + vec!["0x1".to_string(), "0x2".to_string()] + ); + + // Old format. + let event = serde_json::json!({ + "finalizedBlockHash": "0x1", + }); + let decoded: Initialized = serde_json::from_value(event).unwrap(); + assert_eq!(decoded.finalized_block_hashes, vec!["0x1".to_string()]); + + // Wrong format. + let event = serde_json::json!({ + "finalizedBlockHash": ["0x1"], + }); + let _ = serde_json::from_value::>(event).unwrap_err(); + } } diff --git a/testing/integration-tests/Cargo.toml b/testing/integration-tests/Cargo.toml index 122af6a2d3..47293cd1ea 100644 --- a/testing/integration-tests/Cargo.toml +++ b/testing/integration-tests/Cargo.toml @@ -18,6 +18,9 @@ default = [] # Enable to run the tests with Light Client support. unstable-light-client = ["subxt/unstable-light-client"] +# Enable to run the full-client tests with Light Client support. +unstable-light-client-long-running = ["subxt/unstable-light-client"] + # Enable this to use the unstable backend in tests _instead of_ # the default one which relies on the "old" RPC methods. unstable-backend-client = [] @@ -43,3 +46,7 @@ tracing = { workspace = true } tracing-subscriber = { workspace = true } wabt = { workspace = true } substrate-runner = { workspace = true } +subxt-test-macro = { path = "subxt-test-macro" } + +[build-dependencies] +cfg_aliases = "0.2.0" diff --git a/testing/integration-tests/build.rs b/testing/integration-tests/build.rs new file mode 100644 index 0000000000..992c3b3e81 --- /dev/null +++ b/testing/integration-tests/build.rs @@ -0,0 +1,9 @@ +use cfg_aliases::cfg_aliases; + +fn main() { + // Setup cfg aliases + cfg_aliases! { + lightclient: { any(feature = "unstable-light-client", feature = "unstable-light-client-long-running") }, + fullclient: { all(not(feature = "unstable-light-client"), not(feature = "unstable-light-client-long-running")) }, + } +} diff --git a/testing/integration-tests/src/full_client/blocks/mod.rs b/testing/integration-tests/src/full_client/blocks/mod.rs index 0cb40404b7..2686b954ae 100644 --- a/testing/integration-tests/src/full_client/blocks/mod.rs +++ b/testing/integration-tests/src/full_client/blocks/mod.rs @@ -2,17 +2,29 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{test_context, utils::node_runtime}; +use crate::{subxt_test, test_context}; use codec::{Compact, Encode}; use futures::StreamExt; -use subxt::config::signed_extensions::{ChargeAssetTxPayment, CheckMortality, CheckNonce}; -use subxt::config::DefaultExtrinsicParamsBuilder; -use subxt::config::SubstrateConfig; -use subxt::utils::Era; -use subxt_metadata::Metadata; + +#[cfg(fullclient)] +use crate::utils::node_runtime; + +#[cfg(fullclient)] +use subxt::{ + config::{ + signed_extensions::{ChargeAssetTxPayment, CheckMortality, CheckNonce}, + DefaultExtrinsicParamsBuilder, SubstrateConfig, + }, + utils::Era, +}; + +#[cfg(fullclient)] use subxt_signer::sr25519::dev; -#[tokio::test] +use subxt_metadata::Metadata; + +#[cfg(fullclient)] +#[subxt_test] async fn block_subscriptions_are_consistent_with_eachother() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -76,7 +88,7 @@ async fn block_subscriptions_are_consistent_with_eachother() -> Result<(), subxt Ok(()) } -#[tokio::test] +#[subxt_test] async fn finalized_headers_subscription() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -93,7 +105,7 @@ async fn finalized_headers_subscription() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> { use subxt::backend::legacy; @@ -138,7 +150,7 @@ async fn missing_block_headers_will_be_filled_in() -> Result<(), subxt::Error> { } // Check that we can subscribe to non-finalized blocks. -#[tokio::test] +#[subxt_test] async fn runtime_api_call() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -163,7 +175,8 @@ async fn runtime_api_call() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn fetch_block_and_decode_extrinsic_details() { let ctx = test_context().await; let api = ctx.client(); @@ -232,7 +245,8 @@ async fn fetch_block_and_decode_extrinsic_details() { assert!(tx.is_signed()); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn decode_signed_extensions_from_blocks() { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/client/legacy_rpcs.rs b/testing/integration-tests/src/full_client/client/legacy_rpcs.rs index 343079d14b..cd932146fc 100644 --- a/testing/integration-tests/src/full_client/client/legacy_rpcs.rs +++ b/testing/integration-tests/src/full_client/client/legacy_rpcs.rs @@ -5,9 +5,9 @@ //! Just sanity checking some of the legacy RPC methods to make //! sure they don't error out and can decode their results OK. -use crate::test_context; +use crate::{subxt_test, test_context}; -#[tokio::test] +#[subxt_test] async fn chain_get_block_hash() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -15,7 +15,7 @@ async fn chain_get_block_hash() { rpc.chain_get_block_hash(None).await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn chain_get_block() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -24,7 +24,7 @@ async fn chain_get_block() { rpc.chain_get_block(hash).await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn chain_get_finalized_head() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -32,7 +32,7 @@ async fn chain_get_finalized_head() { rpc.chain_get_finalized_head().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn chain_subscribe_all_heads() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -41,7 +41,7 @@ async fn chain_subscribe_all_heads() { let _block_header = sub.next().await.unwrap().unwrap(); } -#[tokio::test] +#[subxt_test] async fn chain_subscribe_finalized_heads() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -50,7 +50,7 @@ async fn chain_subscribe_finalized_heads() { let _block_header = sub.next().await.unwrap().unwrap(); } -#[tokio::test] +#[subxt_test] async fn chain_subscribe_new_heads() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -59,7 +59,7 @@ async fn chain_subscribe_new_heads() { let _block_header = sub.next().await.unwrap().unwrap(); } -#[tokio::test] +#[subxt_test] async fn genesis_hash() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -67,7 +67,7 @@ async fn genesis_hash() { let _genesis_hash = rpc.genesis_hash().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn state_get_metadata() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -75,7 +75,7 @@ async fn state_get_metadata() { let _metadata = rpc.state_get_metadata(None).await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn state_call() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -86,7 +86,7 @@ async fn state_call() { .unwrap(); } -#[tokio::test] +#[subxt_test] async fn system_health() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -94,7 +94,7 @@ async fn system_health() { let _ = rpc.system_health().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn system_chain() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -102,7 +102,7 @@ async fn system_chain() { let _ = rpc.system_chain().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn system_name() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -110,7 +110,7 @@ async fn system_name() { let _ = rpc.system_name().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn system_version() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; @@ -118,7 +118,7 @@ async fn system_version() { let _ = rpc.system_version().await.unwrap(); } -#[tokio::test] +#[subxt_test] async fn system_properties() { let ctx = test_context().await; let rpc = ctx.legacy_rpc_methods().await; diff --git a/testing/integration-tests/src/full_client/client/mod.rs b/testing/integration-tests/src/full_client/client/mod.rs index 49c89a28a0..5cdedab679 100644 --- a/testing/integration-tests/src/full_client/client/mod.rs +++ b/testing/integration-tests/src/full_client/client/mod.rs @@ -3,11 +3,14 @@ // see LICENSE for license details. use crate::{ - test_context, + subxt_test, test_context, utils::{node_runtime, wait_for_blocks}, }; use codec::{Decode, Encode}; + +#[cfg(fullclient)] use futures::StreamExt; + use subxt::{ backend::BackendExt, error::{DispatchError, Error}, @@ -15,10 +18,13 @@ use subxt::{ }; use subxt_signer::sr25519::dev; +#[cfg(fullclient)] mod legacy_rpcs; + mod unstable_rpcs; -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_fetch_raw_keys() { let ctx = test_context().await; let api = ctx.client(); @@ -39,7 +45,8 @@ async fn storage_fetch_raw_keys() { assert_eq!(len, 13) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_iter() { let ctx = test_context().await; let api = ctx.client(); @@ -63,7 +70,8 @@ async fn storage_iter() { assert_eq!(len, 13); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_child_values_same_across_backends() { let ctx = test_context().await; @@ -103,7 +111,7 @@ async fn storage_child_values_same_across_backends() { } } -#[tokio::test] +#[subxt_test] async fn transaction_validation() { let ctx = test_context().await; let api = ctx.client(); @@ -137,7 +145,7 @@ async fn transaction_validation() { .unwrap(); } -#[tokio::test] +#[subxt_test] async fn validation_fails() { use std::str::FromStr; use subxt_signer::{sr25519::Keypair, SecretUri}; @@ -171,7 +179,7 @@ async fn validation_fails() { ); } -#[tokio::test] +#[subxt_test] async fn external_signing() { let ctx = test_context().await; let api = ctx.client(); @@ -204,10 +212,11 @@ async fn external_signing() { .unwrap(); } +#[cfg(fullclient)] // TODO: Investigate and fix this test failure when using the UnstableBackend. // (https://github.com/paritytech/subxt/issues/1308) #[cfg(not(feature = "unstable-backend-client"))] -#[tokio::test] +#[subxt_test] async fn submit_large_extrinsic() { let ctx = test_context().await; let api = ctx.client(); @@ -234,7 +243,7 @@ async fn submit_large_extrinsic() { .unwrap(); } -#[tokio::test] +#[subxt_test] async fn decode_a_module_error() { use node_runtime::runtime_types::pallet_assets::pallet as assets; @@ -248,9 +257,14 @@ async fn decode_a_module_error() { // "unknown" module error from the assets pallet. let freeze_unknown_asset = node_runtime::tx().assets().freeze(1, alice_addr); - let err = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&freeze_unknown_asset, &alice) + .create_signed(&freeze_unknown_asset, &alice, Default::default()) + .await + .unwrap(); + + let err = signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -271,7 +285,7 @@ async fn decode_a_module_error() { ); } -#[tokio::test] +#[subxt_test] async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() { let ctx = test_context().await; let api = ctx.client(); @@ -299,7 +313,7 @@ async fn unsigned_extrinsic_is_same_shape_as_polkadotjs() { assert_eq!(actual_tx_bytes, expected_tx_bytes); } -#[tokio::test] +#[subxt_test] async fn extrinsic_hash_is_same_as_returned() { let ctx = test_context().await; let api = ctx.client(); @@ -351,7 +365,7 @@ pub struct InclusionFee { pub adjusted_weight_fee: u128, } -#[tokio::test] +#[subxt_test] async fn partial_fee_estimate_correct() { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs index bcf797c263..19f8e549a8 100644 --- a/testing/integration-tests/src/full_client/client/unstable_rpcs.rs +++ b/testing/integration-tests/src/full_client/client/unstable_rpcs.rs @@ -5,7 +5,7 @@ //! Just sanity checking some of the new RPC methods to try and //! catch differences as the implementations evolve. -use crate::{test_context, utils::node_runtime}; +use crate::{subxt_test, test_context, utils::node_runtime}; use assert_matches::assert_matches; use codec::Encode; use futures::Stream; @@ -18,9 +18,10 @@ use subxt::{ utils::{AccountId32, MultiAddress}, SubstrateConfig, }; + use subxt_signer::sr25519::dev; -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_follow() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; @@ -60,7 +61,7 @@ async fn chainhead_unstable_follow() { ); } -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_body() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; @@ -88,7 +89,7 @@ async fn chainhead_unstable_body() { ); } -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_header() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; @@ -116,7 +117,7 @@ async fn chainhead_unstable_header() { assert_eq!(new_header, old_header); } -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_storage() { let ctx = test_context().await; let api = ctx.client(); @@ -162,7 +163,7 @@ async fn chainhead_unstable_storage() { assert_matches!(event, FollowEvent::OperationStorageDone(res) if res.operation_id == operation_id); } -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_call() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; @@ -199,7 +200,7 @@ async fn chainhead_unstable_call() { ); } -#[tokio::test] +#[subxt_test] async fn chainhead_unstable_unpin() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; @@ -217,7 +218,8 @@ async fn chainhead_unstable_unpin() { assert!(rpc.chainhead_unstable_unpin(sub_id, hash).await.is_err()); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn chainspec_v1_genesishash() { let ctx = test_context().await; let old_rpc = ctx.legacy_rpc_methods().await; @@ -229,7 +231,8 @@ async fn chainspec_v1_genesishash() { assert_eq!(a, b); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn chainspec_v1_chainname() { let ctx = test_context().await; let old_rpc = ctx.legacy_rpc_methods().await; @@ -241,7 +244,8 @@ async fn chainspec_v1_chainname() { assert_eq!(a, b); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn chainspec_v1_properties() { let ctx = test_context().await; let old_rpc = ctx.legacy_rpc_methods().await; @@ -253,7 +257,8 @@ async fn chainspec_v1_properties() { assert_eq!(a, b); } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn transaction_unstable_submit_and_watch() { let ctx = test_context().await; let rpc = ctx.unstable_rpc_methods().await; diff --git a/testing/integration-tests/src/full_client/frame/balances.rs b/testing/integration-tests/src/full_client/frame/balances.rs index d4ef0392de..d721d8e58c 100644 --- a/testing/integration-tests/src/full_client/frame/balances.rs +++ b/testing/integration-tests/src/full_client/frame/balances.rs @@ -4,7 +4,7 @@ use crate::{ node_runtime::{self, balances, runtime_types, system}, - test_context, + subxt_test, test_context, }; use codec::Decode; use subxt::{ @@ -13,7 +13,7 @@ use subxt::{ }; use subxt_signer::sr25519::dev; -#[tokio::test] +#[subxt_test] async fn tx_basic_transfer() -> Result<(), subxt::Error> { let alice = dev::alice(); let bob = dev::bob(); @@ -45,12 +45,18 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> { .balances() .transfer_allow_death(bob_address, 10_000); - let events = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) - .await? + .create_signed(&tx, &alice, Default::default()) + .await?; + + let events = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; + let event = events .find_first::() .expect("Failed to decode balances::events::Transfer") @@ -85,7 +91,8 @@ async fn tx_basic_transfer() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn tx_dynamic_transfer() -> Result<(), subxt::Error> { use subxt::ext::scale_value::{At, Composite, Value}; @@ -207,7 +214,7 @@ async fn tx_dynamic_transfer() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> { let alice = dev::alice(); let bob = dev::bob(); @@ -232,9 +239,15 @@ async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> { .balances() .transfer_allow_death(bob_address.clone(), 10_000); for _ in 0..3 { - api.tx() - .sign_and_submit_then_watch_default(&tx, &alice) - .await? + let signed_extrinsic = api + .tx() + .create_signed(&tx, &alice, Default::default()) + .await?; + + signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; } @@ -250,7 +263,7 @@ async fn multiple_sequential_transfers_work() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_total_issuance() { let ctx = test_context().await; let api = ctx.client(); @@ -267,7 +280,7 @@ async fn storage_total_issuance() { assert_ne!(total_issuance, 0); } -#[tokio::test] +#[subxt_test] async fn storage_balance_lock() -> Result<(), subxt::Error> { let bob_signer = dev::bob(); let bob: AccountId32 = dev::bob().public_key().into(); @@ -279,9 +292,15 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> { runtime_types::pallet_staking::RewardDestination::Stash, ); - api.tx() - .sign_and_submit_then_watch_default(&tx, &bob_signer) - .await? + let signed_extrinsic = api + .tx() + .create_signed(&tx, &bob_signer, Default::default()) + .await?; + + signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await? .find_first::()? @@ -308,7 +327,7 @@ async fn storage_balance_lock() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn transfer_error() { let alice = dev::alice(); let alice_addr = alice.public_key().into(); @@ -324,8 +343,13 @@ async fn transfer_error() { .balances() .transfer_allow_death(alice_addr, 100_000_000_000_000_000); - api.tx() - .sign_and_submit_then_watch_default(&to_bob_tx, &alice) + let signed_extrinsic = api + .tx() + .create_signed(&to_bob_tx, &alice, Default::default()) + .await + .unwrap(); + signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -334,9 +358,14 @@ async fn transfer_error() { // When we try giving all of the funds back, Bob doesn't have // anything left to pay transfer fees, so we hit an error. - let res = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&to_alice_tx, &bob) + .create_signed(&to_alice_tx, &bob, Default::default()) + .await + .unwrap(); + + let res = signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -353,7 +382,7 @@ async fn transfer_error() { ); } -#[tokio::test] +#[subxt_test] async fn transfer_implicit_subscription() { let alice = dev::alice(); let bob: AccountId32 = dev::bob().public_key().into(); @@ -364,9 +393,14 @@ async fn transfer_implicit_subscription() { .balances() .transfer_allow_death(bob.clone().into(), 10_000); - let event = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&to_bob_tx, &alice) + .create_signed(&to_bob_tx, &alice, Default::default()) + .await + .unwrap(); + + let event = signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -386,7 +420,7 @@ async fn transfer_implicit_subscription() { ); } -#[tokio::test] +#[subxt_test] async fn constant_existential_deposit() { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/frame/contracts.rs b/testing/integration-tests/src/full_client/frame/contracts.rs index a91732ead4..65428eeb37 100644 --- a/testing/integration-tests/src/full_client/frame/contracts.rs +++ b/testing/integration-tests/src/full_client/frame/contracts.rs @@ -9,10 +9,10 @@ use crate::{ runtime_types::{pallet_contracts::wasm::Determinism, sp_weights::weight_v2::Weight}, system, }, - test_context, TestContext, + subxt_test, test_context, TestClient, TestConfig, TestContext, }; use subxt::ext::futures::StreamExt; -use subxt::{tx::TxProgress, utils::MultiAddress, Config, Error, OnlineClient, SubstrateConfig}; +use subxt::{tx::TxProgress, utils::MultiAddress, Config, Error}; use subxt_signer::sr25519::{self, dev}; struct ContractsTestContext { @@ -20,8 +20,8 @@ struct ContractsTestContext { signer: sr25519::Keypair, } -type Hash = ::Hash; -type AccountId = ::AccountId; +type Hash = ::Hash; +type AccountId = ::AccountId; /// A dummy contract which does nothing at all. const CONTRACT: &str = r#" @@ -42,7 +42,7 @@ impl ContractsTestContext { Self { cxt, signer } } - fn client(&self) -> OnlineClient { + fn client(&self) -> TestClient { self.cxt.client() } @@ -54,11 +54,16 @@ impl ContractsTestContext { .contracts() .upload_code(code, None, Determinism::Enforced); - let events = self + let signed_extrinsic = self .client() .tx() - .sign_and_submit_then_watch_default(&upload_tx, &self.signer) - .await? + .create_signed(&upload_tx, &self.signer, Default::default()) + .await?; + + let events = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; @@ -84,11 +89,16 @@ impl ContractsTestContext { vec![], // salt ); - let events = self + let signed_extrinsic = self .client() .tx() - .sign_and_submit_then_watch_default(&instantiate_tx, &self.signer) - .await? + .create_signed(&instantiate_tx, &self.signer, Default::default()) + .await?; + + let events = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; @@ -126,11 +136,15 @@ impl ContractsTestContext { salt, ); - let result = self + let signed_extrinsic = self .client() .tx() - .sign_and_submit_then_watch_default(&instantiate_tx, &self.signer) - .await? + .create_signed(&instantiate_tx, &self.signer, Default::default()) + .await?; + let result = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; @@ -146,7 +160,7 @@ impl ContractsTestContext { &self, contract: AccountId, input_data: Vec, - ) -> Result>, Error> { + ) -> Result, Error> { tracing::info!("call: {:?}", contract); let call_tx = node_runtime::tx().contracts().call( MultiAddress::Id(contract), @@ -170,7 +184,7 @@ impl ContractsTestContext { } } -#[tokio::test] +#[subxt_test] async fn tx_instantiate_with_code() { let ctx = ContractsTestContext::init().await; let result = ctx.instantiate_with_code().await; @@ -181,7 +195,7 @@ async fn tx_instantiate_with_code() { ); } -#[tokio::test] +#[subxt_test] async fn tx_instantiate() { let ctx = ContractsTestContext::init().await; let code_hash = ctx.upload_code().await.unwrap(); @@ -194,7 +208,7 @@ async fn tx_instantiate() { ); } -#[tokio::test] +#[subxt_test] async fn tx_call() { let cxt = ContractsTestContext::init().await; let (_, contract) = cxt.instantiate_with_code().await.unwrap(); diff --git a/testing/integration-tests/src/full_client/frame/mod.rs b/testing/integration-tests/src/full_client/frame/mod.rs index 083c92f4dd..2822d0d8e0 100644 --- a/testing/integration-tests/src/full_client/frame/mod.rs +++ b/testing/integration-tests/src/full_client/frame/mod.rs @@ -5,8 +5,11 @@ //! Test interactions with some built-in FRAME pallets. mod balances; -mod contracts; mod staking; -mod sudo; mod system; mod timestamp; + +#[cfg(fullclient)] +mod contracts; +#[cfg(fullclient)] +mod sudo; diff --git a/testing/integration-tests/src/full_client/frame/staking.rs b/testing/integration-tests/src/full_client/frame/staking.rs index 7d285faa8a..db3742185d 100644 --- a/testing/integration-tests/src/full_client/frame/staking.rs +++ b/testing/integration-tests/src/full_client/frame/staking.rs @@ -11,7 +11,7 @@ use crate::{ }, staking, }, - test_context, + subxt_test, test_context, }; use assert_matches::assert_matches; use subxt::error::{DispatchError, Error}; @@ -34,7 +34,7 @@ fn default_validator_prefs() -> ValidatorPrefs { } } -#[tokio::test] +#[subxt_test] async fn validate_with_stash_account() { let ctx = test_context().await; let api = ctx.client(); @@ -45,8 +45,14 @@ async fn validate_with_stash_account() { .staking() .validate(default_validator_prefs()); - api.tx() - .sign_and_submit_then_watch_default(&tx, &alice_stash) + let signed_extrinsic = api + .tx() + .create_signed(&tx, &alice_stash, Default::default()) + .await + .unwrap(); + + signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -54,7 +60,7 @@ async fn validate_with_stash_account() { .expect("should be successful"); } -#[tokio::test] +#[subxt_test] async fn validate_not_possible_for_controller_account() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -65,12 +71,18 @@ async fn validate_not_possible_for_controller_account() -> Result<(), Error> { .staking() .validate(default_validator_prefs()); - let announce_validator = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) - .await? + .create_signed(&tx, &alice, Default::default()) + .await?; + + let announce_validator = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await; + assert_matches!(announce_validator, Err(Error::Runtime(DispatchError::Module(err))) => { let details = err.details().unwrap(); assert_eq!(details.pallet.name(), "Staking"); @@ -79,7 +91,7 @@ async fn validate_not_possible_for_controller_account() -> Result<(), Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn nominate_with_stash_account() { let ctx = test_context().await; let api = ctx.client(); @@ -91,8 +103,14 @@ async fn nominate_with_stash_account() { .staking() .nominate(vec![bob.public_key().to_address()]); - api.tx() - .sign_and_submit_then_watch_default(&tx, &alice_stash) + let signed_extrinsic = api + .tx() + .create_signed(&tx, &alice_stash, Default::default()) + .await + .unwrap(); + + signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -100,7 +118,7 @@ async fn nominate_with_stash_account() { .expect("should be successful"); } -#[tokio::test] +#[subxt_test] async fn nominate_not_possible_for_controller_account() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -112,9 +130,13 @@ async fn nominate_not_possible_for_controller_account() -> Result<(), Error> { .staking() .nominate(vec![bob.public_key().to_address()]); - let nomination = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) + .create_signed(&tx, &alice, Default::default()) + .await + .unwrap(); + let nomination = signed_extrinsic + .submit_and_watch() .await .unwrap() .wait_for_finalized_success() @@ -128,7 +150,7 @@ async fn nominate_not_possible_for_controller_account() -> Result<(), Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn chill_works_for_stash_only() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -141,9 +163,15 @@ async fn chill_works_for_stash_only() -> Result<(), Error> { let nominate_tx = node_runtime::tx() .staking() .nominate(vec![bob_stash.public_key().to_address()]); - api.tx() - .sign_and_submit_then_watch_default(&nominate_tx, &alice_stash) - .await? + + let signed_extrinsic = api + .tx() + .create_signed(&nominate_tx, &alice_stash, Default::default()) + .await?; + signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await?; @@ -161,10 +189,15 @@ async fn chill_works_for_stash_only() -> Result<(), Error> { let chill_tx = node_runtime::tx().staking().chill(); - let chill = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&chill_tx, &alice) - .await? + .create_signed(&chill_tx, &alice, Default::default()) + .await?; + + let chill = signed_extrinsic + .submit_and_watch() + .await + .unwrap() .wait_for_finalized_success() .await; @@ -174,19 +207,23 @@ async fn chill_works_for_stash_only() -> Result<(), Error> { assert_eq!(&details.variant.name, "NotController"); }); - let is_chilled = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&chill_tx, &alice_stash) + .create_signed(&chill_tx, &alice_stash, Default::default()) + .await?; + let is_chilled = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await? .has::()?; + assert!(is_chilled); Ok(()) } -#[tokio::test] +#[subxt_test] async fn tx_bond() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -197,18 +234,24 @@ async fn tx_bond() -> Result<(), Error> { .staking() .bond(100_000_000_000_000, RewardDestination::Stash); - let bond = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&bond_tx, &alice) + .create_signed(&bond_tx, &alice, Default::default()) + .await?; + let bond = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await; - assert!(bond.is_ok()); - let bond_again = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&bond_tx, &alice) + .create_signed(&bond_tx, &alice, Default::default()) + .await?; + + let bond_again = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await; @@ -221,7 +264,7 @@ async fn tx_bond() -> Result<(), Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_history_depth() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -231,7 +274,7 @@ async fn storage_history_depth() -> Result<(), Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_current_era() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); @@ -246,7 +289,7 @@ async fn storage_current_era() -> Result<(), Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_era_reward_points() -> Result<(), Error> { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/frame/sudo.rs b/testing/integration-tests/src/full_client/frame/sudo.rs index fad808d563..489030e497 100644 --- a/testing/integration-tests/src/full_client/frame/sudo.rs +++ b/testing/integration-tests/src/full_client/frame/sudo.rs @@ -8,14 +8,14 @@ use crate::{ runtime_types::{self, sp_weights::weight_v2::Weight}, sudo, }, - test_context, + subxt_test, test_context, }; use subxt_signer::sr25519::dev; type Call = runtime_types::kitchensink_runtime::RuntimeCall; type BalancesCall = runtime_types::pallet_balances::pallet::Call; -#[tokio::test] +#[subxt_test] async fn test_sudo() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -29,9 +29,13 @@ async fn test_sudo() -> Result<(), subxt::Error> { }); let tx = node_runtime::tx().sudo().sudo(call); - let found_event = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) + .create_signed(&tx, &alice, Default::default()) + .await?; + + let found_event = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await? @@ -41,7 +45,7 @@ async fn test_sudo() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -61,9 +65,13 @@ async fn test_sudo_unchecked_weight() -> Result<(), subxt::Error> { }, ); - let found_event = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) + .create_signed(&tx, &alice, Default::default()) + .await?; + + let found_event = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await? diff --git a/testing/integration-tests/src/full_client/frame/system.rs b/testing/integration-tests/src/full_client/frame/system.rs index 1b3823cead..260ea24c61 100644 --- a/testing/integration-tests/src/full_client/frame/system.rs +++ b/testing/integration-tests/src/full_client/frame/system.rs @@ -4,12 +4,12 @@ use crate::{ node_runtime::{self, system}, - test_context, + subxt_test, test_context, }; use assert_matches::assert_matches; use subxt_signer::sr25519::dev; -#[tokio::test] +#[subxt_test] async fn storage_account() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -31,7 +31,7 @@ async fn storage_account() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn tx_remark_with_event() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -42,9 +42,13 @@ async fn tx_remark_with_event() -> Result<(), subxt::Error> { .system() .remark_with_event(b"remarkable".to_vec()); - let found_event = api + let signed_extrinsic = api .tx() - .sign_and_submit_then_watch_default(&tx, &alice) + .create_signed(&tx, &alice, Default::default()) + .await?; + + let found_event = signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await? diff --git a/testing/integration-tests/src/full_client/frame/timestamp.rs b/testing/integration-tests/src/full_client/frame/timestamp.rs index d291042ae5..5d74801434 100644 --- a/testing/integration-tests/src/full_client/frame/timestamp.rs +++ b/testing/integration-tests/src/full_client/frame/timestamp.rs @@ -2,9 +2,9 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{node_runtime, test_context}; +use crate::{node_runtime, subxt_test, test_context}; -#[tokio::test] +#[subxt_test] async fn storage_get_current_timestamp() { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/metadata/validation.rs b/testing/integration-tests/src/full_client/metadata/validation.rs index e10e953d76..6fbda7bbe8 100644 --- a/testing/integration-tests/src/full_client/metadata/validation.rs +++ b/testing/integration-tests/src/full_client/metadata/validation.rs @@ -2,7 +2,7 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{node_runtime, test_context, TestContext}; +use crate::{node_runtime, subxt_test, test_context, TestContext}; use frame_metadata::v15::{ CustomMetadata, ExtrinsicMetadata, OuterEnums, PalletCallMetadata, PalletMetadata, PalletStorageMetadata, RuntimeMetadataV15, StorageEntryMetadata, StorageEntryModifier, @@ -97,7 +97,7 @@ fn pallets_to_metadata(pallets: Vec) -> Metadata { )) } -#[tokio::test] +#[subxt_test] async fn full_metadata_check() { let ctx = test_context().await; let api = ctx.client(); @@ -114,7 +114,7 @@ async fn full_metadata_check() { assert!(!node_runtime::is_codegen_valid_for(&metadata)); } -#[tokio::test] +#[subxt_test] async fn constant_values_are_not_validated() { let ctx = test_context().await; let api = ctx.client(); @@ -146,7 +146,7 @@ async fn constant_values_are_not_validated() { assert!(api.constants().at(&deposit_addr).is_ok()); } -#[tokio::test] +#[subxt_test] async fn calls_check() { let ctx = test_context().await; let api = ctx.client(); @@ -232,7 +232,7 @@ async fn calls_check() { assert!(api.tx().validate(&withdraw_unbonded_addr).is_ok()); } -#[tokio::test] +#[subxt_test] async fn storage_check() { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/runtime_api/mod.rs b/testing/integration-tests/src/full_client/runtime_api/mod.rs index 2a637f9aff..a24dc73603 100644 --- a/testing/integration-tests/src/full_client/runtime_api/mod.rs +++ b/testing/integration-tests/src/full_client/runtime_api/mod.rs @@ -2,12 +2,12 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{node_runtime, test_context}; +use crate::{node_runtime, subxt_test, test_context}; use codec::Encode; use subxt::utils::AccountId32; use subxt_signer::sr25519::dev; -#[tokio::test] +#[subxt_test] async fn account_nonce() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -29,8 +29,13 @@ async fn account_nonce() -> Result<(), subxt::Error> { // Do some transaction to bump the Alice nonce to 1: let remark_tx = node_runtime::tx().system().remark(vec![1, 2, 3, 4, 5]); - api.tx() - .sign_and_submit_then_watch_default(&remark_tx, &alice) + let signed_extrinsic = api + .tx() + .create_signed(&remark_tx, &alice, Default::default()) + .await?; + + signed_extrinsic + .submit_and_watch() .await? .wait_for_finalized_success() .await?; @@ -49,7 +54,7 @@ async fn account_nonce() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn unchecked_extrinsic_encoding() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/full_client/storage/mod.rs b/testing/integration-tests/src/full_client/storage/mod.rs index 1d7dcd50d4..300260016c 100644 --- a/testing/integration-tests/src/full_client/storage/mod.rs +++ b/testing/integration-tests/src/full_client/storage/mod.rs @@ -2,11 +2,14 @@ // This file is dual-licensed as Apache-2.0 or GPL-3.0. // see LICENSE for license details. -use crate::{node_runtime, test_context, utils::wait_for_blocks}; +use crate::{node_runtime, subxt_test, test_context, utils::wait_for_blocks}; + +#[cfg(fullclient)] use subxt::utils::AccountId32; +#[cfg(fullclient)] use subxt_signer::sr25519::dev; -#[tokio::test] +#[subxt_test] async fn storage_plain_lookup() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -27,7 +30,8 @@ async fn storage_plain_lookup() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_map_lookup() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -56,7 +60,8 @@ async fn storage_map_lookup() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_n_mapish_key_is_properly_created() -> Result<(), subxt::Error> { use codec::Encode; use node_runtime::runtime_types::sp_core::crypto::KeyTypeId; @@ -89,7 +94,8 @@ async fn storage_n_mapish_key_is_properly_created() -> Result<(), subxt::Error> Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -125,7 +131,8 @@ async fn storage_n_map_storage_lookup() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[cfg(fullclient)] +#[subxt_test] async fn storage_partial_lookup() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -199,7 +206,7 @@ async fn storage_partial_lookup() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_runtime_wasm_code() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); @@ -208,7 +215,7 @@ async fn storage_runtime_wasm_code() -> Result<(), subxt::Error> { Ok(()) } -#[tokio::test] +#[subxt_test] async fn storage_pallet_storage_version() -> Result<(), subxt::Error> { let ctx = test_context().await; let api = ctx.client(); diff --git a/testing/integration-tests/src/lib.rs b/testing/integration-tests/src/lib.rs index 0eeb3e607f..a57790d452 100644 --- a/testing/integration-tests/src/lib.rs +++ b/testing/integration-tests/src/lib.rs @@ -14,7 +14,10 @@ pub mod utils; #[cfg_attr(test, allow(unused_imports))] use utils::*; -#[cfg(all(test, not(feature = "unstable-light-client")))] +#[cfg(any( + all(test, not(feature = "unstable-light-client")), + all(test, feature = "unstable-light-client-long-running") +))] mod full_client; #[cfg(all(test, feature = "unstable-light-client"))] diff --git a/testing/integration-tests/src/utils/context.rs b/testing/integration-tests/src/utils/context.rs index f572cb5b5e..e987763eb3 100644 --- a/testing/integration-tests/src/utils/context.rs +++ b/testing/integration-tests/src/utils/context.rs @@ -4,6 +4,7 @@ pub(crate) use crate::{node_runtime, utils::TestNodeProcess}; +use subxt::client::OnlineClient; use subxt::SubstrateConfig; /// `substrate-node` should be installed on the $PATH. We fall back @@ -20,8 +21,12 @@ pub async fn test_context_with(authority: String) -> TestContext { proc.spawn::().await.unwrap() } +pub type TestConfig = SubstrateConfig; + pub type TestContext = TestNodeProcess; +pub type TestClient = OnlineClient; + pub async fn test_context() -> TestContext { test_context_with("alice".to_string()).await } diff --git a/testing/integration-tests/src/utils/mod.rs b/testing/integration-tests/src/utils/mod.rs index 08a3111da8..741e152913 100644 --- a/testing/integration-tests/src/utils/mod.rs +++ b/testing/integration-tests/src/utils/mod.rs @@ -8,4 +8,15 @@ mod wait_for_blocks; pub use context::*; pub use node_proc::TestNodeProcess; -pub use wait_for_blocks::wait_for_blocks; +pub use wait_for_blocks::*; + +pub use subxt_test_macro::subxt_test; + +/// The test timeout is set to 1 second. +/// However, the test is sleeping for 5 seconds. +/// This must cause the test to panic. +#[subxt_test(timeout = 1)] +#[should_panic] +async fn test_subxt_macro() { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; +} diff --git a/testing/integration-tests/src/utils/node_proc.rs b/testing/integration-tests/src/utils/node_proc.rs index 860937a6d7..65ce790b3f 100644 --- a/testing/integration-tests/src/utils/node_proc.rs +++ b/testing/integration-tests/src/utils/node_proc.rs @@ -145,7 +145,7 @@ impl TestNodeProcessBuilder { #[allow(unused_assignments, unused_mut)] let mut legacy_client = None; - #[cfg(feature = "unstable-light-client")] + #[cfg(lightclient)] let client = build_light_client(&proc).await?; #[cfg(feature = "unstable-backend-client")] @@ -155,10 +155,7 @@ impl TestNodeProcessBuilder { client }; - #[cfg(all( - not(feature = "unstable-light-client"), - not(feature = "unstable-backend-client") - ))] + #[cfg(all(not(lightclient), not(feature = "unstable-backend-client")))] let client = { let client = build_legacy_client(rpc_client.clone()).await?; legacy_client = Some(client.clone()); @@ -219,7 +216,7 @@ async fn build_unstable_client( Ok(client) } -#[cfg(feature = "unstable-light-client")] +#[cfg(lightclient)] async fn build_light_client(proc: &SubstrateNode) -> Result, String> { use subxt::lightclient::{ChainConfig, LightClient}; @@ -230,7 +227,11 @@ async fn build_light_client(proc: &SubstrateNode) -> Result::from_url(ws_url.clone()) .await .map_err(|err| format!("Failed to connect to node rpc at {ws_url}: {err}"))?; - super::wait_for_blocks(&client).await; + + // Wait for at least a few blocks before starting the light client. + // Otherwise, the lightclient might error with + // `"Error when retrieving the call proof: No node available for call proof query"`. + super::wait_for_number_of_blocks(&client, 5).await; // Now, configure a light client; fetch the chain spec and modify the bootnodes. let bootnode = format!( @@ -252,9 +253,5 @@ async fn build_light_client(proc: &SubstrateNode) -> Result(api: &impl OnlineClientT) { + // The current finalized block and the next block. + wait_for_number_of_blocks(api, 2).await; +} + +/// Wait for a number of blocks to be produced. +pub async fn wait_for_number_of_blocks( + api: &impl OnlineClientT, + number_of_blocks: usize, +) { let mut sub = api.blocks().subscribe_finalized().await.unwrap(); - // The current finalized block: - sub.next().await; - // The next one: - sub.next().await; + + for _ in 0..number_of_blocks { + sub.next().await; + } } diff --git a/testing/integration-tests/subxt-test-macro/Cargo.toml b/testing/integration-tests/subxt-test-macro/Cargo.toml new file mode 100644 index 0000000000..513afd7e7b --- /dev/null +++ b/testing/integration-tests/subxt-test-macro/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "subxt-test-macro" +version.workspace = true +authors.workspace = true +edition.workspace = true +rust-version.workspace = true +publish = false + +license.workspace = true +repository.workspace = true +documentation.workspace = true +homepage.workspace = true +description = "Subxt integration tests proc-macros" + +[lib] +proc-macro = true + +[dependencies] +syn = { workspace = true } +quote = { workspace = true } diff --git a/testing/integration-tests/subxt-test-macro/src/lib.rs b/testing/integration-tests/subxt-test-macro/src/lib.rs new file mode 100644 index 0000000000..0a0e81843c --- /dev/null +++ b/testing/integration-tests/subxt-test-macro/src/lib.rs @@ -0,0 +1,95 @@ +// Copyright 2019-2023 Parity Technologies (UK) Ltd. +// This file is dual-licensed as Apache-2.0 or GPL-3.0. +// see LICENSE for license details. + +extern crate proc_macro; +use proc_macro::TokenStream; + +use quote::{format_ident, quote}; +use syn::{ + parse::{Parse, ParseStream}, + Error, +}; + +/// Environment variable for setting the timeout for the test. +const SUBXT_TEST_TIMEOUT: &str = "SUBXT_TEST_TIMEOUT"; + +/// Default timeout for the test. +const DEFAULT_TIMEOUT_SECS: u64 = 60 * 6; + +#[proc_macro_attribute] +pub fn subxt_test(attr: TokenStream, item: TokenStream) -> TokenStream { + let subxt_attr = match syn::parse::(attr) { + Ok(subxt_attr) => subxt_attr, + Err(err) => return err.into_compile_error().into(), + }; + + // Timeout is determined by: + // - The timeout attribute if it is set. + // - The SUBXT_TEST_TIMEOUT environment variable if it is set. + // - A default of 6 minutes. + let timeout_duration = subxt_attr.timeout.unwrap_or_else(|| { + std::env::var(SUBXT_TEST_TIMEOUT) + .map(|str| str.parse().unwrap_or(DEFAULT_TIMEOUT_SECS)) + .unwrap_or(DEFAULT_TIMEOUT_SECS) + }); + + let func: syn::ItemFn = match syn::parse(item) { + Ok(func) => func, + Err(err) => return err.into_compile_error().into(), + }; + + let func_attrs = &func.attrs; + let func_vis = &func.vis; + let func_sig = &func.sig; + let func_block = &func.block; + + let mut inner_func_sig = func.sig.clone(); + inner_func_sig.ident = format_ident!("{}_inner", inner_func_sig.ident); + let inner_func_name = &inner_func_sig.ident; + + let result = quote! { + #[tokio::test] + #( #func_attrs )* + #func_vis #func_sig { + #func_vis #inner_func_sig + #func_block + + tokio::time::timeout(std::time::Duration::from_secs(#timeout_duration), #inner_func_name()) + .await + .expect("Test timedout") + } + }; + result.into() +} + +mod keywords { + syn::custom_keyword!(timeout); +} + +struct SubxtTestAttr { + timeout: Option, +} + +impl Parse for SubxtTestAttr { + fn parse(input: ParseStream) -> Result { + if input.is_empty() { + return Ok(Self { timeout: None }); + } + + let _keyword = input.parse::()?; + input.parse::()?; + let timeout = input.parse::()?.base10_parse::()?; + + if !input.is_empty() { + return Err(Error::new( + input.span(), + "Expected tokens: `timeout = value`", + )); + } + + Ok(Self { + timeout: Some(timeout), + }) + } +}