Skip to content

Commit

Permalink
review fix: add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
shamardy committed Sep 19, 2024
1 parent 704305d commit 1a7c003
Show file tree
Hide file tree
Showing 7 changed files with 189 additions and 36 deletions.
2 changes: 2 additions & 0 deletions mm2src/mm2_core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,6 @@ impl DbNamespaceId {
let mut rng = thread_rng();
DbNamespaceId::Test(rng.gen())
}

pub fn for_test_with_id(id: u64) -> DbNamespaceId { DbNamespaceId::Test(id) }
}
6 changes: 6 additions & 0 deletions mm2src/mm2_core/src/mm_ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,12 @@ impl MmCtxBuilder {
self
}

#[cfg(target_arch = "wasm32")]
pub fn with_test_db_namespace_with_id(mut self, id: u64) -> Self {
self.db_namespace = DbNamespaceId::for_test_with_id(id);
self
}

pub fn into_mm_arc(self) -> MmArc {
// NB: We avoid recreating LogState
// in order not to interfere with the integration tests checking LogState drop on shutdown.
Expand Down
4 changes: 3 additions & 1 deletion mm2src/mm2_main/src/lp_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use common::HttpStatusCode;
use crypto::{decrypt_mnemonic, encrypt_mnemonic, generate_mnemonic, CryptoCtx, CryptoInitError, EncryptedData,
MnemonicError};
use http::StatusCode;
use itertools::Itertools;
use mm2_core::mm_ctx::MmArc;
use mm2_err_handle::prelude::*;
use serde::de::DeserializeOwned;
Expand Down Expand Up @@ -536,7 +537,8 @@ impl From<WalletsDBError> for GetWalletsError {

/// Retrieves all created wallets and the currently activated wallet.
pub async fn get_wallet_names_rpc(ctx: MmArc, _req: Json) -> MmResult<GetWalletNamesResponse, GetWalletsError> {
let wallets = read_all_wallet_names(&ctx).await?;
// We want to return wallet names in the same order for both native and wasm32 targets.
let wallets = read_all_wallet_names(&ctx).await?.into_iter().sorted().collect();
// Note: `ok_or` is used here on `Constructible<Option<String>>` to handle the case where the wallet name is not set.
// `wallet_name` can be `None` in the case of no-login mode.
let activated_wallet = ctx.wallet_name.ok_or(GetWalletsError::Internal(
Expand Down
67 changes: 49 additions & 18 deletions mm2src/mm2_main/src/wasm_tests.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
use crate::lp_init;
use common::executor::{spawn, Timer};
use common::executor::{spawn, spawn_abortable, spawn_local_abortable, AbortOnDropHandle, Timer};
use common::log::wasm_log::register_wasm_log;
use mm2_core::mm_ctx::MmArc;
use mm2_number::BigDecimal;
use mm2_rpc::data::legacy::OrderbookResponse;
use mm2_test_helpers::electrums::{doc_electrums, marty_electrums};
use mm2_test_helpers::for_tests::{check_recent_swaps, enable_electrum_json, enable_utxo_v2_electrum,
enable_z_coin_light, morty_conf, pirate_conf, rick_conf, start_swaps,
test_qrc20_history_impl, wait_for_swaps_finish_and_check_status, MarketMakerIt,
Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY,
enable_z_coin_light, get_wallet_names, morty_conf, pirate_conf, rick_conf,
start_swaps, test_qrc20_history_impl, wait_for_swaps_finish_and_check_status,
MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, ARRR, MORTY,
PIRATE_ELECTRUMS, PIRATE_LIGHTWALLETD_URLS, RICK};
use mm2_test_helpers::get_passphrase;
use mm2_test_helpers::structs::{Bip44Chain, EnableCoinBalance, HDAccountAddressId};
use serde_json::json;
use wasm_bindgen_test::wasm_bindgen_test;

const PIRATE_TEST_BALANCE_SEED: &str = "pirate test seed";
const STOP_TIMEOUT_MS: u64 = 1000;

/// Starts the WASM version of MM.
fn wasm_start(ctx: MmArc) {
Expand All @@ -26,13 +27,7 @@ fn wasm_start(ctx: MmArc) {

/// This function runs Alice and Bob nodes, activates coins, starts swaps,
/// and then immediately stops the nodes to check if `MmArc` is dropped in a short period.
async fn test_mm2_stops_impl(
pairs: &[(&'static str, &'static str)],
maker_price: f64,
taker_price: f64,
volume: f64,
stop_timeout_ms: u64,
) {
async fn test_mm2_stops_impl(pairs: &[(&'static str, &'static str)], maker_price: f64, taker_price: f64, volume: f64) {
let coins = json!([rick_conf(), morty_conf()]);

let bob_passphrase = get_passphrase!(".env.seed", "BOB_PASSPHRASE").unwrap();
Expand Down Expand Up @@ -69,20 +64,18 @@ async fn test_mm2_stops_impl(
start_swaps(&mut mm_bob, &mut mm_alice, pairs, maker_price, taker_price, volume).await;

mm_alice
.stop_and_wait_for_ctx_is_dropped(stop_timeout_ms)
.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS)
.await
.unwrap();
mm_bob.stop_and_wait_for_ctx_is_dropped(stop_timeout_ms).await.unwrap();
mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap();
}

#[wasm_bindgen_test]
async fn test_mm2_stops_immediately() {
const STOP_TIMEOUT_MS: u64 = 1000;

register_wasm_log();

let pairs: &[_] = &[("RICK", "MORTY")];
test_mm2_stops_impl(pairs, 1., 1., 0.0001, STOP_TIMEOUT_MS).await;
test_mm2_stops_impl(pairs, 1., 1., 0.0001).await;
}

#[wasm_bindgen_test]
Expand Down Expand Up @@ -147,8 +140,6 @@ async fn trade_base_rel_electrum(
assert_eq!(0, bob_orderbook.asks.len(), "{} {} asks must be empty", base, rel);
}

const STOP_TIMEOUT_MS: u64 = 1000;

mm_bob.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS).await.unwrap();
mm_alice
.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS)
Expand Down Expand Up @@ -266,3 +257,43 @@ async fn activate_z_coin_light() {
};
assert_eq!(balance.balance.spendable, BigDecimal::default());
}

#[wasm_bindgen_test]
async fn test_get_wallet_names() {
const DB_NAMESPACE_NUM: u64 = 1;

let coins = json!([]);

// Initialize the first wallet with a specific name
let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass");
let mm_wallet_1 =
MarketMakerIt::start_with_db(wallet_1.conf, wallet_1.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM)
.await
.unwrap();

// Retrieve and verify the wallet names for the first wallet
let get_wallet_names_1 = get_wallet_names(&mm_wallet_1).await;
assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]);
assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1");

// Stop the first wallet before starting the second one
mm_wallet_1
.stop_and_wait_for_ctx_is_dropped(STOP_TIMEOUT_MS)
.await
.unwrap();

// Initialize the second wallet with a different name
let wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass");
let mm_wallet_2 =
MarketMakerIt::start_with_db(wallet_2.conf, wallet_2.rpc_password, Some(wasm_start), DB_NAMESPACE_NUM)
.await
.unwrap();

// Retrieve and verify the wallet names for the second wallet
let get_wallet_names_2 = get_wallet_names(&mm_wallet_2).await;
assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]);
assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2");

// Stop the second wallet
mm_wallet_2.stop_and_wait_for_ctx_is_dropped(1000).await.unwrap();
}
53 changes: 44 additions & 9 deletions mm2src/mm2_main/tests/mm2_tests/mm2_tests_inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ use mm2_test_helpers::electrums::*;
use mm2_test_helpers::for_tests::wait_check_stats_swap_status;
use mm2_test_helpers::for_tests::{account_balance, btc_segwit_conf, btc_with_spv_conf, btc_with_sync_starting_header,
check_recent_swaps, enable_qrc20, enable_utxo_v2_electrum, eth_dev_conf,
find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id, mm_spat,
morty_conf, my_balance, rick_conf, sign_message, start_swaps, tbtc_conf,
tbtc_segwit_conf, tbtc_with_spv_conf, test_qrc20_history_impl, tqrc20_conf,
verify_message, wait_for_swaps_finish_and_check_status,
wait_till_history_has_records, MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf,
Mm2TestConfForSwap, RaiiDump, DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE,
ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES, ETH_SEPOLIA_SWAP_CONTRACT,
MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK, RICK_ELECTRUM_ADDRS,
TBTC_ELECTRUMS, T_BCH_ELECTRUMS};
find_metrics_in_json, from_env_file, get_new_address, get_shared_db_id,
get_wallet_names, mm_spat, morty_conf, my_balance, rick_conf, sign_message,
start_swaps, tbtc_conf, tbtc_segwit_conf, tbtc_with_spv_conf,
test_qrc20_history_impl, tqrc20_conf, verify_message,
wait_for_swaps_finish_and_check_status, wait_till_history_has_records,
MarketMakerIt, Mm2InitPrivKeyPolicy, Mm2TestConf, Mm2TestConfForSwap, RaiiDump,
DOC_ELECTRUM_ADDRS, ETH_MAINNET_NODE, ETH_MAINNET_SWAP_CONTRACT, ETH_SEPOLIA_NODES,
ETH_SEPOLIA_SWAP_CONTRACT, MARTY_ELECTRUM_ADDRS, MORTY, QRC20_ELECTRUMS, RICK,
RICK_ELECTRUM_ADDRS, TBTC_ELECTRUMS, T_BCH_ELECTRUMS};
use mm2_test_helpers::get_passphrase;
use mm2_test_helpers::structs::*;
use serde_json::{self as json, json, Value as Json};
Expand Down Expand Up @@ -5809,6 +5809,41 @@ fn test_get_shared_db_id() {
);
}

