WIP: Integration tests for RPC server

This commit is contained in:
binarybaron 2023-09-24 19:47:35 +02:00
parent 3c39a34a6e
commit d3bb838806
9 changed files with 746 additions and 568 deletions

View file

@ -123,7 +123,7 @@ impl SwapLock {
// workaround for warning over monero_rpc_process which we must own but not read
#[allow(dead_code)]
pub struct Context {
pub db: Arc<dyn Database + Send + Sync>,
db: Arc<dyn Database + Send + Sync>,
bitcoin_wallet: Option<Arc<bitcoin::Wallet>>,
monero_wallet: Option<Arc<monero::Wallet>>,
monero_rpc_process: Option<monero::WalletRpcProcess>,
@ -205,6 +205,25 @@ impl Context {
Ok(context)
}
pub async fn for_harness(
seed: Seed,
env_config: EnvConfig,
db_path: PathBuf,
bob_bitcoin_wallet: Arc<bitcoin::Wallet>,
bob_monero_wallet: Arc<monero::Wallet>,
) -> Self {
let config = Config::for_harness(seed, env_config);
Self {
bitcoin_wallet: Some(bob_bitcoin_wallet),
monero_wallet: Some(bob_monero_wallet),
config,
db: open_db(db_path).await.unwrap(),
monero_rpc_process: None,
swap_lock: Arc::new(SwapLock::new()),
}
}
}
impl fmt::Debug for Context {
@ -288,6 +307,25 @@ fn env_config_from(testnet: bool) -> EnvConfig {
Mainnet::get_config()
}
}
impl Config {
pub fn for_harness(seed: Seed, env_config: EnvConfig) -> Self {
let data_dir = data::data_dir_from(None, false).unwrap();
Self {
tor_socks5_port: None,
namespace: XmrBtcNamespace::from_is_testnet(false),
server_address: None,
env_config,
seed: Some(seed),
debug: false,
json: false,
is_testnet: false,
data_dir,
}
}
}
#[cfg(test)]
pub mod api_test {
use super::*;
@ -350,38 +388,30 @@ pub mod api_test {
}
};
Request::new(
Method::BuyXmr {
seller,
bitcoin_change_address,
monero_receive_address,
swap_id: Uuid::new_v4(),
},
)
Request::new(Method::BuyXmr {
seller,
bitcoin_change_address,
monero_receive_address,
swap_id: Uuid::new_v4(),
})
}
pub fn resume() -> Request {
Request::new(
Method::Resume {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
},
)
Request::new(Method::Resume {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
pub fn cancel() -> Request {
Request::new(
Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
},
)
Request::new(Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
pub fn refund() -> Request {
Request::new(
Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
},
)
Request::new(Method::CancelAndRefund {
swap_id: Uuid::from_str(SWAP_ID).unwrap(),
})
}
}
}

View file

@ -193,6 +193,9 @@ impl Request {
.as_ref()
.context("Could not get Bitcoin wallet")?;
let state = context.db.get_state(swap_id).await?;
let is_completed = state.swap_finished();
let peerId = context
.db
.get_peer_id(swap_id)
@ -205,9 +208,6 @@ impl Request {
.await
.with_context(|| "Could not get addressess")?;
let state = context.db.get_state(swap_id).await?;
let is_completed = state.swap_finished();
let start_date = context.db.get_swap_start_date(swap_id).await?;
let swap_state: BobState = state.try_into()?;
@ -346,7 +346,7 @@ impl Request {
let (event_loop, mut event_loop_handle) =
EventLoop::new(swap_id, swarm, seller_peer_id)?;
let event_loop = tokio::spawn(event_loop.run().instrument(Span::current()));
let event_loop = tokio::spawn(event_loop.run().instrument(Span::none()));
let max_givable = || bitcoin_wallet.max_giveable(TxLock::script_size());
let estimate_fee = |amount| bitcoin_wallet.estimate_fee(TxLock::weight(), amount);
@ -429,7 +429,7 @@ impl Request {
.release_swap_lock()
.await
.expect("Could not release swap lock");
}.instrument(Span::current()));
}.instrument(Span::none()));
Ok(json!({
"swapId": swap_id.to_string(),
@ -483,7 +483,7 @@ impl Request {
let (event_loop, event_loop_handle) =
EventLoop::new(swap_id, swarm, seller_peer_id)?;
let handle = tokio::spawn(event_loop.run().instrument(Span::current()));
let handle = tokio::spawn(event_loop.run().instrument(Span::none()));
let monero_receive_address = context.db.get_monero_address(swap_id).await?;
let swap = Swap::from_db(
@ -529,7 +529,7 @@ impl Request {
.release_swap_lock()
.await
.expect("Could not release swap lock");
}.instrument(Span::current()));
}.instrument(Span::none()));
Ok(json!({
"result": "ok",
}))
@ -574,7 +574,7 @@ impl Request {
Method::GetRawStates => {
let raw_history = context.db.raw_all().await?;
Ok(json!({ "raw_history": raw_history }))
Ok(json!({ "raw_states": raw_history }))
}
Method::Config => {
let data_dir_display = context.config.data_dir.display();

View file

@ -75,10 +75,10 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC
INFO swap: Deposit at least 0.00001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC
INFO swap: Received Bitcoin new_balance=0.00100000 BTC max_giveable=0.00090000 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Deposit at least 0.00001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001 BTC max_giveable=0 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.001 BTC max_giveable=0.0009 BTC
"
);
}
@ -112,10 +112,10 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC
INFO swap: Deposit at least 0.00001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC
INFO swap: Received Bitcoin new_balance=0.10010000 BTC max_giveable=0.10000000 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Deposit at least 0.00001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00001 BTC max_giveable=0 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.1001 BTC max_giveable=0.1 BTC
"
);
}
@ -149,7 +149,7 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n"
" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC\n"
);
}
@ -182,7 +182,7 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.00000000 BTC maximum_amount=0.01000000 BTC\n"
" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0 BTC maximum_amount=0.01 BTC\n"
);
}
@ -215,10 +215,10 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Deposit at least 0.01001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.01001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.01001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.01001 BTC max_giveable=0 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
"
);
}
@ -252,10 +252,10 @@ mod tests {
assert_eq!((amount, fees), (expected_amount, expected_fees));
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Deposit at least 0.00991000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00991000 BTC max_giveable=0.00010000 BTC minimum_amount=0.01000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.00991 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.00991 BTC max_giveable=0.0001 BTC minimum_amount=0.01 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
"
);
}
@ -292,13 +292,13 @@ mod tests {
assert!(matches!(error, tokio::time::error::Elapsed { .. }));
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Deposit at least 0.10001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Received Bitcoin new_balance=0.01010000 BTC max_giveable=0.01000000 BTC
INFO swap: Deposited amount is less than `min_quantity`
INFO swap: Deposit at least 0.09001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.09001000 BTC max_giveable=0.01000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.10001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001 BTC max_giveable=0 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.0101 BTC max_giveable=0.01 BTC
INFO swap::api::request: Deposited amount is less than `min_quantity`
INFO swap::api::request: Deposit at least 0.09001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.09001 BTC max_giveable=0.01 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
"
);
}
@ -341,10 +341,10 @@ mod tests {
assert_eq!(
writer.captured(),
r" INFO swap: Received quote price=0.00100000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Deposit at least 0.10001000 BTC to cover the min quantity with fee!
INFO swap: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001000 BTC max_giveable=0.00000000 BTC minimum_amount=0.10000000 BTC maximum_amount=184467440737.09551615 BTC
INFO swap: Received Bitcoin new_balance=0.21000000 BTC max_giveable=0.20000000 BTC
r" INFO swap::api::request: Received quote price=0.001 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Deposit at least 0.10001 BTC to cover the min quantity with fee!
INFO swap::api::request: Waiting for Bitcoin deposit deposit_address=1PdfytjS7C8wwd9Lq5o4x9aXA2YRqaCpH6 min_deposit=0.10001 BTC max_giveable=0 BTC minimum_amount=0.1 BTC maximum_amount=184467440737.09551615 BTC
INFO swap::api::request: Received Bitcoin new_balance=0.21 BTC max_giveable=0.2 BTC
"
);
}

