From 3135e291d777d474ab4b76de36d43a96ff104a3c Mon Sep 17 00:00:00 2001
From: valued mammal <valuedmammal@protonmail.com>
Date: Mon, 4 Nov 2024 15:02:46 -0500
Subject: [PATCH] test(wallet): add helpers to `test_utils`

---
 crates/wallet/src/test_utils.rs | 104 +++++++++++++++++++++++++++++++-
 crates/wallet/tests/wallet.rs   |  69 +--------------------
 2 files changed, 102 insertions(+), 71 deletions(-)

diff --git a/crates/wallet/src/test_utils.rs b/crates/wallet/src/test_utils.rs
index 94a11feac..8ea0ff470 100644
--- a/crates/wallet/src/test_utils.rs
+++ b/crates/wallet/src/test_utils.rs
@@ -1,12 +1,13 @@
 //! `bdk_wallet` test utilities
 
 use alloc::string::ToString;
+use alloc::sync::Arc;
 use core::str::FromStr;
 
-use bdk_chain::{tx_graph, BlockId, ConfirmationBlockTime};
+use bdk_chain::{tx_graph, BlockId, ChainPosition, ConfirmationBlockTime};
 use bitcoin::{
-    hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint, Transaction,
-    TxIn, TxOut, Txid,
+    absolute, hashes::Hash, transaction, Address, Amount, BlockHash, FeeRate, Network, OutPoint,
+    Transaction, TxIn, TxOut, Txid,
 };
 
 use crate::{KeychainKind, Update, Wallet};
@@ -224,6 +225,90 @@ pub fn feerate_unchecked(sat_vb: f64) -> FeeRate {
     FeeRate::from_sat_per_kwu(sat_kwu)
 }
 
+/// Receive a tx output with the given value in the latest block
+pub fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
+    let latest_cp = wallet.latest_checkpoint();
+    let height = latest_cp.height();
+    let anchor = if height == 0 {
+        ChainPosition::Unconfirmed(0)
+    } else {
+        ChainPosition::Confirmed(ConfirmationBlockTime {
+            block_id: latest_cp.block_id(),
+            confirmation_time: 0,
+        })
+    };
+    receive_output(wallet, value, anchor)
+}
+
+/// Receive a tx output with the given value and chain position
+pub fn receive_output(
+    wallet: &mut Wallet,
+    value: u64,
+    pos: ChainPosition<ConfirmationBlockTime>,
+) -> OutPoint {
+    let addr = wallet.next_unused_address(KeychainKind::External).address;
+    receive_output_to_address(wallet, addr, value, pos)
+}
+
+/// Receive a tx output to an address with the given value and chain position
+pub fn receive_output_to_address(
+    wallet: &mut Wallet,
+    addr: Address,
+    value: u64,
+    pos: ChainPosition<ConfirmationBlockTime>,
+) -> OutPoint {
+    let tx = Transaction {
+        version: transaction::Version::ONE,
+        lock_time: absolute::LockTime::ZERO,
+        input: vec![],
+        output: vec![TxOut {
+            script_pubkey: addr.script_pubkey(),
+            value: Amount::from_sat(value),
+        }],
+    };
+
+    let txid = tx.compute_txid();
+    wallet.insert_tx(tx);
+
+    match pos {
+        ChainPosition::Confirmed(anchor) => {
+            insert_anchor(wallet, txid, anchor);
+        }
+        ChainPosition::Unconfirmed(last_seen) => {
+            insert_seen_at(wallet, txid, last_seen);
+        }
+    }
+
+    OutPoint { txid, vout: 0 }
+}
+
+/// Insert a checkpoint into the wallet. This can be used to extend the wallet's local chain
+/// or to insert a block that did not exist previously. Note that if replacing a block with
+/// a different one at the same height, then all later blocks are evicted as well.
+pub fn insert_checkpoint(wallet: &mut Wallet, block: BlockId) {
+    let mut cp = wallet.latest_checkpoint();
+    cp = cp.insert(block);
+    wallet
+        .apply_update(Update {
+            chain: Some(cp),
+            ..Default::default()
+        })
+        .unwrap();
+}
+
+/// Insert transaction
+pub fn insert_tx(wallet: &mut Wallet, tx: Transaction) {
+    wallet
+        .apply_update(Update {
+            tx_update: bdk_chain::TxUpdate {
+                txs: vec![Arc::new(tx)],
+                ..Default::default()
+            },
+            ..Default::default()
+        })
+        .unwrap();
+}
+
 /// Simulates confirming a tx with `txid` by applying an update to the wallet containing
 /// the given `anchor`. Note: to be considered confirmed the anchor block must exist in
 /// the current active chain.
