diff --git a/Cargo.lock b/Cargo.lock index f048b4f..772e2d6 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -8736,6 +8736,7 @@ dependencies = [ "parity-scale-codec", "polkadot-sdk", "scale-info", + "test-utils", ] [[package]] @@ -15994,6 +15995,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" +[[package]] +name = "test-utils" +version = "0.1.0" +dependencies = [ + "pallet-torus0", + "parity-scale-codec", + "polkadot-sdk", + "scale-info", +] + [[package]] name = "testnet-parachains-constants" version = "10.0.0" diff --git a/Cargo.toml b/Cargo.toml index 0a5216b..b8b4850 100755 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["node", "runtime", "pallets/*", "xtask"] +members = ["node", "runtime", "pallets/*", "test-utils", "xtask"] resolver = "2" [workspace.package] @@ -13,6 +13,8 @@ torus-runtime = { path = "./runtime", default-features = false } pallet-governance = { path = "./pallets/governance", default-features = false } pallet-torus0 = { path = "./pallets/torus0", default-features = false } +test-utils.path = "./test-utils" + # Substrate codec = { package = "parity-scale-codec", version = "3.6.12", default-features = false } scale-info = { version = "2.11.1", default-features = false } diff --git a/pallets/torus0/Cargo.toml b/pallets/torus0/Cargo.toml index b2cc669..6e0ab91 100644 --- a/pallets/torus0/Cargo.toml +++ b/pallets/torus0/Cargo.toml @@ -16,3 +16,6 @@ try-runtime = ["polkadot-sdk/try-runtime"] codec = { workspace = true, features = ["derive"] } scale-info = { workspace = true, features = ["derive"] } polkadot-sdk = { workspace = true, features = ["experimental", "runtime"] } + +[dev-dependencies] +test-utils.workspace = true diff --git a/pallets/torus0/tests/foo.rs b/pallets/torus0/tests/foo.rs new file mode 100644 index 0000000..07c6fdd --- /dev/null +++ b/pallets/torus0/tests/foo.rs @@ -0,0 +1,5 @@ +#[test] +fn foo() { + let val = test_utils::new_test_ext().execute_with(|| "val"); + assert_eq!(val, "val"); +} diff --git a/test-utils/Cargo.toml b/test-utils/Cargo.toml new file mode 100644 index 0000000..74a9be5 --- /dev/null +++ b/test-utils/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "test-utils" +version = "0.1.0" +authors.workspace = true +edition.workspace = true + +[dependencies] +codec.workspace = true +scale-info.workspace = true +polkadot-sdk = { workspace = true, features = [ + "std", + "runtime", + "pallet-balances", +] } + +pallet-torus0.workspace = true diff --git a/test-utils/src/lib.rs b/test-utils/src/lib.rs new file mode 100644 index 0000000..4510a3a --- /dev/null +++ b/test-utils/src/lib.rs @@ -0,0 +1,201 @@ +#![allow(non_camel_case_types)] + +use polkadot_sdk::{ + frame_support::{ + self, parameter_types, + traits::{Currency, Everything, Hooks}, + }, + frame_system, pallet_balances, + polkadot_sdk_frame::runtime::prelude::*, + sp_core::H256, + sp_io, + sp_runtime::traits::{BlakeTwo256, IdentityLookup}, + sp_runtime::BuildStorage, + sp_tracing, +}; + +#[frame_construct_runtime] +mod runtime { + #[runtime::runtime] + #[runtime::derive(RuntimeCall, RuntimeEvent, RuntimeError, RuntimeOrigin)] + pub struct Test; + + #[runtime::pallet_index(0)] + pub type System = frame_system::Pallet; + + #[runtime::pallet_index(1)] + pub type Balances = pallet_balances::Pallet; + + #[runtime::pallet_index(2)] + pub type Torus0 = pallet_torus0::Pallet; +} + +pub type Block = frame_system::mocking::MockBlock; +pub type AccountId = u32; + +#[allow(dead_code)] +pub type BalanceCall = pallet_balances::Call; + +#[allow(dead_code)] +pub type TestRuntimeCall = frame_system::Call; + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u16 = 888; +} + +// Balance of an account. +pub type Balance = u128; + +// An index to a block. +pub type BlockNumber = u64; + +parameter_types! { + pub const ExistentialDeposit: Balance = 1; + pub const MaxLocks: u32 = 50; + pub const MaxReserves: u32 = 50; +} + +impl pallet_torus0::Config for Test {} + +impl pallet_balances::Config for Test { + type RuntimeEvent = RuntimeEvent; + type AccountStore = System; + type Balance = Balance; + type DustRemoval = (); + type ExistentialDeposit = ExistentialDeposit; + type MaxLocks = MaxLocks; + type WeightInfo = (); + type MaxReserves = MaxReserves; + type ReserveIdentifier = (); + type RuntimeHoldReason = (); + type FreezeIdentifier = (); + type MaxFreezes = polkadot_sdk::frame_support::traits::ConstU32<16>; + type RuntimeFreezeReason = (); +} + +impl frame_system::Config for Test { + type BaseCallFilter = Everything; + type Block = Block; + type BlockWeights = (); + type BlockLength = (); + type AccountId = AccountId; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type Lookup = IdentityLookup; + type RuntimeEvent = RuntimeEvent; + type RuntimeOrigin = RuntimeOrigin; + type BlockHashCount = BlockHashCount; + type DbWeight = (); + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; + + type RuntimeTask = (); + type SingleBlockMigrations = (); + type MultiBlockMigrator = (); + type PreInherents = (); + type PostInherents = (); + type PostTransactions = (); +} + +// Utility functions +//=================== + +const TOKEN_DECIMALS: u32 = 18; + +pub const fn to_nano(x: Balance) -> Balance { + x.saturating_add((10 as Balance).pow(TOKEN_DECIMALS)) +} + +pub const fn from_nano(x: Balance) -> Balance { + x.saturating_div((10 as Balance).pow(TOKEN_DECIMALS)) +} + +pub fn add_balance(key: AccountId, amount: Balance) { + drop(>::deposit_creating( + &key, amount, + )); +} + +pub fn new_test_ext() -> sp_io::TestExternalities { + new_test_ext_with_block(0) +} + +pub fn new_test_ext_with_block(block: BlockNumber) -> sp_io::TestExternalities { + sp_tracing::try_init_simple(); + let t = frame_system::GenesisConfig::::default() + .build_storage() + .unwrap(); + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(block)); + ext +} + +pub fn get_origin(key: AccountId) -> RuntimeOrigin { + <::RuntimeOrigin>::signed(key) +} + +pub fn step_block(count: BlockNumber) { + let current = System::block_number(); + for block in current..current + count { + Torus0::on_finalize(block); + System::on_finalize(block); + System::set_block_number(block + 1); + System::on_initialize(block + 1); + Torus0::on_initialize(block + 1); + } +} + +pub fn run_to_block(target: BlockNumber) { + step_block(target - System::block_number()); +} + +pub fn get_balance(key: AccountId) -> Balance { + >::free_balance(&key) +} + +pub fn round_first_five(num: u64) -> u64 { + let place_value = 10_u64.pow(num.to_string().len() as u32 - 5); + let first_five = num / place_value; + + if first_five % 10 >= 5 { + (first_five / 10 + 1) * place_value * 10 + } else { + (first_five / 10) * place_value * 10 + } +} + +#[macro_export] +macro_rules! assert_ok { + ( $x:expr $(,)? ) => { + match $x { + Ok(v) => v, + is => panic!("Expected Ok(_). Got {is:#?}"), + } + }; + ( $x:expr, $y:expr $(,)? ) => { + assert_eq!($x, Ok($y)); + }; +} + +#[macro_export] +macro_rules! assert_in_range { + ($value:expr, $expected:expr, $margin:expr) => { + assert!( + ($expected - $margin..=$expected + $margin).contains(&$value), + "value {} is out of range {}..={}", + $value, + $expected, + $margin + ); + }; +}