Introduce a testutils module with a DummyMinisafe for unit tests
So we can have unit tests without a dummy bitcoind thread, as we currently do for the startup test. This commit also implements sanity checks for the two existing commands using this mechanism.
This commit is contained in:
parent
e510c0a30d
commit
d03c469967
@ -9,7 +9,7 @@ use std::sync;
|
||||
use miniscript::bitcoin;
|
||||
|
||||
/// Information about the best block in the chain
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
||||
pub struct BlockChainTip {
|
||||
pub hash: bitcoin::BlockHash,
|
||||
pub height: i32,
|
||||
|
||||
@ -38,4 +38,9 @@ impl Poller {
|
||||
self.shutdown.store(true, atomic::Ordering::Relaxed);
|
||||
self.handle.join().expect("The poller loop must not fail");
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn test_stop(&mut self) {
|
||||
self.shutdown.store(true, atomic::Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
|
||||
@ -64,3 +64,39 @@ pub struct GetInfoResult {
|
||||
pub struct GetAddressResult {
|
||||
pub address: bitcoin::Address,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::testutils::*;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn getinfo() {
|
||||
let ms = DummyMinisafe::new();
|
||||
// We can query getinfo
|
||||
ms.handle.control.get_info();
|
||||
ms.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn getnewaddress() {
|
||||
let ms = DummyMinisafe::new();
|
||||
|
||||
let control = &ms.handle.control;
|
||||
// We can get an address
|
||||
let addr = control.get_new_address().address;
|
||||
assert_eq!(
|
||||
addr,
|
||||
bitcoin::Address::from_str(
|
||||
"bc1qgudekhcrejgtlx3yhlvdul7t4q76e5lhm0vtcsndxs6aslh4r9jsqkqhwu"
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
// We won't get the same twice.
|
||||
let addr2 = control.get_new_address().address;
|
||||
assert_ne!(addr, addr2);
|
||||
|
||||
ms.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
30
src/lib.rs
30
src/lib.rs
@ -7,6 +7,8 @@ mod database;
|
||||
pub mod descriptors;
|
||||
#[cfg(feature = "jsonrpc_server")]
|
||||
mod jsonrpc;
|
||||
#[cfg(test)]
|
||||
mod testutils;
|
||||
|
||||
pub use miniscript;
|
||||
|
||||
@ -359,6 +361,12 @@ impl DaemonHandle {
|
||||
pub fn shutdown(self) {
|
||||
self.bitcoin_poller.stop();
|
||||
}
|
||||
|
||||
// We need a shutdown utility that does not move for implementing Drop for the DummyMinisafe
|
||||
#[cfg(test)]
|
||||
pub fn test_shutdown(&mut self) {
|
||||
self.bitcoin_poller.test_stop();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, unix))]
|
||||
@ -515,6 +523,10 @@ mod tests {
|
||||
stream.flush().unwrap();
|
||||
}
|
||||
|
||||
// TODO: we could move the dummy bitcoind thread stuff to the bitcoind module to test the
|
||||
// bitcoind interface, and use the DummyMinisafe from testutils to sanity check the startup.
|
||||
// Note that startup as checked by this unit test is also tested in the functional test
|
||||
// framework.
|
||||
#[test]
|
||||
fn daemon_startup() {
|
||||
let tmp_dir = env::temp_dir().join(format!(
|
||||
@ -578,21 +590,7 @@ mod tests {
|
||||
let config = config.clone();
|
||||
move || {
|
||||
let handle = DaemonHandle::start_default(config).unwrap();
|
||||
// TODO: avoid scope creep. We should move the bitcoind-specific checks to the
|
||||
// bitcoind module, test the startup with a mocked bitcoind interface, and not test
|
||||
// commands here but in the commands module.
|
||||
let addr = handle.control.get_new_address().address;
|
||||
let addr2 = handle.control.get_new_address().address;
|
||||
assert_eq!(
|
||||
addr,
|
||||
bitcoin::Address::from_str(
|
||||
"bc1qdu9dama0pwc6fd9lj4sqzq4f728y5q2ucqyj55mfzfvuxr268zks7yajm3"
|
||||
)
|
||||
.unwrap()
|
||||
);
|
||||
assert_ne!(addr, addr2);
|
||||
handle.shutdown();
|
||||
addr
|
||||
}
|
||||
});
|
||||
complete_sanity_check(&server);
|
||||
@ -603,13 +601,11 @@ mod tests {
|
||||
complete_wallet_check(&server, &wo_path);
|
||||
complete_desc_check(&server, desc_str);
|
||||
complete_sync_check(&server);
|
||||
let addr = daemon_thread.join().unwrap();
|
||||
daemon_thread.join().unwrap();
|
||||
|
||||
// The datadir is created now, so if we restart it it won't create the wo wallet.
|
||||
let daemon_thread = thread::spawn(move || {
|
||||
let handle = DaemonHandle::start_default(config).unwrap();
|
||||
// TODO: avoid scope creep. See above comment.
|
||||
assert_ne!(handle.control.get_new_address().address, addr);
|
||||
handle.shutdown();
|
||||
});
|
||||
complete_sanity_check(&server);
|
||||
|
||||
124
src/testutils.rs
Normal file
124
src/testutils.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use crate::{
|
||||
bitcoin::{BitcoinInterface, BlockChainTip},
|
||||
config::{BitcoinConfig, Config},
|
||||
database::{DatabaseConnection, DatabaseInterface},
|
||||
DaemonControl, DaemonHandle,
|
||||
};
|
||||
|
||||
use std::{env, fs, path, process, str::FromStr, sync, thread, time};
|
||||
|
||||
use miniscript::{
|
||||
bitcoin::{self, util::bip32},
|
||||
descriptor,
|
||||
};
|
||||
|
||||
pub struct DummyBitcoind {}
|
||||
|
||||
impl BitcoinInterface for DummyBitcoind {
|
||||
fn sync_progress(&self) -> f64 {
|
||||
1.0
|
||||
}
|
||||
|
||||
fn chain_tip(&self) -> BlockChainTip {
|
||||
let hash = bitcoin::BlockHash::from_str(
|
||||
"000000007bc154e0fa7ea32218a72fe2c1bb9f86cf8c9ebf9a715ed27fdb229a",
|
||||
)
|
||||
.unwrap();
|
||||
let height = 100;
|
||||
BlockChainTip { hash, height }
|
||||
}
|
||||
|
||||
fn is_in_chain(&self, _: &BlockChainTip) -> bool {
|
||||
// No reorg
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyDb {
|
||||
curr_index: bip32::ChildNumber,
|
||||
curr_tip: Option<BlockChainTip>,
|
||||
}
|
||||
|
||||
impl DummyDb {
|
||||
pub fn new() -> DummyDb {
|
||||
DummyDb {
|
||||
curr_index: 0.into(),
|
||||
curr_tip: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DatabaseInterface for sync::Arc<sync::RwLock<DummyDb>> {
|
||||
fn connection(&self) -> Box<dyn DatabaseConnection> {
|
||||
Box::new(DummyDbConn { db: self.clone() })
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyDbConn {
|
||||
db: sync::Arc<sync::RwLock<DummyDb>>,
|
||||
}
|
||||
|
||||
impl DatabaseConnection for DummyDbConn {
|
||||
fn chain_tip(&mut self) -> Option<BlockChainTip> {
|
||||
self.db.read().unwrap().curr_tip
|
||||
}
|
||||
|
||||
fn update_tip(&mut self, tip: &BlockChainTip) {
|
||||
self.db.write().unwrap().curr_tip = Some(*tip);
|
||||
}
|
||||
|
||||
fn derivation_index(&mut self) -> bip32::ChildNumber {
|
||||
self.db.read().unwrap().curr_index
|
||||
}
|
||||
|
||||
fn update_derivation_index(&mut self, index: bip32::ChildNumber) {
|
||||
self.db.write().unwrap().curr_index = index;
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DummyMinisafe {
|
||||
tmp_dir: path::PathBuf,
|
||||
pub handle: DaemonHandle,
|
||||
}
|
||||
|
||||
impl DummyMinisafe {
|
||||
pub fn new() -> DummyMinisafe {
|
||||
let tmp_dir = env::temp_dir().join(format!(
|
||||
"minisafed-unit-tests-{}-{:?}",
|
||||
process::id(),
|
||||
thread::current().id()
|
||||
));
|
||||
fs::create_dir_all(&tmp_dir).unwrap();
|
||||
let data_dir: path::PathBuf = [tmp_dir.as_path(), path::Path::new("datadir")]
|
||||
.iter()
|
||||
.collect();
|
||||
|
||||
let network = bitcoin::Network::Bitcoin;
|
||||
let bitcoin_config = BitcoinConfig {
|
||||
network,
|
||||
poll_interval_secs: time::Duration::from_secs(2),
|
||||
};
|
||||
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("xpub68JJTXc1MWK8KLW4HGLXZBJknja7kDUJuFHnM424LbziEXsfkh1WQCiEjjHw4zLqSUm4rvhgyGkkuRowE9tCJSgt3TQB5J3SKAbZ2SdcKST/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub68JJTXc1MWK8PEQozKsRatrUHXKFNkD1Cb1BuQU9Xr5moCv87anqGyXLyUd4KpnDyZgo3gz4aN1r3NiaoweFW8UutBsBbgKHzaD5HkTkifK/*").unwrap();
|
||||
let desc = crate::descriptors::inheritance_descriptor(owner_key, heir_key, 10_000).unwrap();
|
||||
let config = Config {
|
||||
bitcoin_config,
|
||||
bitcoind_config: None,
|
||||
data_dir: Some(data_dir.clone()),
|
||||
#[cfg(unix)]
|
||||
daemon: false,
|
||||
log_level: log::LevelFilter::Debug,
|
||||
main_descriptor: desc,
|
||||
};
|
||||
|
||||
let db = sync::Arc::from(sync::RwLock::from(DummyDb::new()));
|
||||
let handle = DaemonHandle::start(config, Some(DummyBitcoind {}), Some(db)).unwrap();
|
||||
DummyMinisafe { tmp_dir, handle }
|
||||
}
|
||||
|
||||
pub fn shutdown(self) {
|
||||
self.handle.shutdown();
|
||||
fs::remove_dir_all(&self.tmp_dir).unwrap();
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user