#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_get_wallet_names() {
let coins = json!([]);

// Initialize the first wallet with a specific name
let wallet_1 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_1", "pass");
let mm_wallet_1 = MarketMakerIt::start(wallet_1.conf, wallet_1.rpc_password, None).unwrap();

// Retrieve and verify the wallet names for the first wallet
let get_wallet_names_1 = block_on(get_wallet_names(&mm_wallet_1));
assert_eq!(get_wallet_names_1.wallet_names, vec!["wallet_1"]);
assert_eq!(get_wallet_names_1.activated_wallet.unwrap(), "wallet_1");

// Initialize the second wallet with a different name
let mut wallet_2 = Mm2TestConf::seednode_with_wallet_name(&coins, "wallet_2", "pass");

// Set the database directory for the second wallet to the same as the first wallet
wallet_2.conf["dbdir"] = mm_wallet_1.folder.join("DB").to_str().unwrap().into();

// Stop the first wallet before starting the second one
block_on(mm_wallet_1.stop()).unwrap();

// Start the second wallet
let mm_wallet_2 = MarketMakerIt::start(wallet_2.conf, wallet_2.rpc_password, None).unwrap();

// Retrieve and verify the wallet names for the second wallet
let get_wallet_names_2 = block_on(get_wallet_names(&mm_wallet_2));
assert_eq!(get_wallet_names_2.wallet_names, vec!["wallet_1", "wallet_2"]);
assert_eq!(get_wallet_names_2.activated_wallet.unwrap(), "wallet_2");

