mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-12 18:11:10 +00:00
Move test crates into a "testing" folder and add a ui (trybuild) test and ui-test helpers (#567)
* move test crates into a testing folder and add a ui test and helpers * undo wee mixup with another PR * cargo fmt * clippy * tidy ui-tests a little * test different DispatchError types * refactor dispatch error stuff * name ui tests * duff => useless * align versions and cargo fmt
This commit is contained in:
+3
-2
@@ -4,9 +4,10 @@ members = [
|
||||
"cli",
|
||||
"codegen",
|
||||
"examples",
|
||||
"integration-tests",
|
||||
"testing/test-runtime",
|
||||
"testing/integration-tests",
|
||||
"testing/ui-tests",
|
||||
"macro",
|
||||
"metadata",
|
||||
"subxt",
|
||||
"test-runtime"
|
||||
]
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
[package]
|
||||
name = "integration-tests"
|
||||
version = "0.21.0"
|
||||
authors = ["Parity Technologies <admin@parity.io>"]
|
||||
edition = "2021"
|
||||
|
||||
license = "GPL-3.0"
|
||||
readme = "../README.md"
|
||||
repository = "https://github.com/paritytech/subxt"
|
||||
documentation = "https://docs.rs/subxt"
|
||||
homepage = "https://www.parity.io/"
|
||||
description = "Subxt integration tests that rely on the Substrate binary"
|
||||
|
||||
[features]
|
||||
default = ["subxt/integration-tests"]
|
||||
|
||||
[dev-dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
|
||||
frame-metadata = "15.0.0"
|
||||
futures = "0.3.13"
|
||||
hex = "0.4.3"
|
||||
scale-info = { version = "2.0.0", features = ["bit-vec"] }
|
||||
sp-core = { version = "6.0.0", default-features = false }
|
||||
sp-keyring = "6.0.0"
|
||||
sp-runtime = "6.0.0"
|
||||
subxt = { version = "0.21.0", path = "../../subxt" }
|
||||
test-runtime = { path = "../test-runtime" }
|
||||
tokio = { version = "1.8", features = ["macros", "time"] }
|
||||
tracing = "0.1.34"
|
||||
tracing-subscriber = "0.3.11"
|
||||
wabt = "0.10.0"
|
||||
which = "4.0.2"
|
||||
@@ -0,0 +1,133 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
test_node_process,
|
||||
test_node_process_with,
|
||||
utils::node_runtime::system,
|
||||
};
|
||||
|
||||
use sp_core::storage::{
|
||||
well_known_keys,
|
||||
StorageKey,
|
||||
};
|
||||
use sp_keyring::AccountKeyring;
|
||||
|
||||
#[tokio::test]
|
||||
async fn insert_key() {
|
||||
let test_node_process = test_node_process_with(AccountKeyring::Bob).await;
|
||||
let client = test_node_process.client();
|
||||
let public = AccountKeyring::Alice.public().as_array_ref().to_vec();
|
||||
client
|
||||
.rpc()
|
||||
.insert_key(
|
||||
"aura".to_string(),
|
||||
"//Alice".to_string(),
|
||||
public.clone().into(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
assert!(client
|
||||
.rpc()
|
||||
.has_key(public.clone().into(), "aura".to_string())
|
||||
.await
|
||||
.unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_block_hash() {
|
||||
let node_process = test_node_process().await;
|
||||
node_process.client().rpc().block_hash(None).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_block() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let block_hash = client.rpc().block_hash(None).await.unwrap();
|
||||
client.rpc().block(block_hash).await.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_read_proof() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let block_hash = client.rpc().block_hash(None).await.unwrap();
|
||||
client
|
||||
.rpc()
|
||||
.read_proof(
|
||||
vec![
|
||||
StorageKey(well_known_keys::HEAP_PAGES.to_vec()),
|
||||
StorageKey(well_known_keys::EXTRINSIC_INDEX.to_vec()),
|
||||
],
|
||||
block_hash,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn chain_subscribe_blocks() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let mut blocks = client.rpc().subscribe_blocks().await.unwrap();
|
||||
blocks.next().await.unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn chain_subscribe_finalized_blocks() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let mut blocks = client.rpc().subscribe_finalized_blocks().await.unwrap();
|
||||
blocks.next().await.unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_keys() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let keys = client
|
||||
.storage()
|
||||
.fetch_keys::<system::storage::Account>(4, None, None)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(keys.len(), 4)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_iter() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
let mut iter = client
|
||||
.storage()
|
||||
.iter::<system::storage::Account>(None)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut i = 0;
|
||||
while iter.next().await.unwrap().is_some() {
|
||||
i += 1;
|
||||
}
|
||||
assert_eq!(i, 13);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn fetch_system_info() {
|
||||
let node_process = test_node_process().await;
|
||||
let client = node_process.client();
|
||||
assert_eq!(client.rpc().system_chain().await.unwrap(), "Development");
|
||||
assert_eq!(client.rpc().system_name().await.unwrap(), "Substrate Node");
|
||||
assert!(!client.rpc().system_version().await.unwrap().is_empty());
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#![deny(unused_crate_dependencies)]
|
||||
|
||||
#[cfg(test)]
|
||||
mod codegen;
|
||||
#[cfg(test)]
|
||||
mod utils;
|
||||
|
||||
#[cfg(test)]
|
||||
mod client;
|
||||
#[cfg(test)]
|
||||
mod events;
|
||||
#[cfg(test)]
|
||||
mod frame;
|
||||
#[cfg(test)]
|
||||
mod metadata;
|
||||
#[cfg(test)]
|
||||
mod storage;
|
||||
|
||||
#[cfg(test)]
|
||||
use test_runtime::node_runtime;
|
||||
#[cfg(test)]
|
||||
use utils::*;
|
||||
@@ -0,0 +1,340 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::{
|
||||
test_context,
|
||||
TestContext,
|
||||
};
|
||||
use frame_metadata::{
|
||||
ExtrinsicMetadata,
|
||||
PalletCallMetadata,
|
||||
PalletMetadata,
|
||||
PalletStorageMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
RuntimeMetadataV14,
|
||||
StorageEntryMetadata,
|
||||
StorageEntryModifier,
|
||||
StorageEntryType,
|
||||
};
|
||||
use scale_info::{
|
||||
build::{
|
||||
Fields,
|
||||
Variants,
|
||||
},
|
||||
meta_type,
|
||||
Path,
|
||||
Type,
|
||||
TypeInfo,
|
||||
};
|
||||
use subxt::{
|
||||
ClientBuilder,
|
||||
DefaultConfig,
|
||||
Metadata,
|
||||
SubstrateExtrinsicParams,
|
||||
};
|
||||
|
||||
use crate::utils::node_runtime;
|
||||
|
||||
type RuntimeApi =
|
||||
node_runtime::RuntimeApi<DefaultConfig, SubstrateExtrinsicParams<DefaultConfig>>;
|
||||
|
||||
async fn metadata_to_api(metadata: RuntimeMetadataV14, cxt: &TestContext) -> RuntimeApi {
|
||||
let prefixed = RuntimeMetadataPrefixed::from(metadata);
|
||||
let metadata = Metadata::try_from(prefixed).unwrap();
|
||||
|
||||
ClientBuilder::new()
|
||||
.set_url(cxt.node_proc.ws_url().to_string())
|
||||
.set_metadata(metadata)
|
||||
.build()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_runtime_api::<node_runtime::RuntimeApi<
|
||||
DefaultConfig,
|
||||
SubstrateExtrinsicParams<DefaultConfig>,
|
||||
>>()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn full_metadata_check() {
|
||||
let cxt = test_context().await;
|
||||
let api = &cxt.api;
|
||||
|
||||
// Runtime metadata is identical to the metadata used during API generation.
|
||||
assert!(api.validate_metadata().is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let mut metadata: RuntimeMetadataV14 = {
|
||||
let locked_client_metadata = api.client.metadata();
|
||||
let client_metadata = locked_client_metadata.read();
|
||||
client_metadata.runtime_metadata().clone()
|
||||
};
|
||||
metadata.pallets[0].name = "NewPallet".to_string();
|
||||
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
assert_eq!(
|
||||
new_api
|
||||
.validate_metadata()
|
||||
.err()
|
||||
.expect("Validation should fail for incompatible metadata"),
|
||||
::subxt::MetadataError::IncompatibleMetadata
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn constants_check() {
|
||||
let cxt = test_context().await;
|
||||
let api = &cxt.api;
|
||||
|
||||
// Ensure that `ExistentialDeposit` is compatible before altering the metadata.
|
||||
assert!(cxt.api.constants().balances().existential_deposit().is_ok());
|
||||
|
||||
// Modify the metadata.
|
||||
let mut metadata: RuntimeMetadataV14 = {
|
||||
let locked_client_metadata = api.client.metadata();
|
||||
let client_metadata = locked_client_metadata.read();
|
||||
client_metadata.runtime_metadata().clone()
|
||||
};
|
||||
|
||||
let mut existential = metadata
|
||||
.pallets
|
||||
.iter_mut()
|
||||
.find(|pallet| pallet.name == "Balances")
|
||||
.expect("Metadata must contain Balances pallet")
|
||||
.constants
|
||||
.iter_mut()
|
||||
.find(|constant| constant.name == "ExistentialDeposit")
|
||||
.expect("ExistentialDeposit constant must be present");
|
||||
existential.value = vec![0u8; 32];
|
||||
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
|
||||
assert!(new_api.validate_metadata().is_err());
|
||||
assert!(new_api
|
||||
.constants()
|
||||
.balances()
|
||||
.existential_deposit()
|
||||
.is_err());
|
||||
|
||||
// Other constant validation should not be impacted.
|
||||
assert!(new_api.constants().balances().max_locks().is_ok());
|
||||
}
|
||||
|
||||
fn default_pallet() -> PalletMetadata {
|
||||
PalletMetadata {
|
||||
name: "Test",
|
||||
storage: None,
|
||||
calls: None,
|
||||
event: None,
|
||||
constants: vec![],
|
||||
error: None,
|
||||
index: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn pallets_to_metadata(pallets: Vec<PalletMetadata>) -> RuntimeMetadataV14 {
|
||||
RuntimeMetadataV14::new(
|
||||
pallets,
|
||||
ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
},
|
||||
meta_type::<()>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn calls_check() {
|
||||
let cxt = test_context().await;
|
||||
|
||||
// Ensure that `Unbond` and `WinthdrawUnbonded` calls are compatible before altering the metadata.
|
||||
assert!(cxt.api.tx().staking().unbond(123_456_789_012_345).is_ok());
|
||||
assert!(cxt.api.tx().staking().withdraw_unbonded(10).is_ok());
|
||||
|
||||
// Reconstruct the `Staking` call as is.
|
||||
struct CallRec;
|
||||
impl TypeInfo for CallRec {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("Call", "pallet_staking::pallet::pallet"))
|
||||
.variant(
|
||||
Variants::new()
|
||||
.variant("unbond", |v| {
|
||||
v.index(0).fields(Fields::named().field(|f| {
|
||||
f.compact::<u128>()
|
||||
.name("value")
|
||||
.type_name("BalanceOf<T>")
|
||||
}))
|
||||
})
|
||||
.variant("withdraw_unbonded", |v| {
|
||||
v.index(1).fields(Fields::named().field(|f| {
|
||||
f.ty::<u32>().name("num_slashing_spans").type_name("u32")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
let pallet = PalletMetadata {
|
||||
name: "Staking",
|
||||
calls: Some(PalletCallMetadata {
|
||||
ty: meta_type::<CallRec>(),
|
||||
}),
|
||||
..default_pallet()
|
||||
};
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
assert!(new_api.tx().staking().unbond(123_456_789_012_345).is_ok());
|
||||
assert!(new_api.tx().staking().withdraw_unbonded(10).is_ok());
|
||||
|
||||
// Change `Unbond` call but leave the rest as is.
|
||||
struct CallRecSecond;
|
||||
impl TypeInfo for CallRecSecond {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("Call", "pallet_staking::pallet::pallet"))
|
||||
.variant(
|
||||
Variants::new()
|
||||
.variant("unbond", |v| {
|
||||
v.index(0).fields(Fields::named().field(|f| {
|
||||
// Is of type u32 instead of u128.
|
||||
f.compact::<u32>().name("value").type_name("BalanceOf<T>")
|
||||
}))
|
||||
})
|
||||
.variant("withdraw_unbonded", |v| {
|
||||
v.index(1).fields(Fields::named().field(|f| {
|
||||
f.ty::<u32>().name("num_slashing_spans").type_name("u32")
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
let pallet = PalletMetadata {
|
||||
name: "Staking",
|
||||
calls: Some(PalletCallMetadata {
|
||||
ty: meta_type::<CallRecSecond>(),
|
||||
}),
|
||||
..default_pallet()
|
||||
};
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
// Unbond call should fail, while withdraw_unbonded remains compatible.
|
||||
assert!(new_api.tx().staking().unbond(123_456_789_012_345).is_err());
|
||||
assert!(new_api.tx().staking().withdraw_unbonded(10).is_ok());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn storage_check() {
|
||||
let cxt = test_context().await;
|
||||
|
||||
// Ensure that `ExtrinsicCount` and `EventCount` storages are compatible before altering the metadata.
|
||||
assert!(cxt
|
||||
.api
|
||||
.storage()
|
||||
.system()
|
||||
.extrinsic_count(None)
|
||||
.await
|
||||
.is_ok());
|
||||
assert!(cxt
|
||||
.api
|
||||
.storage()
|
||||
.system()
|
||||
.all_extrinsics_len(None)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
// Reconstruct the storage.
|
||||
let storage = PalletStorageMetadata {
|
||||
prefix: "System",
|
||||
entries: vec![
|
||||
StorageEntryMetadata {
|
||||
name: "ExtrinsicCount",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(meta_type::<u32>()),
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: "AllExtrinsicsLen",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(meta_type::<u32>()),
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
},
|
||||
],
|
||||
};
|
||||
let pallet = PalletMetadata {
|
||||
name: "System",
|
||||
storage: Some(storage),
|
||||
..default_pallet()
|
||||
};
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
assert!(new_api
|
||||
.storage()
|
||||
.system()
|
||||
.extrinsic_count(None)
|
||||
.await
|
||||
.is_ok());
|
||||
assert!(new_api
|
||||
.storage()
|
||||
.system()
|
||||
.all_extrinsics_len(None)
|
||||
.await
|
||||
.is_ok());
|
||||
|
||||
// Reconstruct the storage while modifying ExtrinsicCount.
|
||||
let storage = PalletStorageMetadata {
|
||||
prefix: "System",
|
||||
entries: vec![
|
||||
StorageEntryMetadata {
|
||||
name: "ExtrinsicCount",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
// Previously was u32.
|
||||
ty: StorageEntryType::Plain(meta_type::<u8>()),
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
},
|
||||
StorageEntryMetadata {
|
||||
name: "AllExtrinsicsLen",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Plain(meta_type::<u32>()),
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
},
|
||||
],
|
||||
};
|
||||
let pallet = PalletMetadata {
|
||||
name: "System",
|
||||
storage: Some(storage),
|
||||
..default_pallet()
|
||||
};
|
||||
let metadata = pallets_to_metadata(vec![pallet]);
|
||||
let new_api = metadata_to_api(metadata, &cxt).await;
|
||||
assert!(new_api
|
||||
.storage()
|
||||
.system()
|
||||
.extrinsic_count(None)
|
||||
.await
|
||||
.is_err());
|
||||
assert!(new_api
|
||||
.storage()
|
||||
.system()
|
||||
.all_extrinsics_len(None)
|
||||
.await
|
||||
.is_ok());
|
||||
}
|
||||
@@ -4,12 +4,12 @@ version = "0.21.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
subxt = { path = "../subxt" }
|
||||
subxt = { path = "../../subxt" }
|
||||
sp-runtime = "6.0.0"
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
|
||||
|
||||
[build-dependencies]
|
||||
subxt = { path = "../subxt" }
|
||||
subxt = { path = "../../subxt" }
|
||||
sp-core = "6.0.0"
|
||||
tokio = { version = "1.8", features = ["macros", "rt-multi-thread"] }
|
||||
which = "4.2.2"
|
||||
@@ -0,0 +1,15 @@
|
||||
[package]
|
||||
name = "ui-tests"
|
||||
version = "0.21.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild = "1.0.63"
|
||||
scale-info = { version = "2.0.0", features = ["bit-vec"] }
|
||||
frame-metadata = "15.0.0"
|
||||
codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "full", "bit-vec"] }
|
||||
subxt = { path = "../../subxt" }
|
||||
@@ -0,0 +1,39 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use crate::utils::{
|
||||
dispatch_error::{
|
||||
ArrayDispatchError,
|
||||
LegacyDispatchError,
|
||||
NamedFieldDispatchError,
|
||||
},
|
||||
generate_metadata_from_pallets_custom_dispatch_error,
|
||||
};
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
|
||||
pub fn metadata_array_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<ArrayDispatchError>(vec![])
|
||||
}
|
||||
|
||||
pub fn metadata_legacy_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<LegacyDispatchError>(vec![])
|
||||
}
|
||||
|
||||
pub fn metadata_named_field_dispatch_error() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<NamedFieldDispatchError>(
|
||||
vec![],
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
#![cfg(test)]
|
||||
|
||||
mod dispatch_errors;
|
||||
mod storage;
|
||||
mod utils;
|
||||
|
||||
use crate::utils::MetadataTestRunner;
|
||||
|
||||
// Each of these tests leads to some rust code being compiled and
|
||||
// executed to test that compilation is successful (or errors in the
|
||||
// way that we'd expect).
|
||||
#[test]
|
||||
fn ui_tests() {
|
||||
let mut m = MetadataTestRunner::default();
|
||||
let t = trybuild::TestCases::new();
|
||||
|
||||
// Check that storage maps with no keys are handled properly.
|
||||
t.pass(&m.path_to_ui_test_for_metadata(
|
||||
"storage_map_no_keys",
|
||||
storage::metadata_storage_map_no_keys(),
|
||||
));
|
||||
|
||||
// Test that the codegen can handle the different types of DispatchError.
|
||||
t.pass(&m.path_to_ui_test_for_metadata(
|
||||
"named_field_dispatch_error",
|
||||
dispatch_errors::metadata_named_field_dispatch_error(),
|
||||
));
|
||||
t.pass(&m.path_to_ui_test_for_metadata(
|
||||
"legacy_dispatch_error",
|
||||
dispatch_errors::metadata_legacy_dispatch_error(),
|
||||
));
|
||||
t.pass(&m.path_to_ui_test_for_metadata(
|
||||
"array_dispatch_error",
|
||||
dispatch_errors::metadata_array_dispatch_error(),
|
||||
));
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use frame_metadata::{
|
||||
RuntimeMetadataPrefixed,
|
||||
StorageEntryMetadata,
|
||||
StorageEntryModifier,
|
||||
StorageEntryType,
|
||||
};
|
||||
use scale_info::meta_type;
|
||||
|
||||
use crate::utils::generate_metadata_from_storage_entries;
|
||||
|
||||
/// Generate metadata which contains a `Map` storage entry with no hashers/values.
|
||||
/// This is a bit of an odd case, but it was raised in https://github.com/paritytech/subxt/issues/552,
|
||||
/// and this test will fail before the fix and should pass once the fix is applied.
|
||||
pub fn metadata_storage_map_no_keys() -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_storage_entries(vec![StorageEntryMetadata {
|
||||
name: "MapWithNoKeys",
|
||||
modifier: StorageEntryModifier::Optional,
|
||||
ty: StorageEntryType::Map {
|
||||
hashers: vec![],
|
||||
key: meta_type::<()>(),
|
||||
value: meta_type::<u32>(),
|
||||
},
|
||||
default: vec![0],
|
||||
docs: vec![],
|
||||
}])
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use scale_info::{
|
||||
build::{
|
||||
FieldsBuilder,
|
||||
NamedFields,
|
||||
UnnamedFields,
|
||||
Variants,
|
||||
},
|
||||
Path,
|
||||
Type,
|
||||
TypeInfo,
|
||||
};
|
||||
|
||||
/// See the `ModuleErrorType` in `subxt_codegen` for more info on the different DispatchError
|
||||
/// types that we've encountered. We need the path to match `sp_runtime::DispatchError`, otherwise
|
||||
/// we could just implement roughly the correct types and derive TypeInfo on them.
|
||||
|
||||
/// This type has TypeInfo compatible with the `NamedField` version of the DispatchError.
|
||||
/// This is the oldest version that subxt supports:
|
||||
/// `DispatchError::Module { index: u8, error: u8 }`
|
||||
pub enum NamedFieldDispatchError {}
|
||||
impl TypeInfo for NamedFieldDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(
|
||||
FieldsBuilder::<NamedFields>::default()
|
||||
.field(|b| b.name("error").ty::<u8>())
|
||||
.field(|b| b.name("index").ty::<u8>()),
|
||||
)
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This type has TypeInfo compatible with the `LegacyError` version of the DispatchError.
|
||||
/// This is the version wasn't around for long:
|
||||
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: u8 } )`
|
||||
pub enum LegacyDispatchError {}
|
||||
impl TypeInfo for LegacyDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
struct ModuleError;
|
||||
impl TypeInfo for ModuleError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("ModuleError", "sp_runtime"))
|
||||
.composite(
|
||||
FieldsBuilder::<NamedFields>::default()
|
||||
.field(|b| b.name("index").ty::<u8>())
|
||||
.field(|b| b.name("error").ty::<u8>()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(
|
||||
FieldsBuilder::<UnnamedFields>::default()
|
||||
.field(|b| b.ty::<ModuleError>()),
|
||||
)
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
/// This type has TypeInfo compatible with the `ArrayError` version of the DispatchError.
|
||||
/// This is the current version:
|
||||
/// `DispatchError::Module ( sp_runtime::ModuleError { index: u8, error: [u8; 4] } )`
|
||||
pub enum ArrayDispatchError {}
|
||||
impl TypeInfo for ArrayDispatchError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
struct ModuleError;
|
||||
impl TypeInfo for ModuleError {
|
||||
type Identity = Self;
|
||||
fn type_info() -> Type {
|
||||
Type::builder()
|
||||
.path(Path::new("ModuleError", "sp_runtime"))
|
||||
.composite(
|
||||
FieldsBuilder::<NamedFields>::default()
|
||||
.field(|b| b.name("index").ty::<u8>())
|
||||
.field(|b| b.name("error").ty::<[u8; 4]>()),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Type::builder()
|
||||
.path(Path::new("DispatchError", "sp_runtime"))
|
||||
.variant(Variants::new().variant("Module", |builder| {
|
||||
builder
|
||||
.fields(
|
||||
FieldsBuilder::<UnnamedFields>::default()
|
||||
.field(|b| b.ty::<ModuleError>()),
|
||||
)
|
||||
.index(0)
|
||||
}))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use codec::Encode;
|
||||
use frame_metadata::RuntimeMetadataPrefixed;
|
||||
|
||||
static TEST_DIR_PREFIX: &str = "subxt_generated_ui_tests_";
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MetadataTestRunner {
|
||||
index: usize,
|
||||
}
|
||||
|
||||
impl MetadataTestRunner {
|
||||
pub fn path_to_ui_test_for_metadata(
|
||||
&mut self,
|
||||
name: impl AsRef<str>,
|
||||
metadata: RuntimeMetadataPrefixed,
|
||||
) -> String {
|
||||
let test_name = name.as_ref();
|
||||
|
||||
// increment test index to avoid overlaps.
|
||||
let index = self.index;
|
||||
self.index += 1;
|
||||
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{index}"));
|
||||
|
||||
let tmp_metadata_path = {
|
||||
let mut t = tmp_dir.clone();
|
||||
t.push("metadata.scale");
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
let tmp_rust_path = {
|
||||
let mut t = tmp_dir.clone();
|
||||
t.push(format!("{test_name}.rs"));
|
||||
t.to_string_lossy().into_owned()
|
||||
};
|
||||
|
||||
let encoded_metadata = metadata.encode();
|
||||
let rust_file = format!(
|
||||
r#"
|
||||
use subxt;
|
||||
|
||||
#[subxt::subxt(runtime_metadata_path = "{tmp_metadata_path}")]
|
||||
pub mod polkadot {{}}
|
||||
|
||||
fn main() {{}}
|
||||
"#
|
||||
);
|
||||
|
||||
std::fs::create_dir_all(&tmp_dir).expect("could not create tmp ui test dir");
|
||||
// Write metadata to tmp folder:
|
||||
std::fs::write(&tmp_metadata_path, &encoded_metadata).unwrap();
|
||||
// Write test file to tmp folder (it'll be moved by trybuild):
|
||||
std::fs::write(&tmp_rust_path, &rust_file).unwrap();
|
||||
|
||||
tmp_rust_path
|
||||
}
|
||||
}
|
||||
|
||||
// `trybuild` runs all tests once it's dropped. So, we defer all cleanup until we
|
||||
// are dropped too, to make sure that cleanup happens after tests are ran.
|
||||
impl Drop for MetadataTestRunner {
|
||||
fn drop(&mut self) {
|
||||
for i in 0..self.index {
|
||||
let mut tmp_dir = std::env::temp_dir();
|
||||
tmp_dir.push(format!("{TEST_DIR_PREFIX}{i}"));
|
||||
std::fs::remove_dir_all(tmp_dir).expect("cannot cleanup temp files");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
// Copyright 2019-2022 Parity Technologies (UK) Ltd.
|
||||
// This file is part of subxt.
|
||||
//
|
||||
// subxt is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// subxt is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with subxt. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
pub mod dispatch_error;
|
||||
mod metadata_test_runner;
|
||||
|
||||
use frame_metadata::{
|
||||
v14::RuntimeMetadataV14,
|
||||
ExtrinsicMetadata,
|
||||
PalletMetadata,
|
||||
PalletStorageMetadata,
|
||||
RuntimeMetadataPrefixed,
|
||||
StorageEntryMetadata,
|
||||
};
|
||||
use scale_info::{
|
||||
meta_type,
|
||||
IntoPortable,
|
||||
TypeInfo,
|
||||
};
|
||||
|
||||
pub use metadata_test_runner::MetadataTestRunner;
|
||||
|
||||
/// Given some pallet metadata, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// We default to a useless extrinsic type, and register a fake `DispatchError`
|
||||
/// type matching the generic type param provided.
|
||||
pub fn generate_metadata_from_pallets_custom_dispatch_error<
|
||||
DispatchError: TypeInfo + 'static,
|
||||
>(
|
||||
pallets: Vec<PalletMetadata>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
// We don't care about the extrinsic type.
|
||||
let extrinsic = ExtrinsicMetadata {
|
||||
ty: meta_type::<()>(),
|
||||
version: 0,
|
||||
signed_extensions: vec![],
|
||||
};
|
||||
|
||||
// Construct metadata manually from our types (See `RuntimeMetadataV14::new()`).
|
||||
// Add any extra types we need to the registry.
|
||||
let mut registry = scale_info::Registry::new();
|
||||
let pallets = registry.map_into_portable(pallets);
|
||||
let extrinsic = extrinsic.into_portable(&mut registry);
|
||||
let ty = registry.register_type(&meta_type::<()>());
|
||||
|
||||
// Metadata needs to contain this DispatchError, since codegen looks for it.
|
||||
registry.register_type(&meta_type::<DispatchError>());
|
||||
|
||||
let metadata = RuntimeMetadataV14 {
|
||||
types: registry.into(),
|
||||
pallets,
|
||||
extrinsic,
|
||||
ty,
|
||||
};
|
||||
|
||||
RuntimeMetadataPrefixed::from(metadata)
|
||||
}
|
||||
|
||||
/// Given some pallet metadata, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// We default to a useless extrinsic type, and register a fake `DispatchError`
|
||||
/// type so that codegen is happy with the metadata generated.
|
||||
pub fn generate_metadata_from_pallets(
|
||||
pallets: Vec<PalletMetadata>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
generate_metadata_from_pallets_custom_dispatch_error::<
|
||||
dispatch_error::ArrayDispatchError,
|
||||
>(pallets)
|
||||
}
|
||||
|
||||
/// Given some storage entries, generate a [`RuntimeMetadataPrefixed`] struct.
|
||||
/// We default to a useless extrinsic type, mock a pallet out, and register a
|
||||
/// fake `DispatchError` type so that codegen is happy with the metadata generated.
|
||||
pub fn generate_metadata_from_storage_entries(
|
||||
storage_entries: Vec<StorageEntryMetadata>,
|
||||
) -> RuntimeMetadataPrefixed {
|
||||
let storage = PalletStorageMetadata {
|
||||
prefix: "System",
|
||||
entries: storage_entries,
|
||||
};
|
||||
|
||||
let pallet = PalletMetadata {
|
||||
index: 0,
|
||||
name: "System",
|
||||
storage: Some(storage),
|
||||
constants: vec![],
|
||||
calls: None,
|
||||
event: None,
|
||||
error: None,
|
||||
};
|
||||
|
||||
generate_metadata_from_pallets(vec![pallet])
|
||||
}
|
||||
Reference in New Issue
Block a user