Skip to content

Commit

Permalink
Running tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dastansam committed Feb 11, 2024
1 parent 135ef5b commit 51a24f8
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 33 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ clap = { version = "4.4.2", features = ["derive"] }
futures = { version = "0.3.21", features = ["thread-pool"]}
lite-json = { git = "https://github.com/xlc/lite-json.git", branch ="master", default-features = false }
log = { version = "0.4.17", default-features = false }
hex = { version = "0.4", features = ["serde"], default-features = false }

# Runtime dependencies (wasm)
frame-benchmarking = { version = "4.0.0-dev", git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v1.0.0", default-features = false }
Expand Down
1 change: 1 addition & 0 deletions pallets/iso-8583/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ sp-runtime = { workspace = true, features = ["std"] }
pallet-balances = { workspace = true, features = ["std", "insecure_zero_ed"] }
pallet-timestamp = { workspace = true, features = ["std"] }
sp-keystore = { workspace = true, features = ["std"] }
hex = { version = "0.4", features = ["serde"] }

[features]
default = ["std"]
Expand Down
16 changes: 10 additions & 6 deletions pallets/iso-8583/src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ use frame_support::{
traits::tokens::{currency::Currency, ExistenceRequirement},
};

use sp_core::crypto::Ss58Codec;
use sp_runtime::{traits::TryConvert, AccountId32, SaturatedConversion};
use sp_runtime::{traits::TryConvert, SaturatedConversion};
use sp_std::vec::Vec;

impl<T: Config> ERC20R<AccountIdOf<T>, BalanceOf<T>> for Pallet<T> {
fn transfer(from: &AccountIdOf<T>, to: &AccountIdOf<T>, value: BalanceOf<T>) -> DispatchResult {
Expand Down Expand Up @@ -83,10 +83,14 @@ pub(crate) struct AccountIdDecoder<T: Config>(sp_std::marker::PhantomData<T>);

impl<T: Config> TryConvert<&JsonValue, AccountIdOf<T>> for AccountIdDecoder<T> {
fn try_convert(json: &JsonValue) -> Result<AccountIdOf<T>, &JsonValue> {
let raw_bytes =
json.clone().to_string().map(|v| v.iter().collect::<Vec<char>>()).ok_or(json)?;
let raw_bytes = json
.clone()
.to_string()
.ok_or(json)?
.into_iter()
.map(|c| c as u8)
.collect::<Vec<_>>();

let account_id_32 = AccountId32::from_ss58check(&raw_bytes).map_err(|_| json)?;
AccountIdOf::<T>::decode(&mut &account_id_32.encode()[..]).map_err(|_| json)
AccountIdOf::<T>::decode(&mut &raw_bytes[..]).map_err(|_| json)
}
}
20 changes: 16 additions & 4 deletions pallets/iso-8583/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use sp_runtime::{
KeyTypeId, Saturating,
};

use frame_support::{storage::unhashed, weights::WeightToFee};
use frame_support::weights::WeightToFee;
use frame_system::{offchain::CreateSignedTransaction, pallet_prelude::*};
use lite_json::{parse_json, JsonValue, Serialize};
use sp_std::vec;
Expand Down Expand Up @@ -401,9 +401,18 @@ pub mod pallet {

let mut accounts = Vec::new();
while let Some(next) = sp_io::storage::next_key(&previous_key) {
// Ensure we are iterating through the correct storage prefix
if !next.starts_with(&prefix) {
break;
}

previous_key = next;
count += 1;
if let Some(account) = unhashed::get::<AccountIdOf<T>>(&previous_key) {

// decode from last 32 bytes of the key
if let Ok(account) =
AccountIdOf::<T>::decode(&mut &previous_key[previous_key.len() - 32..])
{
accounts.push(account);
}

Expand Down Expand Up @@ -563,8 +572,11 @@ impl<T: Config> Pallet<T> {
let body = JsonValue::Array(
accounts
.iter()
.map(|account| JsonValue::String(account.to_string().chars().into_iter().collect()))
.collect(),
.map(|account| {
JsonValue::String(account.encode().into_iter().map(|x| x as char).collect())
})
.collect::<Vec<_>>()
.into(),
)
.serialize();

Expand Down
2 changes: 1 addition & 1 deletion pallets/iso-8583/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ parameter_types! {
}

pub(crate) type Extrinsic = TestXt<RuntimeCall, ()>;
type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;
pub(crate) type AccountId = <<Signature as Verify>::Signer as IdentifyAccount>::AccountId;

impl frame_system::offchain::SigningTypes for Test {
type Public = <Signature as Verify>::Signer;
Expand Down
159 changes: 137 additions & 22 deletions pallets/iso-8583/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,14 +390,67 @@ mod trait_tests {

mod offchain_worker {
use super::*;
use crate::{AccountsOf, Config};
use crate::{Accounts, AccountsOf, Config};
use codec::Decode;
use frame_support::traits::{Get, OffchainWorker};
use frame_system::pallet_prelude::BlockNumberFor;
use lite_json::{JsonValue, NumberValue, Serialize};
use sp_core::offchain::{testing, OffchainWorkerExt, TransactionPoolExt};
use sp_keystore::{testing::MemoryKeystore, Keystore, KeystoreExt};
use sp_runtime::RuntimeAppPublic;

/// Indent the body of the request.
///
/// simply adds opening and closing brackets and newlines to the body.
fn mock_request(accounts: Vec<u8>) -> Vec<u8> {
let mut full = Vec::new();

full.push('[' as u8);

for index in accounts {
full.push('"' as u8);
full.extend_from_slice(&account(index).encode()[..]);
full.push('"' as u8);
full.push(',' as u8);
}

// remove the last comma
full.pop();
full.push(']' as u8);

full
}

fn mock_response(accounts: Vec<(u8, f64)>) -> Vec<u8> {
JsonValue::Array(
accounts
.iter()
.map(|(id, balance)| {
JsonValue::Object(vec![
(
"account_id".to_string().chars().into_iter().collect(),
JsonValue::String(
account(*id).encode().into_iter().map(|v| v as char).collect(),
),
),
(
"balance".to_string().chars().into_iter().collect(),
JsonValue::Number(NumberValue {
integer: balance.trunc() as u64,
fraction: (balance.fract() * 100.0).ceil() as u64,
fraction_length: 2,
exponent: 0,
negative: false,
}),
),
])
})
.collect::<Vec<_>>()
.into(),
)
.serialize()
}

#[test]
fn fetch_balances_works() {
let (offchain, state) = testing::TestOffchainExt::new();
Expand All @@ -415,25 +468,14 @@ mod offchain_worker {
let mut state = state.write();
assert_eq!(state.requests.len(), 0);

let account_a = format!("[{:?}]", account(123).to_string());

// Example response:
// ```json
// [
// {"account_id": "5GQ...","balance": 100.11},
// {"account_id": "5FQ...","balance": 200.22},
// ..
// ]
// ```
let response =
format!(r#"[{{"account_id": "{}","balance": 100.11 }}]"#, account(123).to_string());
let response = mock_response(vec![(123, 100.11)]);

// prepare expectation for the request
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:3001/balances".into(),
body: account_a.into(),
response: Some(response.into()),
body: mock_request(vec![123]),
response: Some(response),
sent: true,
headers: vec![
("Content-Type".to_string(), "application/json".to_string()),
Expand Down Expand Up @@ -470,16 +512,15 @@ mod offchain_worker {
{
let mut state = state.write();
assert_eq!(state.requests.len(), 0);
let account_a = format!("[{:?}]", account(123).to_string());
let response =
format!(r#"[{{"account_id": "{}","balance": 100.11 }}]"#, account(123).to_string());

let response = mock_response(vec![(123, 100.11), (125, 125.25)]);

// prepare expectation for the request
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:3001/balances".into(),
body: account_a.into(),
response: Some(response.into()),
body: mock_request(vec![123, 125]),
response: Some(response),
sent: true,
headers: vec![
("Content-Type".to_string(), "application/json".to_string()),
Expand All @@ -491,7 +532,8 @@ mod offchain_worker {

// we are not expecting any request
t.execute_with(|| {
ISO8583::fetch_and_submit_updated_balances(vec![account(123)], vec![]).unwrap();
ISO8583::fetch_and_submit_updated_balances(vec![account(123), account(125)], vec![])
.unwrap();

let tx = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
Expand All @@ -500,10 +542,83 @@ mod offchain_worker {
assert_eq!(
tx.call,
RuntimeCall::ISO8583(crate::Call::update_accounts {
updated_accounts: vec![(account(123), 10011)].try_into().unwrap(),
updated_accounts: vec![(account(123), 10011), (account(125), 12525)]
.try_into()
.unwrap(),
last_iterated_storage_key: Some(vec![].try_into().unwrap())
})
);
});
}

#[test]
fn offchain_worker_works() {
const PHRASE: &str =
"news slush supreme milk chapter athlete soap sausage put clutch what kitten";

let (offchain, state) = testing::TestOffchainExt::new();
let (pool, pool_state) = testing::TestTransactionPoolExt::new();
let keystore = MemoryKeystore::new();
keystore
.sr25519_generate_new(crate::crypto::Public::ID, Some(&format!("{}/iso8583", PHRASE)))
.unwrap();

let mut t = ExtBuilder::default().with_accounts(vec![123, 125]).build();

t.register_extension(OffchainWorkerExt::new(offchain));
t.register_extension(TransactionPoolExt::new(pool));
t.register_extension(KeystoreExt::new(keystore));

let interval: BlockNumberFor<Test> = <Test as Config>::OffchainWorkerInterval::get();

{
let mut state = state.write();
assert_eq!(state.requests.len(), 0);

let response = mock_response(vec![(125, 125.25), (123, 100.11)]);

// prepare expectation for the request
state.expect_request(testing::PendingRequest {
method: "POST".into(),
uri: "http://localhost:3001/balances".into(),
body: mock_request(vec![125, 123]),
response: Some(response),
sent: true,
headers: vec![
("Content-Type".to_string(), "application/json".to_string()),
("accept".to_string(), "*/*".to_string()),
],
..Default::default()
});
}

t.execute_with(|| {
ISO8583::offchain_worker(interval);

let tx = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
let tx = crate::mock::Extrinsic::decode(&mut &tx[..]).unwrap();
// assert_eq!(tx.signature.unwrap().0, account(123));
assert_eq!(
tx.call,
RuntimeCall::ISO8583(crate::Call::update_accounts {
updated_accounts: vec![(account(125), 12525), (account(123), 10011)]
.try_into()
.unwrap(),
last_iterated_storage_key: Some(
vec![
154, 237, 128, 107, 236, 245, 7, 140, 126, 24, 139, 0, 248, 2, 43, 16,
142, 231, 65, 138, 101, 49, 23, 61, 96, 209, 246, 168, 45, 143, 77, 81,
255, 208, 58, 29, 34, 171, 170, 249, 207, 18, 242, 36, 206, 63, 124,
149, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123, 123,
123, 123, 123, 123, 123
]
.try_into()
.unwrap()
)
})
);
})
}
}

0 comments on commit 51a24f8

Please sign in to comment.