// Stop the second wallet
block_on(mm_wallet_2.stop()).unwrap();
}

#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_sign_raw_transaction_rick() {
Expand Down
86 changes: 78 additions & 8 deletions mm2src/mm2_test_helpers/src/for_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,21 @@ impl Mm2TestConf {
}
}

pub fn seednode_with_wallet_name(coins: &Json, wallet_name: &str, wallet_password: &str) -> Self {
Mm2TestConf {
conf: json!({
"gui": "nogui",
"netid": 9998,
"coins": coins,
"rpc_password": DEFAULT_RPC_PASSWORD,
"i_am_seed": true,
"wallet_name": wallet_name,
"wallet_password": wallet_password,
}),
rpc_password: DEFAULT_RPC_PASSWORD.into(),
}
}

pub fn light_node(passphrase: &str, coins: &Json, seednodes: &[&str]) -> Self {
Mm2TestConf {
conf: json!({
Expand Down Expand Up @@ -1357,17 +1372,49 @@ impl MarketMakerIt {
/// Start a new MarketMaker locally.
///
/// * `conf` - The command-line configuration passed to the MarketMaker.
/// * `userpass` - RPC API key.
/// * `local` - Function to start the MarketMaker locally.
/// * `envs` - The environment variables passed to the process.
/// The argument is ignored for nodes running in a browser.
#[cfg(target_arch = "wasm32")]
pub async fn start_with_envs(
conf: Json,
userpass: String,
local: Option<LocalStart>,
_envs: &[(&str, &str)],
) -> Result<MarketMakerIt, String> {
MarketMakerIt::start_market_maker(conf, userpass, local, None).await
}

/// Start a new MarketMaker locally with a specific database namespace.
///
/// * `conf` - The command-line configuration passed to the MarketMaker.
/// * `userpass` - RPC API key.
/// * `local` - Function to start the MarketMaker locally.
/// * `db_namespace_id` - The test database namespace identifier.
#[cfg(target_arch = "wasm32")]
pub async fn start_with_db(
conf: Json,
userpass: String,
local: Option<LocalStart>,
db_namespace_id: u64,
) -> Result<MarketMakerIt, String> {
MarketMakerIt::start_market_maker(conf, userpass, local, Some(db_namespace_id)).await
}

/// Common helper function to start the MarketMaker.
///
/// * `conf` - The command-line configuration passed to the MarketMaker.
/// Unique P2P in-memory port is injected as `p2p_in_memory_port` unless this field is already present.
/// * `userpass` - RPC API key. We should probably extract it automatically from the MM log.
/// * `local` - Function to start the MarketMaker locally. Required for nodes running in a browser.
/// * `envs` - The enviroment variables passed to the process.
/// The argument is ignore for nodes running in a browser.
/// * `db_namespace_id` - Optional test database namespace identifier.
#[cfg(target_arch = "wasm32")]
pub async fn start_with_envs(
async fn start_market_maker(
mut conf: Json,
userpass: String,
local: Option<LocalStart>,
_envs: &[(&str, &str)],
db_namespace_id: Option<u64>,
) -> Result<MarketMakerIt, String> {
if conf["p2p_in_memory"].is_null() {
conf["p2p_in_memory"] = Json::Bool(true);
Expand All @@ -1383,10 +1430,19 @@ impl MarketMakerIt {
conf["p2p_in_memory_port"] = Json::Number(new_p2p_port.into());
}

let ctx = mm2_core::mm_ctx::MmCtxBuilder::new()
.with_conf(conf.clone())
.with_test_db_namespace()
.into_mm_arc();
let ctx = {
let builder = MmCtxBuilder::new()
.with_conf(conf.clone());

let builder = if let Some(ns) = db_namespace_id {
builder.with_test_db_namespace_with_id(ns)
} else {
builder.with_test_db_namespace()
};

builder.into_mm_arc()
};

let local = try_s!(local.ok_or("!local"));
local(ctx.clone());

Expand Down Expand Up @@ -2844,6 +2900,20 @@ pub async fn get_shared_db_id(mm: &MarketMakerIt) -> GetSharedDbIdResult {
res.result
}

pub async fn get_wallet_names(mm: &MarketMakerIt) -> GetWalletNamesResult {
let request = mm
.rpc(&json!({
"userpass": mm.userpass,
"method": "get_wallet_names",
"mmrpc": "2.0",
}))
.await
.unwrap();
assert_eq!(request.0, StatusCode::OK, "'get_wallet_names' failed: {}", request.1);
let res: RpcSuccessResponse<_> = json::from_str(&request.1).unwrap();
res.result
}

pub async fn max_maker_vol(mm: &MarketMakerIt, coin: &str) -> RpcResponse {
let rc = mm
.rpc(&json!({
Expand Down
7 changes: 7 additions & 0 deletions mm2src/mm2_test_helpers/src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -827,6 +827,13 @@ pub struct GetSharedDbIdResult {
pub shared_db_id: String,
}

#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct GetWalletNamesResult {
pub wallet_names: Vec<String>,
pub activated_wallet: Option<String>,
}

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RpcV2Response<T> {
Expand Down

0 comments on commit 1a7c003

Please sign in to comment.