View file

@ -370,8 +370,8 @@ mod tests {
use crate::env::{GetConfig, Regtest};
use crate::protocol::{alice, bob};
use rand::rngs::OsRng;
use uuid::Uuid;
use std::matches;
use uuid::Uuid;
#[test]
fn lock_confirmations_le_to_cancel_timelock_no_timelock_expired() {
@ -400,7 +400,7 @@ mod tests {
tx_cancel_status,
);
assert!(matches!(expired_timelock, ExpiredTimelocks::Cancel {..}));
assert!(matches!(expired_timelock, ExpiredTimelocks::Cancel { .. }));
}
#[test]

View file

@ -194,6 +194,7 @@ impl Wallet {
tokio::time::sleep(Duration::from_secs(5)).await;
}
}.instrument(Span::current()));
}.instrument(Span::none()));
Subscription {
receiver,

View file

@ -499,16 +499,20 @@ mod tests {
use crate::api::api_test::*;
use crate::api::Config;
use crate::fs::system_data_dir;
use crate::monero::monero_address::MoneroAddressNetworkMismatch;
use serial_test::serial;
const BINARY_NAME: &str = "swap";
const ARGS_DATA_DIR: &str = "/tmp/dir/";
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_mainnet_then_defaults_to_mainnet() {
// this test is very long, however it just checks that various CLI arguments sets the
// internal Context and Request properly. It is unlikely to fail and splitting it in various
// tests would require to run the tests sequantially which is very slow (due to the context
// need to access files like the Bitcoin wallet).
async fn test_cli_arguments() {
// given_buy_xmr_on_mainnet_then_defaults_to_mainnet
let raw_ars = vec![
BINARY_NAME,
"buy-xmr",
@ -523,23 +527,31 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, false, false);
let (expected_config, expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
let (actual_config, actual_request) = match args {
ParseResult::Context(context, request) => (context.config.clone(), request),
_ => panic!("Couldn't parse result"),
};
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
// since Uuid is random, copy before comparing requests
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_testnet_then_defaults_to_testnet() {
// given_buy_xmr_on_testnet_then_defaults_to_testnet
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -555,7 +567,7 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (true, false, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
@ -565,13 +577,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_mainnet_with_testnet_address_then_fails() {
// given_buy_xmr_on_mainnet_with_testnet_address_then_fails
let raw_ars = vec![
BINARY_NAME,
"buy-xmr",
@ -592,11 +611,8 @@ mod tests {
actual: monero::Network::Stagenet
}
);
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_testnet_with_mainnet_address_then_fails() {
// given_buy_xmr_on_testnet_with_mainnet_address_then_fails
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -618,11 +634,8 @@ mod tests {
actual: monero::Network::Mainnet
}
);
}
#[tokio::test]
#[serial]
async fn given_resume_on_mainnet_then_defaults_to_mainnet() {
// given_resume_on_mainnet_then_defaults_to_mainnet
let raw_ars = vec![BINARY_NAME, "resume", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -640,11 +653,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_testnet_then_defaults_to_testnet() {
// given_resume_on_testnet_then_defaults_to_testnet
let raw_ars = vec![BINARY_NAME, "--testnet", "resume", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -662,11 +672,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_cancel_on_mainnet_then_defaults_to_mainnet() {
// given_cancel_on_mainnet_then_defaults_to_mainnet
let raw_ars = vec![BINARY_NAME, "cancel", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -685,11 +692,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_cancel_on_testnet_then_defaults_to_testnet() {
// given_cancel_on_testnet_then_defaults_to_testnet
let raw_ars = vec![BINARY_NAME, "--testnet", "cancel", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -707,11 +711,7 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_refund_on_mainnet_then_defaults_to_mainnet() {
let raw_ars = vec![BINARY_NAME, "refund", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -729,11 +729,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_refund_on_testnet_then_defaults_to_testnet() {
// given_refund_on_testnet_then_defaults_to_testnet
let raw_ars = vec![BINARY_NAME, "--testnet", "refund", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
@ -751,11 +748,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_mainnet_with_data_dir_then_data_dir_set() {
// given_buy_xmr_on_mainnet_with_data_dir_then_data_dir_set
let raw_ars = vec![
BINARY_NAME,
"--data-base-dir",
@ -773,7 +767,7 @@ mod tests {
let (is_testnet, debug, json) = (false, false, false);
let data_dir = PathBuf::from_str(ARGS_DATA_DIR).unwrap();
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
Request::buy_xmr(is_testnet),
);
@ -783,13 +777,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_testnet_with_data_dir_then_data_dir_set() {
// given_buy_xmr_on_testnet_with_data_dir_then_data_dir_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -808,7 +809,7 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (true, false, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
Request::buy_xmr(is_testnet),
);
@ -818,13 +819,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_mainnet_with_data_dir_then_data_dir_set() {
// given_resume_on_mainnet_with_data_dir_then_data_dir_set
let raw_ars = vec![
BINARY_NAME,
"--data-base-dir",
@ -838,11 +846,21 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, false, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, Some(data_dir.clone()), debug, json),
Request::resume(),
);
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
let (actual_config, actual_request) = match args {
ParseResult::Context(context, request) => (context.config.clone(), request),
_ => panic!("Couldn't parse result"),
@ -850,11 +868,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_testnet_with_data_dir_then_data_dir_set() {
// given_resume_on_testnet_with_data_dir_then_data_dir_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -881,11 +896,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_mainnet_with_debug_then_debug_set() {
// given_buy_xmr_on_mainnet_with_debug_then_debug_set
let raw_ars = vec![
BINARY_NAME,
"--debug",
@ -901,7 +913,7 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, true, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
@ -911,13 +923,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_testnet_with_debug_then_debug_set() {
// given_buy_xmr_on_testnet_with_debug_then_debug_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -934,7 +953,7 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (true, true, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
@ -944,19 +963,26 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_mainnet_with_debug_then_debug_set() {
// given_resume_on_mainnet_with_debug_then_debug_set
let raw_ars = vec![BINARY_NAME, "--debug", "resume", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, true, false);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::resume(),
);
@ -966,13 +992,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_testnet_with_debug_then_debug_set() {
// given_resume_on_testnet_with_debug_then_debug_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -997,11 +1030,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_mainnet_with_json_then_json_set() {
// given_buy_xmr_on_mainnet_with_json_then_json_set
let raw_ars = vec![
BINARY_NAME,
"--json",
@ -1016,9 +1046,8 @@ mod tests {
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, false, true);
let data_dir = data_dir_path_cli(is_testnet);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
@ -1028,13 +1057,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_buy_xmr_on_testnet_with_json_then_json_set() {
// given_buy_xmr_on_testnet_with_json_then_json_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -1050,7 +1086,7 @@ mod tests {
let (is_testnet, debug, json) = (true, false, true);
let (expected_config, expected_request) = (
let (expected_config, mut expected_request) = (
Config::default(is_testnet, None, debug, json),
Request::buy_xmr(is_testnet),
);
@ -1061,13 +1097,20 @@ mod tests {
_ => panic!("Couldn't parse result"),
};
if let Method::BuyXmr {
ref mut swap_id, ..
} = expected_request.cmd
{
*swap_id = match actual_request.cmd {
Method::BuyXmr { swap_id, .. } => swap_id,
_ => panic!("Not the Method we expected"),
}
};
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_mainnet_with_json_then_json_set() {
// given_resume_on_mainnet_with_json_then_json_set
let raw_ars = vec![BINARY_NAME, "--json", "resume", "--swap-id", SWAP_ID];
let args = parse_args_and_apply_defaults(raw_ars).await.unwrap();
let (is_testnet, debug, json) = (false, false, true);
@ -1084,11 +1127,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn given_resume_on_testnet_with_json_then_json_set() {
// given_resume_on_testnet_with_json_then_json_set
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -1113,11 +1153,8 @@ mod tests {
assert_eq!(actual_config, expected_config);
assert_eq!(actual_request, Box::new(expected_request));
}
#[tokio::test]
#[serial]
async fn only_bech32_addresses_mainnet_are_allowed() {
// only_bech32_addresses_mainnet_are_allowed
let raw_ars = vec![
BINARY_NAME,
"buy-xmr",
@ -1128,7 +1165,7 @@ mod tests {
"--seller",
MULTI_ADDRESS,
];
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
let raw_ars = vec![
BINARY_NAME,
@ -1140,7 +1177,7 @@ mod tests {
"--seller",
MULTI_ADDRESS,
];
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
let raw_ars = vec![
BINARY_NAME,
@ -1154,11 +1191,8 @@ mod tests {
];
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap();
assert!(matches!(result, ParseResult::Context(_, _)));
}
#[tokio::test]
#[serial]
async fn only_bech32_addresses_testnet_are_allowed() {
// only_bech32_addresses_testnet_are_allowed
let raw_ars = vec![
BINARY_NAME,
"--testnet",
@ -1170,7 +1204,7 @@ mod tests {
"--seller",
MULTI_ADDRESS,
];
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
let raw_ars = vec![
BINARY_NAME,
@ -1183,7 +1217,7 @@ mod tests {
"--seller",
MULTI_ADDRESS,
];
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
parse_args_and_apply_defaults(raw_ars).await.unwrap_err();
let raw_ars = vec![
BINARY_NAME,
@ -1199,12 +1233,4 @@ mod tests {
let result = parse_args_and_apply_defaults(raw_ars).await.unwrap();
assert!(matches!(result, ParseResult::Context(_, _)));
}
fn data_dir_path_cli(is_testnet: bool) -> PathBuf {
if is_testnet {
system_data_dir().unwrap().join("cli").join("testnet")
} else {
system_data_dir().unwrap().join("cli").join("mainnet")
}
}
}

View file

@ -219,11 +219,13 @@ async fn execute_request(
cmd: Method,
context: &Arc<Context>,
) -> Result<serde_json::Value, jsonrpsee_core::Error> {
let params_parsed = params
// If we fail to parse the params as a String HashMap, it's most likely because its an empty object
// In that case, we want to make sure not to fail the request, so we set the log_reference_id to None
// and swallow the error
let reference_id = params
.parse::<HashMap<String, String>>()
.map_err(|err| jsonrpsee_core::Error::Custom(err.to_string()))?;
let reference_id = params_parsed.get("log_reference_id");
.ok()
.and_then(|params_parsed| params_parsed.get("log_reference_id").cloned());
let request = Request::with_id(cmd, reference_id.map(|s| s.clone()));
request

View file

@ -23,9 +23,9 @@ use swap::network::rendezvous::XmrBtcNamespace;
use swap::network::swarm;
use swap::protocol::alice::{AliceState, Swap};
use swap::protocol::bob::BobState;
use swap::protocol::{alice, bob};
use swap::protocol::{alice, bob, Database};
use swap::seed::Seed;
use swap::{asb, bitcoin, cli, env, monero};
use swap::{api, asb, bitcoin, cli, env, monero};
use tempfile::{tempdir, NamedTempFile};
use testcontainers::clients::Cli;
use testcontainers::{Container, Docker, RunArgs};
@ -454,6 +454,8 @@ impl BobParams {
}
let db = Arc::new(SqliteDatabase::open(&self.db_path).await?);
db.insert_peer_id(swap_id, self.alice_peer_id).await?;
let swap = bob::Swap::new(
db,
swap_id,
@ -534,6 +536,17 @@ pub struct TestContext {
}
impl TestContext {
pub async fn get_bob_context(self) -> api::Context {
api::Context::for_harness(
self.bob_params.seed,
self.env_config,
self.bob_params.db_path,
self.bob_bitcoin_wallet,
self.bob_monero_wallet,
)
.await
}
pub async fn restart_alice(&mut self) {
self.alice_handle.abort();

View file

@ -1,19 +1,33 @@
pub mod harness;
use anyhow::Result;
use jsonrpsee::rpc_params;
use jsonrpsee::ws_client::WsClientBuilder;
use jsonrpsee_core::client::ClientT;
use jsonrpsee_core::client::{Client, ClientT};
use jsonrpsee_core::params::ObjectParams;
use serial_test::serial;
use serde_json::{json, Value};
use std::collections::HashMap;
use std::future::Future;
use std::net::SocketAddr;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::Duration;
use swap::api::request::{Method, Request };
use swap::api::Context;
use swap::api::request::{Method, Request};
use swap::api::{Config, Context, SwapLock};
use swap::cli::command::{Bitcoin, Monero};
use tempfile::tempdir;
use tracing_subscriber::filter::LevelFilter;
use crate::harness::alice_run_until::is_xmr_lock_transaction_sent;
use crate::harness::bob_run_until::is_btc_locked;
use crate::harness::{setup_test, SlowCancelConfig, TestContext};
use swap::asb::FixedRate;
use swap::database::open_db;
use swap::network::rendezvous::XmrBtcNamespace;
use swap::protocol::{alice, bob};
use swap::tracing_ext::{capture_logs, MakeCapturingWriter};
use uuid::Uuid;
#[cfg(test)]
@ -27,419 +41,511 @@ const MONERO_ADDR: &str = "53gEuGZUhP9JMEBZoGaFNzhwEgiG7hwQdMCqFxiyiTeFPmkbt1mAo
const SELLER: &str =
"/ip4/127.0.0.1/tcp/9939/p2p/12D3KooWCdMKjesXMJz1SiZ7HgotrxuqhQJbP5sgBm2BwP1cqThi";
const SWAP_ID: &str = "ea030832-3be9-454f-bb98-5ea9a788406b";
const SERVER_START_TIMEOUT_SECS: u64 = 50;
pub async fn initialize_context() -> (Arc<Context>, Request) {
let (is_testnet, debug, json) = (true, false, false);
// let data_dir = data::data_dir_from(None, is_testnet).unwrap();
let server_address = None;
pub async fn setup_daemon<T, F>(testfn: T)
where
T: Fn(TestContext, Client, MakeCapturingWriter, Arc<Context>) -> F,
F: Future<Output = Result<()>>,
{
setup_test(SlowCancelConfig, |harness_context| async move {
let writer = capture_logs(LevelFilter::DEBUG);
let server_address: SocketAddr = SERVER_ADDRESS.parse().unwrap();
let bitcoin = Bitcoin {
bitcoin_electrum_rpc_url: None,
bitcoin_target_block: None,
};
let request = Request::new(Method::StartDaemon {
server_address: Some(server_address),
});
let _monero = Monero {
monero_daemon_address: None,
};
let context = Arc::new(harness_context.get_bob_context().await);
let request = Request::new(Method::StartDaemon { server_address: None });
let context_clone = context.clone();
let context = Context::build(
Some(bitcoin),
None,
None,
None,
is_testnet,
debug,
json,
server_address,
)
tokio::spawn(async move {
if let Err(err) = request.call(context_clone).await {
println!("Failed to initialize daemon for testing: {}", err);
}
});
for _ in 0..SERVER_START_TIMEOUT_SECS {
if writer.captured().contains("Started RPC server") {
let url = format!("ws://{}", SERVER_ADDRESS);
let client = WsClientBuilder::default().build(&url).await.unwrap();
return testfn(harness_context, client, writer, context).await;
}
tokio::time::sleep(Duration::from_secs(1)).await;
}
panic!(
"Failed to start RPC server after {} seconds",
SERVER_START_TIMEOUT_SECS
);
})
.await
.unwrap();
}
(Arc::new(context), request)
fn assert_has_keys_serde(map: &serde_json::Map<String, Value>, keys: &[&str]) {
for &key in keys {
assert!(map.contains_key(key), "Key {} is missing", key);
}
}
// Helper function for HashMap
fn assert_has_keys_hashmap(map: &HashMap<String, Value>, keys: &[&str]) {
for &key in keys {
assert!(map.contains_key(key), "Key {} is missing", key);
}
}
#[tokio::test]
#[serial]
pub async fn can_start_server() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
tokio::time::sleep(Duration::from_secs(3)).await;
assert!(true);
setup_daemon(|_, _, _, _| async move {
assert!(true);
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn get_bitcoin_balance() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
setup_daemon(|_, client| async move {
let response: HashMap<String, i32> = client
.request("get_bitcoin_balance", ObjectParams::new())
.await
.unwrap();
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
assert_eq!(response, HashMap::from([("balance".to_string(), 10000000)]));
let client = WsClientBuilder::default().build(&url).await.unwrap();
let response: HashMap<String, i32> = client
.request("get_bitcoin_balance", rpc_params!["id"])
.await
.unwrap();
Ok(())
})
.await;
}
assert_eq!(response, HashMap::from([("balance".to_string(), 0)]));
#[tokio::test]
#[serial]
pub async fn get_bitcoin_balance_with_log_reference_id() {
setup_daemon(|_, client, writer| async move {
let mut params = ObjectParams::new();
params.insert("log_reference_id", "test_ref_id").unwrap();
let _: HashMap<String, i32> = client.request("get_bitcoin_balance", params).await.unwrap();
assert!(writer.captured().contains(
"log_reference_id=\"test_ref_id\"}: swap::api::request: Checked Bitcoin balance balance=0"
));
Ok(())
}).await;
}
#[tokio::test]
#[serial]
pub async fn get_history() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
setup_daemon(|mut harness_ctx, client| async move {
// Start a swap and wait for xmr lock transaction to be published (XmrLockTransactionSent)
let (bob_swap, _) = harness_ctx.bob_swap().await;
let bob_swap_id = bob_swap.id;
tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
let alice_swap = harness_ctx.alice_next_swap().await;
alice::run_until(
alice_swap,
is_xmr_lock_transaction_sent,
FixedRate::default(),
)
.await?;
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let response: HashMap<String, Vec<(Uuid, String)>> = client
.request("get_history", ObjectParams::new())
.await
.unwrap();
let swaps: Vec<(Uuid, String)> = vec![(bob_swap_id, "btc is locked".to_string())];
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
assert_eq!(response, HashMap::from([("swaps".to_string(), swaps)]));
let response: HashMap<String, Vec<(Uuid, String)>> =
client.request("get_history", params).await.unwrap();
let swaps: Vec<(Uuid, String)> = Vec::new();
assert_eq!(response, HashMap::from([("swaps".to_string(), swaps)]));
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn get_raw_history() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
pub async fn get_raw_states() {
setup_daemon(|mut harness_ctx, client| async move {
// Start a swap and wait for xmr lock transaction to be published (XmrLockTransactionSent)
let (bob_swap, _) = harness_ctx.bob_swap().await;
let bob_swap_id = bob_swap.id;
tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
let alice_swap = harness_ctx.alice_next_swap().await;
alice::run_until(
alice_swap,
is_xmr_lock_transaction_sent,
FixedRate::default(),
)
.await?;
let response: HashMap<String, HashMap<Uuid, Vec<Value>>> = client
.request("get_raw_states", ObjectParams::new())
.await
.unwrap();
let response_raw_states = response.get("raw_states").unwrap();
assert!(response_raw_states.contains_key(&bob_swap_id));
assert_eq!(response_raw_states.get(&bob_swap_id).unwrap().len(), 2);
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn get_swap_info_valid_swap_id() {
setup_daemon(|mut harness_ctx, client| async move {
// Start a swap and wait for xmr lock transaction to be published (XmrLockTransactionSent)
let (bob_swap, _) = harness_ctx.bob_swap().await;
let bob_swap_id = bob_swap.id;
tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
let alice_swap = harness_ctx.alice_next_swap().await;
alice::run_until(
alice_swap,
is_xmr_lock_transaction_sent,
FixedRate::default(),
)
.await?;
let mut params = ObjectParams::new();
params.insert("swap_id", bob_swap_id).unwrap();
let response: HashMap<String, Value> =
client.request("get_swap_info", params).await.unwrap();
// Check primary keys in response
assert_has_keys_hashmap(
&response,
&[
"txRefundFee",
"swapId",
"cancelTimelock",
"timelock",
"punishTimelock",
"stateName",
"btcAmount",
"startDate",
"btcRefundAddress",
"txCancelFee",
"xmrAmount",
"completed",
"txLockId",
"seller",
],
);
// Assert specific fields
assert_eq!(response.get("swapId").unwrap(), &bob_swap_id.to_string());
assert_eq!(
response.get("stateName").unwrap(),
&"btc is locked".to_string()
);
assert_eq!(response.get("completed").unwrap(), &Value::Bool(false));
// Check seller object and its keys
let seller = response
.get("seller")
.expect("Field 'seller' is missing from response")
.as_object()
.expect("'seller' is not an object");
assert_has_keys_serde(seller, &["peerId"]);
// Check timelock object, nested 'None' object, and blocks_left
let timelock = response
.get("timelock")
.expect("Field 'timelock' is missing from response")
.as_object()
.expect("'timelock' is not an object");
let none_obj = timelock
.get("None")
.expect("Field 'None' is missing from 'timelock'")
.as_object()
.expect("'None' is not an object in 'timelock'");
let blocks_left = none_obj
.get("blocks_left")
.expect("Field 'blocks_left' is missing from 'None'")
.as_i64()
.expect("'blocks_left' is not an integer");
// Validate blocks_left
assert!(
blocks_left > 0 && blocks_left <= 180,
"Field 'blocks_left' should be > 0 and <= 180 but got {}",
blocks_left
);
Ok(())
})
.await;
}
#[tokio::test]
#[serial]
pub async fn swap_endpoints_invalid_or_missing_swap_id() {
setup_daemon(|_, client| async move {
for method in ["get_swap_info", "resume_swap", "cancel_refund_swap"].iter() {
let mut params = ObjectParams::new();
params.insert("swap_id", "invalid_swap").unwrap();
let response: Result<HashMap<String, String>, _> = client.request(method, params).await;
response.expect_err(&format!(
"Expected an error when swap_id is invalid for method {}",
method
));
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request(method, params).await;
response.expect_err(&format!(
"Expected an error when swap_id is missing for method {}",
method
));
}
Ok(())
})
.await;
}
#[tokio::test]
pub async fn list_sellers_missing_rendezvous_point() {
setup_daemon(|_, client| async move {
let params = ObjectParams::new();
let result: Result<HashMap<String, String>, _> =
client.request("list_sellers", params).await;
result.expect_err("Expected an error when rendezvous_point is missing");
Ok(())
})
.await;
}
/*
#[tokio::test]
#[serial]
pub async fn resume_swap_valid_swap_id() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params.insert("swap_id", SWAP_ID).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("resume_swap", params).await;
response.expect("Expected a HashMap, got an error");
}
#[tokio::test]
#[serial]
pub async fn withdraw_btc_missing_address() {
let (client, _, _) = initialize_context().await;
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when withdraw_address is missing");
}
#[tokio::test]
#[serial]
pub async fn withdraw_btc_invalid_address() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params.insert("address", "invalid_address").unwrap();
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when withdraw_address is malformed");
}
#[tokio::test]
#[serial]
pub async fn withdraw_btc_zero_amount() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params.insert("address", BITCOIN_ADDR).unwrap();
params.insert("amount", "0").unwrap();
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
response.expect_err("Expected an error when amount is 0");
}
#[tokio::test]
#[serial]
pub async fn withdraw_btc_valid_params() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params.insert("address", BITCOIN_ADDR).unwrap();
params.insert("amount", "0.1").unwrap();
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
response.expect("Expected a HashMap, got an error");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_no_params() {
let (client, _, _) = initialize_context().await;
let params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when no params are given");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_missing_seller() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when seller is missing");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_missing_monero_address() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when monero_receive_address is missing");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_missing_bitcoin_address() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when bitcoin_change_address is missing");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_malformed_bitcoin_address() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", "invalid_address")
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when bitcoin_change_address is malformed");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_malformed_monero_address() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", "invalid_address")
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when monero_receive_address is malformed");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_malformed_seller() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", "invalid_seller").unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect_err("Expected an error when seller is malformed");
}
#[tokio::test]
#[serial]
pub async fn buy_xmr_valid_params() {
let (client, _, _) = initialize_context().await;
let mut params = ObjectParams::new();
params
.insert("bitcoin_change_address", BITCOIN_ADDR)
.unwrap();
params
.insert("monero_receive_address", MONERO_ADDR)
.unwrap();
params.insert("seller", SELLER).unwrap();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
response.expect("Expected a HashMap, got an error");
}
#[tokio::test]
#[serial]
pub async fn suspend_current_swap_no_swap_running() {
let (client, _, _) = initialize_context().await;
let response: Result<HashMap<String, String>, _> = client
.request("suspend_current_swap", ObjectParams::new())
.await;
response.expect_err("Expected an error when no swap is running");
}
#[tokio::test]
#[serial]
pub async fn suspend_current_swap_swap_running() {
let (client, _, ctx) = initialize_context().await;
ctx.swap_lock
.acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap())
.await
.unwrap();
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
// Immediately release lock when suspend signal is received. Mocks a running swap that is then cancelled.
ctx.swap_lock
.listen_for_swap_force_suspension()
.await
.unwrap();
ctx.swap_lock.release_swap_lock().await.unwrap();
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let raw_history: HashMap<Uuid, String> = HashMap::new();
let response: HashMap<String, HashMap<Uuid, String>> =
client.request("get_raw_history", params).await.unwrap();
let response: HashMap<String, String> = client
.request("suspend_current_swap", ObjectParams::new())
.await
.unwrap();
assert_eq!(
response,
HashMap::from([("raw_history".to_string(), raw_history)])
HashMap::from([("swapId".to_string(), SWAP_ID.to_string())])
);
}
#[tokio::test]
#[serial]
pub async fn get_seller() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request("get_seller", params).await;
// We should ideally match the expected error and panic if it's different one,
// but the request returns a custom error (to investigate)
// Err(jsonrpsee_core::Error::Call(CallError::InvalidParams(e))) => (),
// Err(e) => panic!("ErrorType was not ParseError but {e:?}"),
match response {
Err(e) => (),
_ => panic!("Expected an error when swap_id is missing"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", "invalid_swap");
let response: Result<HashMap<String, String>, _> = client.request("get_seller", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error swap_id is malformed"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", SWAP_ID);
let response: Result<HashMap<String, String>, _> = client.request("get_seller", params).await;
match response {
Ok(hash) => (),
Err(e) => panic!(
"Expected a HashMap with correct params, got an error: {}",
e
),
}
}
#[tokio::test]
#[serial]
pub async fn get_swap_start_date() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when swap_id is missing"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", "invalid_swap");
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when swap_id is malformed"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", SWAP_ID);
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Ok(hash) => (),
Err(e) => panic!("Expected a HashMap, got an error: {}", e),
}
}
#[tokio::test]
#[serial]
pub async fn resume_swap() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when swap_id is missing"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", "invalid_swap");
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when swap_id is malformed"),
}
let mut params = ObjectParams::new();
params.insert("swap_id", SWAP_ID);
let response: Result<HashMap<String, String>, _> =
client.request("get_swap_start_date", params).await;
match response {
Ok(hash) => (),
Err(e) => panic!("Expected a HashMap, got an error: {}", e),
}
}
#[tokio::test]
#[serial]
pub async fn withdraw_btc() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when withdraw_address is missing"),
}
let mut params = ObjectParams::new();
params.insert("address", "invalid_address");
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when withdraw_address is malformed"),
}
let mut params = ObjectParams::new();
params.insert("address", BITCOIN_ADDR);
params.insert("amount", "0");
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when amount is 0"),
}
let mut params = ObjectParams::new();
params.insert("address", BITCOIN_ADDR);
params.insert("amount", "0.1");
let response: Result<HashMap<String, String>, _> = client.request("withdraw_btc", params).await;
match response {
Ok(hash) => (),
Err(e) => panic!("Expected a HashMap, got an error: {}", e),
}
}
#[tokio::test]
#[serial]
pub async fn buy_xmr() {
let (ctx, mut request) = initialize_context().await;
let move_ctx = Arc::clone(&ctx);
tokio::spawn(async move {
request.call(Arc::clone(&move_ctx)).await;
});
let url = format!("ws://{}", SERVER_ADDRESS);
tokio::time::sleep(Duration::from_secs(3)).await;
let client = WsClientBuilder::default().build(&url).await.unwrap();
let mut params = ObjectParams::new();
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when no params are given"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", BITCOIN_ADDR);
params.insert("monero_receive_address", MONERO_ADDR);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when seller is missing"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", BITCOIN_ADDR);
params.insert("seller", SELLER);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when monero_receive_address is missing"),
}
let mut params = ObjectParams::new();
params.insert("monero_receive_address", MONERO_ADDR);
params.insert("seller", SELLER);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when bitcoin_change_address is missing"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", "invalid_address");
params.insert("monero_receive_address", MONERO_ADDR);
params.insert("seller", SELLER);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when bitcoin_change_address is malformed"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", BITCOIN_ADDR);
params.insert("monero_receive_address", "invalid_address");
params.insert("seller", SELLER);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when monero_receive_address is malformed"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", BITCOIN_ADDR);
params.insert("monero_receive_address", MONERO_ADDR);
params.insert("seller", "invalid_seller");
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Err(e) => (),
_ => panic!("Expected an error when seller is malformed"),
}
let mut params = ObjectParams::new();
params.insert("bitcoin_change_address", BITCOIN_ADDR);
params.insert("monero_receive_address", MONERO_ADDR);
params.insert("seller", SELLER);
let response: Result<HashMap<String, String>, _> = client.request("buy_xmr", params).await;
match response {
Ok(hash) => (),
Err(e) => panic!("Expected a HashMap, got an error: {}", e),
}
pub async fn suspend_current_swap_timeout() {
let (client, _, ctx) = initialize_context().await;
ctx.swap_lock
.acquire_swap_lock(Uuid::parse_str(SWAP_ID).unwrap())
.await
.unwrap();
let response: Result<HashMap<String, String>, _> = client
.request("suspend_current_swap", ObjectParams::new())
.await;
response.expect_err("Expected an error when suspend signal times out");
}
*/