@@ -238,3 +323,16 @@ pub fn insert_anchor(wallet: &mut Wallet, txid: Txid, anchor: ConfirmationBlockT
         })
         .unwrap();
 }
+
+/// Marks the given `txid` seen as unconfirmed at `seen_at`
+pub fn insert_seen_at(wallet: &mut Wallet, txid: Txid, seen_at: u64) {
+    wallet
+        .apply_update(crate::Update {
+            tx_update: tx_graph::TxUpdate {
+                seen_ats: [(txid, seen_at)].into_iter().collect(),
+                ..Default::default()
+            },
+            ..Default::default()
+        })
+        .unwrap();
+}
diff --git a/crates/wallet/tests/wallet.rs b/crates/wallet/tests/wallet.rs
index 825dd930b..1ca338542 100644
--- a/crates/wallet/tests/wallet.rs
+++ b/crates/wallet/tests/wallet.rs
@@ -5,7 +5,7 @@ use std::str::FromStr;
 
 use anyhow::Context;
 use assert_matches::assert_matches;
-use bdk_chain::{tx_graph, COINBASE_MATURITY};
+use bdk_chain::COINBASE_MATURITY;
 use bdk_chain::{BlockId, ChainPosition, ConfirmationBlockTime};
 use bdk_wallet::coin_selection::{self, LargestFirstCoinSelection};
 use bdk_wallet::descriptor::{calc_checksum, DescriptorError, IntoWalletDescriptor};
@@ -31,73 +31,6 @@ use miniscript::{descriptor::KeyMap, Descriptor, DescriptorPublicKey};
 use rand::rngs::StdRng;
 use rand::SeedableRng;
 
-fn receive_output(
-    wallet: &mut Wallet,
-    value: u64,
-    height: ChainPosition<ConfirmationBlockTime>,
-) -> OutPoint {
-    let addr = wallet.next_unused_address(KeychainKind::External).address;
-    receive_output_to_address(wallet, addr, value, height)
-}
-
-fn receive_output_to_address(
-    wallet: &mut Wallet,
-    addr: Address,
-    value: u64,
-    height: ChainPosition<ConfirmationBlockTime>,
-) -> OutPoint {
-    let tx = Transaction {
-        version: transaction::Version::ONE,
-        lock_time: absolute::LockTime::ZERO,
-        input: vec![],
-        output: vec![TxOut {
-            script_pubkey: addr.script_pubkey(),
-            value: Amount::from_sat(value),
-        }],
-    };
-
-    let txid = tx.compute_txid();
-    wallet.insert_tx(tx);
-
-    match height {
-        ChainPosition::Confirmed(anchor) => {
-            insert_anchor(wallet, txid, anchor);
-        }
-        ChainPosition::Unconfirmed(last_seen) => {
-            insert_seen_at(wallet, txid, last_seen);
-        }
-    }
-
-    OutPoint { txid, vout: 0 }
-}
-
-fn receive_output_in_latest_block(wallet: &mut Wallet, value: u64) -> OutPoint {
-    let latest_cp = wallet.latest_checkpoint();
-    let height = latest_cp.height();
-    let anchor = if height == 0 {
-        ChainPosition::Unconfirmed(0)
-    } else {
-        ChainPosition::Confirmed(ConfirmationBlockTime {
-            block_id: latest_cp.block_id(),
-            confirmation_time: 0,
-        })
-    };
-    receive_output(wallet, value, anchor)
-}
-
-fn insert_seen_at(wallet: &mut Wallet, txid: Txid, seen_at: u64) {
-    use bdk_wallet::Update;
-    wallet
-        .apply_update(Update {
-            tx_update: tx_graph::TxUpdate {
-                seen_ats: [(txid, seen_at)].into_iter().collect(),
-                ..Default::default()
-            },
-            ..Default::default()
-        })
-        .unwrap();
-}
-
 fn parse_descriptor(s: &str) -> (Descriptor<DescriptorPublicKey>, KeyMap) {
     <Descriptor<DescriptorPublicKey>>::parse_descriptor(&Secp256k1::new(), s)
         .expect("failed to parse descriptor")