Testing Concurrent Pool Creation with PocketIC
Hey everyone,
I’ve been experimenting with concurrency testing using PocketIC and came across this documentation.
To test concurrency in my backend, I wrote a function that simulates multiple pool creation requests along with the necessary ICRC-2 approvals happening concurrently. Below is my test function:
#[test]
fn test_create_multiple_pools_concurrently_with_approval() {
let (pic, backend_canister, ckbtc_canister, cketh_canister, _ckusdc_canister) = setup();
// Principal for testing
let hardcoded_principal = Principal::from_text("xkd3g-llatk-lmuv7-eoudm-qtjnr-iapqh-taggr-pwpmo-3rojt-pxkwo-4qe").unwrap();
let mut pool_creation_msg_ids = Vec::new();
let mut approval_msg_ids = Vec::new();
let token_canisters = [ckbtc_canister, cketh_canister];
// Submit multiple concurrent approval and pool creation calls
for i in 0..4 {
let token_canister = token_canisters[i % 2]; // Alternate between ckbtc and cketh
// Approve operation for each pool creation
let approval_args = ApproveArgs {
fee: None,
memo: None,
from_subaccount: None,
created_at_time: None,
amount: Nat::from(10000000u64),
expected_allowance: None,
expires_at: None,
spender: Account {
owner: backend_canister,
subaccount: None,
},
};
let approval_encoded_args = candid::encode_args((approval_args,)).unwrap();
let approval_msg_id = pic.submit_call(
token_canister,
hardcoded_principal,
"icrc2_approve",
approval_encoded_args,
).unwrap();
approval_msg_ids.push(approval_msg_id);
// Create pool with varying parameters
let pool_data = Pool_Data {
pool_data: vec![
CreatePoolParams {
token_name: format!("Token{}", i),
balance: Nat::from(100000u64 * (i as u64 + 1)),
weight: Nat::from(10u64),
value: Nat::from(100u64 * (i as u64 + 1)),
ledger_canister_id: token_canister,
image: format!("image{}.png", i),
}
],
swap_fee: Nat::from(5u64),
};
let pool_encoded_args = candid::encode_args((pool_data,)).unwrap();
let pool_msg_id = pic.submit_call(
backend_canister,
hardcoded_principal,
"create_pools",
pool_encoded_args,
).unwrap();
pool_creation_msg_ids.push(pool_msg_id);
}
// Await all submitted approval calls and check results
for msg_id in approval_msg_ids {
let res = pic.await_call(msg_id).unwrap();
assert!(matches!(res, WasmResult::Reply(_)), "Approval should be successful to proceed with pool creation");
}
// Await all submitted pool creation calls and check results
for msg_id in pool_creation_msg_ids {
let res = pic.await_call(msg_id).unwrap();
match res {
WasmResult::Reply(data) => {
let result: Result<(), CustomError> = candid::decode_one(&data).unwrap();
assert!(result.is_ok(), "Expected successful pool creation");
},
WasmResult::Reject(message) => {
println!("Pool creation failed with message: {}", message);
assert!(false, "Pool creation should not fail");
},
}
}
}
What I’m Trying to Achieve
- Testing concurrent update calls using PocketIC.
- Submitting approval transactions (ICRC-2) before calling the
create_pools
function. - Ensuring multiple pool creations execute correctly without race conditions.
Question
- Does this test structure make sense for concurrency testing in PocketIC?
Would appreciate any feedback or suggestions from the community!