Browse Source

Merge #458

458: Roll-out usage of a mocking framework in actor tests r=klochowicz a=klochowicz

Mockall is a mocking framework that removes the need for writing more actors,
making tests easier to write.

Summary:
- add one more layer of indirection (a trait per actor type: Wallet, Oracle, Monitor)
- Mocks implementing the actor traits (with default stubbed implementations if no extra
  behaviour needed)
- references to the mocks are being passed into the tests (via Arc<Mutex>>), allowing
  for dynamically changing the behaviour and adding assertions. This also
    aids readability, as the mock setup can be collocated with a particular
    test, if the test needs something extra

Co-authored-by: Mariusz Klochowicz <mariusz@klochowicz.com>
burn-down-handle
bors[bot] 3 years ago
committed by GitHub
parent
commit
9ea131cd4e
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 97
      Cargo.lock
  2. 2
      daemon/Cargo.toml
  3. 99
      daemon/tests/happy_path.rs
  4. 54
      daemon/tests/harness/mocks/mod.rs
  5. 52
      daemon/tests/harness/mocks/monitor.rs
  6. 68
      daemon/tests/harness/mocks/oracle.rs
  7. 124
      daemon/tests/harness/mocks/wallet.rs
  8. 69
      daemon/tests/harness/mod.rs

97
Cargo.lock

@ -528,6 +528,8 @@ dependencies = [
"hex", "hex",
"hkdf", "hkdf",
"itertools", "itertools",
"mockall",
"mockall_derive",
"nalgebra", "nalgebra",
"ndarray", "ndarray",
"ndarray_einsum_beta", "ndarray_einsum_beta",
@ -634,6 +636,12 @@ version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499" checksum = "0e25ea47919b1560c4e3b7fe0aaab9becf5b84a10325ddf7db0f0ba5e1026499"
[[package]]
name = "difference"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "524cbf6897b527295dff137cec09ecf3a05f4fddffd7dfcd1585403449e74198"
[[package]] [[package]]
name = "digest" name = "digest"
version = "0.9.0" version = "0.9.0"
@ -655,6 +663,12 @@ version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f"
[[package]]
name = "downcast"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bb454f0228b18c7f4c3b0ebbee346ed9c52e7443b0999cd543ff3571205701d"
[[package]] [[package]]
name = "either" name = "either"
version = "1.6.1" version = "1.6.1"
@ -741,6 +755,15 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "float-cmp"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1267f4ac4f343772758f7b1bdcbe767c218bbab93bb432acbf5162bbf85a6c4"
dependencies = [
"num-traits",
]
[[package]] [[package]]
name = "flume" name = "flume"
version = "0.10.9" version = "0.10.9"
@ -769,6 +792,12 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "fragile"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69a039c3498dc930fe810151a34ba0c1c70b02b8625035592e74432f678591f2"
[[package]] [[package]]
name = "fuchsia-cprng" name = "fuchsia-cprng"
version = "0.1.1" version = "0.1.1"
@ -1365,6 +1394,33 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "mockall"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ab571328afa78ae322493cacca3efac6a0f2e0a67305b4df31fd439ef129ac0"
dependencies = [
"cfg-if 1.0.0",
"downcast",
"fragile",
"lazy_static",
"mockall_derive",
"predicates",
"predicates-tree",
]
[[package]]
name = "mockall_derive"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7e25b214433f669161f414959594216d8e6ba83b6679d3db96899c0b4639033"
dependencies = [
"cfg-if 1.0.0",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "multer" name = "multer"
version = "2.0.1" version = "2.0.1"
@ -1437,6 +1493,12 @@ dependencies = [
"version_check", "version_check",
] ]
[[package]]
name = "normalize-line-endings"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be"
[[package]] [[package]]
name = "ntapi" name = "ntapi"
version = "0.3.6" version = "0.3.6"
@ -1675,6 +1737,35 @@ version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857"
[[package]]
name = "predicates"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f49cfaf7fdaa3bfacc6fa3e7054e65148878354a5cfddcf661df4c851f8021df"
dependencies = [
"difference",
"float-cmp",
"normalize-line-endings",
"predicates-core",
"regex",
]
[[package]]
name = "predicates-core"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57e35a3326b75e49aa85f5dc6ec15b41108cf5aee58eabb1f274dd18b73c2451"
[[package]]
name = "predicates-tree"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "338c7be2905b732ae3984a2f40032b5e94fd8f52505b186c7d4d68d193445df7"
dependencies = [
"predicates-core",
"termtree",
]
[[package]] [[package]]
name = "pretty_assertions" name = "pretty_assertions"
version = "1.0.0" version = "1.0.0"
@ -2838,6 +2929,12 @@ dependencies = [
"winapi-util", "winapi-util",
] ]
[[package]]
name = "termtree"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13a4ec180a2de59b57434704ccfad967f789b12737738798fa08798cd5824c16"
[[package]] [[package]]
name = "textwrap" name = "textwrap"
version = "0.14.2" version = "0.14.2"

2
daemon/Cargo.toml

@ -53,6 +53,8 @@ name = "maker"
path = "src/maker.rs" path = "src/maker.rs"
[dev-dependencies] [dev-dependencies]
mockall = "0.10.2"
mockall_derive = "0.10.2"
pretty_assertions = "1" pretty_assertions = "1"
serde_test = "1" serde_test = "1"
time = { version = "0.3", features = ["std"] } time = { version = "0.3", features = ["std"] }

99
daemon/tests/happy_path.rs

@ -1,6 +1,6 @@
use crate::harness::mocks::monitor::Monitor; use crate::harness::bdk::dummy_partially_signed_transaction;
use crate::harness::mocks::oracle::Oracle; use crate::harness::mocks::oracle::dummy_announcement;
use crate::harness::mocks::wallet::Wallet; use crate::harness::mocks::wallet::build_party_params;
use crate::harness::start_both; use crate::harness::start_both;
use anyhow::Context; use anyhow::Context;
use cfd_protocol::secp256k1_zkp::schnorrsig; use cfd_protocol::secp256k1_zkp::schnorrsig;
@ -8,9 +8,10 @@ use daemon::maker_cfd;
use daemon::model::cfd::{Cfd, CfdState, Order, Origin}; use daemon::model::cfd::{Cfd, CfdState, Order, Origin};
use daemon::model::{Price, Usd}; use daemon::model::{Price, Usd};
use daemon::tokio_ext::FutureExt; use daemon::tokio_ext::FutureExt;
use harness::bdk::dummy_tx_id;
use rust_decimal_macros::dec; use rust_decimal_macros::dec;
use std::time::Duration; use std::time::Duration;
use tokio::sync::watch; use tokio::sync::{watch, MutexGuard};
use tracing::subscriber::DefaultGuard; use tracing::subscriber::DefaultGuard;
use tracing_subscriber::filter::LevelFilter; use tracing_subscriber::filter::LevelFilter;
use tracing_subscriber::util::SubscriberInitExt; use tracing_subscriber::util::SubscriberInitExt;
@ -20,15 +21,7 @@ mod harness;
#[tokio::test] #[tokio::test]
async fn taker_receives_order_from_maker_on_publication() { async fn taker_receives_order_from_maker_on_publication() {
let _guard = init_tracing(); let _guard = init_tracing();
let (mut maker, mut taker) = start_both( let (mut maker, mut taker) = start_both().await;
Oracle::default(),
Monitor,
Wallet::default(),
Oracle::default(),
Monitor,
Wallet::default(),
)
.await;
assert!(is_next_none(&mut taker.order_feed).await); assert!(is_next_none(&mut taker.order_feed).await);
@ -45,15 +38,7 @@ async fn taker_receives_order_from_maker_on_publication() {
#[tokio::test] #[tokio::test]
async fn taker_takes_order_and_maker_rejects() { async fn taker_takes_order_and_maker_rejects() {
let _guard = init_tracing(); let _guard = init_tracing();
let (mut maker, mut taker) = start_both( let (mut maker, mut taker) = start_both().await;
Oracle::default(),
Monitor,
Wallet::default().with_wallet_info(),
Oracle::default(),
Monitor,
Wallet::default().with_wallet_info(),
)
.await;
// TODO: Why is this needed? For the cfd stream it is not needed // TODO: Why is this needed? For the cfd stream it is not needed
is_next_none(&mut taker.order_feed).await; is_next_none(&mut taker.order_feed).await;
@ -86,28 +71,25 @@ async fn taker_takes_order_and_maker_rejects() {
assert!(matches!(maker_cfd.state, CfdState::Rejected { .. })); assert!(matches!(maker_cfd.state, CfdState::Rejected { .. }));
} }
// Helper function setting up a "happy path" wallet mock
fn mock_wallet_sign_and_broadcast(wallet: &mut MutexGuard<'_, harness::mocks::wallet::MockWallet>) {
let mut seq = mockall::Sequence::new();
wallet
.expect_sign()
.times(1)
.returning(|_| Ok(dummy_partially_signed_transaction()))
.in_sequence(&mut seq);
wallet
.expect_broadcast()
.times(1)
.returning(|_| Ok(dummy_tx_id()))
.in_sequence(&mut seq);
}
#[tokio::test] #[tokio::test]
async fn taker_takes_order_and_maker_accepts_and_contract_setup() { async fn taker_takes_order_and_maker_accepts_and_contract_setup() {
let _guard = init_tracing(); let _guard = init_tracing();
let (mut maker, mut taker) = start_both( let (mut maker, mut taker) = start_both().await;
Oracle::default()
.with_dummy_announcement(harness::cfd_protocol::OliviaData::example_0().announcement()),
Monitor,
Wallet::default()
.with_wallet_info()
.with_party_params()
.with_psbt(harness::bdk::dummy_partially_signed_transaction())
.with_txid(harness::bdk::dummy_tx_id()),
Oracle::default()
.with_dummy_announcement(harness::cfd_protocol::OliviaData::example_0().announcement()),
Monitor,
Wallet::default()
.with_wallet_info()
.with_party_params()
.with_psbt(harness::bdk::dummy_partially_signed_transaction())
.with_txid(harness::bdk::dummy_tx_id()),
)
.await;
is_next_none(&mut taker.order_feed).await; is_next_none(&mut taker.order_feed).await;
@ -118,8 +100,40 @@ async fn taker_takes_order_and_maker_accepts_and_contract_setup() {
taker.take_order(received.clone(), Usd::new(dec!(5))); taker.take_order(received.clone(), Usd::new(dec!(5)));
let (_, _) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await; let (_, _) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await;
maker
.mocks
.oracle()
.await
.expect_get_announcement()
.returning(|_| Some(dummy_announcement()));
taker
.mocks
.oracle()
.await
.expect_get_announcement()
.returning(|_| Some(dummy_announcement()));
maker.accept_take_request(received.clone()); maker.accept_take_request(received.clone());
#[allow(clippy::redundant_closure)] // clippy is in the wrong here
maker
.mocks
.wallet()
.await
.expect_build_party_params()
.times(1)
.returning(|msg| build_party_params(msg));
#[allow(clippy::redundant_closure)] // clippy is in the wrong here
taker
.mocks
.wallet()
.await
.expect_build_party_params()
.times(1)
.returning(|msg| build_party_params(msg));
let (taker_cfd, maker_cfd) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await; let (taker_cfd, maker_cfd) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await;
// TODO: More elaborate Cfd assertions // TODO: More elaborate Cfd assertions
assert_eq!(taker_cfd.order.id, received.id); assert_eq!(taker_cfd.order.id, received.id);
@ -127,6 +141,9 @@ async fn taker_takes_order_and_maker_accepts_and_contract_setup() {
assert!(matches!(taker_cfd.state, CfdState::ContractSetup { .. })); assert!(matches!(taker_cfd.state, CfdState::ContractSetup { .. }));
assert!(matches!(maker_cfd.state, CfdState::ContractSetup { .. })); assert!(matches!(maker_cfd.state, CfdState::ContractSetup { .. }));
mock_wallet_sign_and_broadcast(&mut maker.mocks.wallet().await);
mock_wallet_sign_and_broadcast(&mut taker.mocks.wallet().await);
let (taker_cfd, maker_cfd) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await; let (taker_cfd, maker_cfd) = next_cfd(&mut taker.cfd_feed, &mut maker.cfd_feed).await;
// TODO: More elaborate Cfd assertions // TODO: More elaborate Cfd assertions
assert_eq!(taker_cfd.order.id, received.id); assert_eq!(taker_cfd.order.id, received.id);

54
daemon/tests/harness/mocks/mod.rs

@ -1,3 +1,57 @@
use std::sync::Arc;
use tokio::sync::{Mutex, MutexGuard};
use self::monitor::MonitorActor;
use self::oracle::OracleActor;
use self::wallet::WalletActor;
pub mod monitor; pub mod monitor;
pub mod oracle; pub mod oracle;
pub mod wallet; pub mod wallet;
#[derive(Clone)]
pub struct Mocks {
pub wallet: Arc<Mutex<wallet::MockWallet>>,
pub monitor: Arc<Mutex<monitor::MockMonitor>>,
pub oracle: Arc<Mutex<oracle::MockOracle>>,
}
impl Mocks {
pub async fn wallet(&mut self) -> MutexGuard<'_, wallet::MockWallet> {
self.wallet.lock().await
}
#[allow(dead_code)] // will be used soon
pub async fn monitor(&mut self) -> MutexGuard<'_, monitor::MockMonitor> {
self.monitor.lock().await
}
pub async fn oracle(&mut self) -> MutexGuard<'_, oracle::MockOracle> {
self.oracle.lock().await
}
}
impl Default for Mocks {
fn default() -> Self {
Self {
oracle: Arc::new(Mutex::new(oracle::MockOracle::new())),
monitor: Arc::new(Mutex::new(monitor::MockMonitor::new())),
wallet: Arc::new(Mutex::new(wallet::MockWallet::new())),
}
}
}
/// Creates actors with embedded mock handlers
pub fn create_actors(mocks: &Mocks) -> (OracleActor, MonitorActor, WalletActor) {
let oracle = OracleActor {
mock: mocks.oracle.clone(),
};
let monitor = MonitorActor {
mock: mocks.monitor.clone(),
};
let wallet = WalletActor {
mock: mocks.wallet.clone(),
};
(oracle, monitor, wallet)
}

52
daemon/tests/harness/mocks/monitor.rs

@ -1,29 +1,53 @@
use std::sync::Arc;
use daemon::{monitor, oracle}; use daemon::{monitor, oracle};
use mockall::*;
use tokio::sync::Mutex;
use xtra_productivity::xtra_productivity; use xtra_productivity::xtra_productivity;
/// Test Stub simulating the Monitor actor /// Test Stub simulating the Monitor actor.
pub struct Monitor; /// Serves as an entrypoint for injected mock handlers.
impl xtra::Actor for Monitor {} pub struct MonitorActor {
pub mock: Arc<Mutex<dyn Monitor + Send>>,
}
impl xtra::Actor for MonitorActor {}
impl Monitor for MonitorActor {}
#[xtra_productivity(message_impl = false)] #[xtra_productivity(message_impl = false)]
impl Monitor { impl MonitorActor {
async fn handle(&mut self, _msg: monitor::Sync) {} async fn handle(&mut self, msg: monitor::Sync) {
self.mock.lock().await.sync(msg)
}
async fn handle(&mut self, _msg: monitor::StartMonitoring) { async fn handle(&mut self, msg: monitor::StartMonitoring) {
todo!("stub this if needed") self.mock.lock().await.start_monitoring(msg)
} }
async fn handle(&mut self, _msg: monitor::CollaborativeSettlement) { async fn handle(&mut self, msg: monitor::CollaborativeSettlement) {
todo!("stub this if needed") self.mock.lock().await.collaborative_settlement(msg)
} }
async fn handle(&mut self, _msg: oracle::Attestation) { async fn handle(&mut self, msg: oracle::Attestation) {
todo!("stub this if needed") self.mock.lock().await.oracle_attestation(msg)
} }
} }
impl Default for Monitor { #[automock]
fn default() -> Self { pub trait Monitor {
Monitor fn sync(&mut self, _msg: monitor::Sync) {
unreachable!("mockall will reimplement this method")
}
fn start_monitoring(&mut self, _msg: monitor::StartMonitoring) {
unreachable!("mockall will reimplement this method")
}
fn collaborative_settlement(&mut self, _msg: monitor::CollaborativeSettlement) {
unreachable!("mockall will reimplement this method")
}
fn oracle_attestation(&mut self, _msg: oracle::Attestation) {
unreachable!("mockall will reimplement this method")
} }
} }

68
daemon/tests/harness/mocks/oracle.rs

@ -1,45 +1,57 @@
use crate::harness::cfd_protocol::OliviaData;
use daemon::model::BitMexPriceEventId; use daemon::model::BitMexPriceEventId;
use daemon::oracle; use daemon::oracle;
use mockall::*;
use std::sync::Arc;
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::sync::Mutex;
use xtra_productivity::xtra_productivity; use xtra_productivity::xtra_productivity;
pub struct Oracle { /// Test Stub simulating the Oracle actor.
announcement: Option<oracle::Announcement>, /// Serves as an entrypoint for injected mock handlers.
pub struct OracleActor {
pub mock: Arc<Mutex<dyn Oracle + Send>>,
} }
impl Oracle { impl xtra::Actor for OracleActor {}
pub fn with_dummy_announcement( impl Oracle for OracleActor {}
mut self,
dummy_announcement: cfd_protocol::Announcement, #[xtra_productivity(message_impl = false)]
) -> Self { impl OracleActor {
self.announcement = Some(oracle::Announcement { async fn handle(&mut self, msg: oracle::GetAnnouncement) -> Option<oracle::Announcement> {
id: BitMexPriceEventId::new(OffsetDateTime::UNIX_EPOCH, 0), self.mock.lock().await.get_announcement(msg)
expected_outcome_time: OffsetDateTime::now_utc(),
nonce_pks: dummy_announcement.nonce_pks,
});
self
} }
}
impl xtra::Actor for Oracle {} async fn handle(&mut self, msg: oracle::MonitorAttestation) {
self.mock.lock().await.monitor_attestation(msg)
}
#[xtra_productivity(message_impl = false)] async fn handle(&mut self, msg: oracle::Sync) {
impl Oracle { self.mock.lock().await.sync(msg)
async fn handle_get_announcement(
&mut self,
_msg: oracle::GetAnnouncement,
) -> Option<oracle::Announcement> {
self.announcement.clone()
} }
}
async fn handle(&mut self, _msg: oracle::MonitorAttestation) {} #[automock]
pub trait Oracle {
fn get_announcement(&mut self, _msg: oracle::GetAnnouncement) -> Option<oracle::Announcement> {
unreachable!("mockall will reimplement this method")
}
fn monitor_attestation(&mut self, _msg: oracle::MonitorAttestation) {
unreachable!("mockall will reimplement this method")
}
async fn handle(&mut self, _msg: oracle::Sync) {} fn sync(&mut self, _msg: oracle::Sync) {
unreachable!("mockall will reimplement this method")
}
} }
impl Default for Oracle { pub fn dummy_announcement() -> oracle::Announcement {
fn default() -> Self { let announcement = OliviaData::example_0().announcement();
Oracle { announcement: None }
oracle::Announcement {
id: BitMexPriceEventId::new(OffsetDateTime::UNIX_EPOCH, 0),
expected_outcome_time: OffsetDateTime::now_utc(),
nonce_pks: announcement.nonce_pks,
} }
} }

124
daemon/tests/harness/mocks/wallet.rs

@ -1,93 +1,81 @@
use crate::harness::cfd_protocol::dummy_wallet; use crate::harness::cfd_protocol::dummy_wallet;
use anyhow::{anyhow, Result}; use anyhow::Result;
use bdk::bitcoin::util::psbt::PartiallySignedTransaction; use bdk::bitcoin::util::psbt::PartiallySignedTransaction;
use bdk::bitcoin::{ecdsa, Amount, Txid}; use bdk::bitcoin::{ecdsa, Amount, Txid};
use cfd_protocol::secp256k1_zkp::Secp256k1; use cfd_protocol::secp256k1_zkp::Secp256k1;
use cfd_protocol::{PartyParams, WalletExt}; use cfd_protocol::{PartyParams, WalletExt};
use daemon::model::{Timestamp, WalletInfo}; use daemon::model::{Timestamp, WalletInfo};
use daemon::wallet; use daemon::wallet::{self};
use mockall::*;
use rand::thread_rng; use rand::thread_rng;
use std::sync::Arc;
use tokio::sync::Mutex;
use xtra_productivity::xtra_productivity; use xtra_productivity::xtra_productivity;
pub struct Wallet { /// Test Stub simulating the Wallet actor.
party_params: bool, /// Serves as an entrypoint for injected mock handlers.
wallet_info: Option<WalletInfo>, pub struct WalletActor {
psbt: Option<PartiallySignedTransaction>, pub mock: Arc<Mutex<dyn Wallet + Send>>,
txid: Option<Txid>,
} }
impl Wallet { impl xtra::Actor for WalletActor {}
pub fn with_party_params(mut self) -> Self {
self.party_params = true;
self
}
pub fn with_wallet_info(mut self) -> Self { #[xtra_productivity(message_impl = false)]
let s = Secp256k1::new(); impl WalletActor {
let public_key = ecdsa::PublicKey::new(s.generate_keypair(&mut thread_rng()).1); async fn handle(&mut self, msg: wallet::BuildPartyParams) -> Result<PartyParams> {
let address = bdk::bitcoin::Address::p2pkh(&public_key, bdk::bitcoin::Network::Testnet); self.mock.lock().await.build_party_params(msg)
}
async fn handle(&mut self, msg: wallet::Sync) -> Result<WalletInfo> {
tracing::info!("We are handling the wallet sync msg");
self.mock.lock().await.sync(msg)
}
async fn handle(&mut self, msg: wallet::Sign) -> Result<PartiallySignedTransaction> {
self.mock.lock().await.sign(msg)
}
async fn handle(&mut self, msg: wallet::TryBroadcastTransaction) -> Result<Txid> {
self.mock.lock().await.broadcast(msg)
}
}
self.wallet_info = Some(WalletInfo { #[automock]
balance: bdk::bitcoin::Amount::ONE_BTC, pub trait Wallet {
address, fn build_party_params(&mut self, _msg: wallet::BuildPartyParams) -> Result<PartyParams> {
last_updated_at: Timestamp::now().unwrap(), unreachable!("mockall will reimplement this method")
}); }
self fn sign(&mut self, _msg: wallet::Sign) -> Result<PartiallySignedTransaction> {
unreachable!("mockall will reimplement this method")
} }
pub fn with_psbt(mut self, psbt: PartiallySignedTransaction) -> Self { fn broadcast(&mut self, _msg: wallet::TryBroadcastTransaction) -> Result<Txid> {
self.psbt = Some(psbt); unreachable!("mockall will reimplement this method")
self
} }
pub fn with_txid(mut self, txid: Txid) -> Self { fn sync(&mut self, _msg: wallet::Sync) -> Result<WalletInfo> {
self.txid = Some(txid); unreachable!("mockall will reimplement this method")
self
} }
} }
impl xtra::Actor for Wallet {} #[allow(dead_code)]
/// tells the user they have plenty of moneys
#[xtra_productivity(message_impl = false)] fn dummy_wallet_info() -> Result<WalletInfo> {
impl Wallet { let s = Secp256k1::new();
async fn handle(&mut self, msg: wallet::BuildPartyParams) -> Result<PartyParams> { let public_key = ecdsa::PublicKey::new(s.generate_keypair(&mut thread_rng()).1);
if self.party_params { let address = bdk::bitcoin::Address::p2pkh(&public_key, bdk::bitcoin::Network::Testnet);
let mut rng = thread_rng();
let wallet = dummy_wallet(&mut rng, Amount::from_btc(0.4).unwrap(), 5).unwrap();
let party_params = wallet
.build_party_params(msg.amount, msg.identity_pk)
.unwrap();
return Ok(party_params); Ok(WalletInfo {
} balance: bdk::bitcoin::Amount::ONE_BTC,
address,
panic!("Stub not configured to return party params") last_updated_at: Timestamp::now()?,
} })
async fn handle(&mut self, _msg: wallet::Sync) -> Result<WalletInfo> {
self.wallet_info
.clone()
.ok_or_else(|| anyhow!("Stub not configured to return WalletInfo"))
}
async fn handle(&mut self, _msg: wallet::Sign) -> Result<PartiallySignedTransaction> {
self.psbt
.clone()
.ok_or_else(|| anyhow!("Stub not configured to return PartiallySignedTransaction"))
}
async fn handle(&mut self, _msg: wallet::TryBroadcastTransaction) -> Result<Txid> {
self.txid
.ok_or_else(|| anyhow!("Stub not configured to return Txid"))
}
} }
impl Default for Wallet { pub fn build_party_params(msg: wallet::BuildPartyParams) -> Result<PartyParams> {
fn default() -> Self { let mut rng = thread_rng();
Wallet { let wallet = dummy_wallet(&mut rng, Amount::from_btc(0.4).unwrap(), 5).unwrap();
party_params: false,
wallet_info: None, let party_params = wallet
psbt: None, .build_party_params(msg.amount, msg.identity_pk)
txid: None, .unwrap();
} Ok(party_params)
}
} }

69
daemon/tests/harness/mod.rs

@ -1,6 +1,6 @@
use crate::harness::mocks::monitor::Monitor; use crate::harness::mocks::monitor::MonitorActor;
use crate::harness::mocks::oracle::Oracle; use crate::harness::mocks::oracle::OracleActor;
use crate::harness::mocks::wallet::Wallet; use crate::harness::mocks::wallet::WalletActor;
use crate::schnorrsig; use crate::schnorrsig;
use daemon::maker_cfd::CfdAction; use daemon::maker_cfd::CfdAction;
use daemon::model::cfd::{Cfd, Order}; use daemon::model::cfd::{Cfd, Order};
@ -18,54 +18,43 @@ pub mod bdk;
pub mod cfd_protocol; pub mod cfd_protocol;
pub mod mocks; pub mod mocks;
// TODO: Remove all these parameters once we have the mock framework (because we have pub async fn start_both() -> (Maker, Taker) {
// Arcs then and can just default inside)
pub async fn start_both(
taker_oracle: Oracle,
taker_monitor: Monitor,
taker_wallet: Wallet,
maker_oracle: Oracle,
maker_monitor: Monitor,
maker_wallet: Wallet,
) -> (Maker, Taker) {
let oracle_pk: schnorrsig::PublicKey = schnorrsig::PublicKey::from_str( let oracle_pk: schnorrsig::PublicKey = schnorrsig::PublicKey::from_str(
"ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7", "ddd4636845a90185991826be5a494cde9f4a6947b1727217afedc6292fa4caf7",
) )
.unwrap(); .unwrap();
let maker = Maker::start(oracle_pk, maker_oracle, maker_monitor, maker_wallet).await; let maker = Maker::start(oracle_pk).await;
let taker = Taker::start( let taker = Taker::start(oracle_pk, maker.listen_addr).await;
oracle_pk,
maker.listen_addr,
taker_oracle,
taker_monitor,
taker_wallet,
)
.await;
(maker, taker) (maker, taker)
} }
/// Maker Test Setup /// Maker Test Setup
#[derive(Clone)] #[derive(Clone)]
pub struct Maker { pub struct Maker {
pub cfd_actor_addr: pub cfd_actor_addr: xtra::Address<
xtra::Address<maker_cfd::Actor<Oracle, Monitor, maker_inc_connections::Actor, Wallet>>, maker_cfd::Actor<OracleActor, MonitorActor, maker_inc_connections::Actor, WalletActor>,
>,
pub order_feed: watch::Receiver<Option<Order>>, pub order_feed: watch::Receiver<Option<Order>>,
pub cfd_feed: watch::Receiver<Vec<Cfd>>, pub cfd_feed: watch::Receiver<Vec<Cfd>>,
#[allow(dead_code)] // we need to keep the xtra::Address for refcounting #[allow(dead_code)] // we need to keep the xtra::Address for refcounting
pub inc_conn_actor_addr: xtra::Address<maker_inc_connections::Actor>, pub inc_conn_actor_addr: xtra::Address<maker_inc_connections::Actor>,
pub listen_addr: SocketAddr, pub listen_addr: SocketAddr,
pub mocks: mocks::Mocks,
} }
impl Maker { impl Maker {
pub async fn start( pub async fn start(oracle_pk: schnorrsig::PublicKey) -> Self {
oracle_pk: schnorrsig::PublicKey,
oracle: Oracle,
monitor: Monitor,
wallet: Wallet,
) -> Self {
let db = in_memory_db().await; let db = in_memory_db().await;
let mocks = mocks::Mocks::default();
let (oracle, monitor, wallet) = mocks::create_actors(&mocks);
// Sync method need to be mocked before the actors start
mocks.oracle.lock().await.expect_sync().return_const(());
mocks.monitor.lock().await.expect_sync().return_const(());
let wallet_addr = wallet.create(None).spawn_global(); let wallet_addr = wallet.create(None).spawn_global();
let settlement_time_interval_hours = time::Duration::hours(24); let settlement_time_interval_hours = time::Duration::hours(24);
@ -106,6 +95,7 @@ impl Maker {
cfd_feed: maker.cfd_feed_receiver, cfd_feed: maker.cfd_feed_receiver,
inc_conn_actor_addr: maker.inc_conn_addr, inc_conn_actor_addr: maker.inc_conn_addr,
listen_addr: address, listen_addr: address,
mocks,
} }
} }
@ -131,17 +121,12 @@ impl Maker {
pub struct Taker { pub struct Taker {
pub order_feed: watch::Receiver<Option<Order>>, pub order_feed: watch::Receiver<Option<Order>>,
pub cfd_feed: watch::Receiver<Vec<Cfd>>, pub cfd_feed: watch::Receiver<Vec<Cfd>>,
pub cfd_actor_addr: xtra::Address<taker_cfd::Actor<Oracle, Monitor, Wallet>>, pub cfd_actor_addr: xtra::Address<taker_cfd::Actor<OracleActor, MonitorActor, WalletActor>>,
pub mocks: mocks::Mocks,
} }
impl Taker { impl Taker {
pub async fn start( pub async fn start(oracle_pk: schnorrsig::PublicKey, maker_address: SocketAddr) -> Self {
oracle_pk: schnorrsig::PublicKey,
maker_address: SocketAddr,
oracle: Oracle,
monitor: Monitor,
wallet: Wallet,
) -> Self {
let connection::Actor { let connection::Actor {
send_to_maker, send_to_maker,
read_from_maker, read_from_maker,
@ -149,6 +134,13 @@ impl Taker {
let db = in_memory_db().await; let db = in_memory_db().await;
let mocks = mocks::Mocks::default();
let (oracle, monitor, wallet) = mocks::create_actors(&mocks);
// Sync method need to be mocked before the actors start
mocks.oracle.lock().await.expect_sync().return_const(());
mocks.monitor.lock().await.expect_sync().return_const(());
let wallet_addr = wallet.create(None).spawn_global(); let wallet_addr = wallet.create(None).spawn_global();
let taker = daemon::TakerActorSystem::new( let taker = daemon::TakerActorSystem::new(
@ -167,6 +159,7 @@ impl Taker {
order_feed: taker.order_feed_receiver, order_feed: taker.order_feed_receiver,
cfd_feed: taker.cfd_feed_receiver, cfd_feed: taker.cfd_feed_receiver,
cfd_actor_addr: taker.cfd_actor_addr, cfd_actor_addr: taker.cfd_actor_addr,
mocks,
} }
} }

Loading…
Cancel
Save