diff --git a/crates/cdk-integration-tests/Cargo.toml b/crates/cdk-integration-tests/Cargo.toml index 2a5c92c7..e3d821b1 100644 --- a/crates/cdk-integration-tests/Cargo.toml +++ b/crates/cdk-integration-tests/Cargo.toml @@ -36,7 +36,7 @@ serde.workspace = true serde_json.workspace = true # ln-regtest-rs = { path = "../../../../ln-regtest-rs" } ln-regtest-rs = { git = "https://github.com/thesimplekid/ln-regtest-rs", rev = "ed24716" } -lightning-invoice = { version = "0.32.0", features = ["serde", "std"] } +lightning-invoice.workspace = true tracing.workspace = true tracing-subscriber.workspace = true tokio-tungstenite.workspace = true diff --git a/crates/cdk-integration-tests/src/init_pure_tests.rs b/crates/cdk-integration-tests/src/init_pure_tests.rs index 89447a06..ac1f8f90 100644 --- a/crates/cdk-integration-tests/src/init_pure_tests.rs +++ b/crates/cdk-integration-tests/src/init_pure_tests.rs @@ -3,6 +3,7 @@ use std::fmt::{Debug, Formatter}; use std::str::FromStr; use std::sync::Arc; +use anyhow::{anyhow, Result}; use async_trait::async_trait; use bip39::Mnemonic; use cdk::amount::SplitTarget; @@ -21,7 +22,7 @@ use cdk::wallet::client::MintConnector; use cdk::wallet::Wallet; use cdk::{Amount, Error, Mint}; use cdk_fake_wallet::FakeWallet; -use tokio::sync::Notify; +use tokio::sync::{Mutex, Notify}; use tracing_subscriber::EnvFilter; use uuid::Uuid; @@ -142,7 +143,7 @@ impl MintConnector for DirectMintConnection { } } -pub async fn create_and_start_test_mint() -> anyhow::Result> { +pub fn setup_tracing() { let default_filter = "debug"; let sqlx_filter = "sqlx=warn"; @@ -153,8 +154,14 @@ pub async fn create_and_start_test_mint() -> anyhow::Result> { default_filter, sqlx_filter, hyper_filter )); - tracing_subscriber::fmt().with_env_filter(env_filter).init(); + // Ok if successful, Err if already initialized + // Allows us to setup tracing at the start of several parallel tests + let _ = tracing_subscriber::fmt() + .with_env_filter(env_filter) + .try_init(); +} +pub async fn create_and_start_test_mint() -> Result> { let mut mint_builder = MintBuilder::new(); let database = cdk_sqlite::mint::memory::empty().await?; @@ -188,6 +195,7 @@ pub async fn create_and_start_test_mint() -> anyhow::Result> { mint_builder = mint_builder .with_name("pure test mint".to_string()) .with_description("pure test mint".to_string()) + .with_urls(vec!["https://aaa".to_string()]) .with_seed(mnemonic.to_seed_normalized("").to_vec()); localstore @@ -210,23 +218,41 @@ pub async fn create_and_start_test_mint() -> anyhow::Result> { Ok(mint_arc) } -pub async fn create_test_wallet_for_mint(mint: Arc) -> anyhow::Result> { - let connector = DirectMintConnection::new(mint); +async fn create_test_wallet_for_mint(mint: Arc) -> Result { + let connector = DirectMintConnection::new(mint.clone()); + + let mint_info = mint.mint_info().await?; + let mint_url = mint_info + .urls + .as_ref() + .ok_or(anyhow!("Test mint URLs list is unset"))? + .first() + .ok_or(anyhow!("Test mint has empty URLs list"))?; let seed = Mnemonic::generate(12)?.to_seed_normalized(""); - let mint_url = "http://aa".to_string(); let unit = CurrencyUnit::Sat; let localstore = cdk_sqlite::wallet::memory::empty().await?; - let mut wallet = Wallet::new(&mint_url, unit, Arc::new(localstore), &seed, None)?; + let mut wallet = Wallet::new(mint_url, unit, Arc::new(localstore), &seed, None)?; wallet.set_client(connector); - Ok(Arc::new(wallet)) + Ok(wallet) +} + +pub async fn create_test_wallet_arc_for_mint(mint: Arc) -> Result> { + create_test_wallet_for_mint(mint).await.map(Arc::new) +} + +pub async fn create_test_wallet_arc_mut_for_mint(mint: Arc) -> Result>> { + create_test_wallet_for_mint(mint) + .await + .map(Mutex::new) + .map(Arc::new) } /// Creates a mint quote for the given amount and checks its state in a loop. Returns when /// amount is minted. -pub async fn fund_wallet(wallet: Arc, amount: u64) -> anyhow::Result { +pub async fn fund_wallet(wallet: Arc, amount: u64) -> Result { let desired_amount = Amount::from(amount); let quote = wallet.mint_quote(desired_amount, None).await?; diff --git a/crates/cdk-integration-tests/tests/integration_tests_pure.rs b/crates/cdk-integration-tests/tests/integration_tests_pure.rs index 61458575..46ed99eb 100644 --- a/crates/cdk-integration-tests/tests/integration_tests_pure.rs +++ b/crates/cdk-integration-tests/tests/integration_tests_pure.rs @@ -4,14 +4,13 @@ use cdk::amount::SplitTarget; use cdk::nuts::nut00::ProofsMethods; use cdk::wallet::SendKind; use cdk::Amount; -use cdk_integration_tests::init_pure_tests::{ - create_and_start_test_mint, create_test_wallet_for_mint, fund_wallet, -}; +use cdk_integration_tests::init_pure_tests::*; #[tokio::test] async fn test_swap_to_send() -> anyhow::Result<()> { + setup_tracing(); let mint_bob = create_and_start_test_mint().await?; - let wallet_alice = create_test_wallet_for_mint(mint_bob.clone()).await?; + let wallet_alice = create_test_wallet_arc_for_mint(mint_bob.clone()).await?; // Alice gets 64 sats fund_wallet(wallet_alice.clone(), 64).await?; @@ -33,7 +32,7 @@ async fn test_swap_to_send() -> anyhow::Result<()> { assert_eq!(Amount::from(24), wallet_alice.total_balance().await?); // Alice sends cashu, Carol receives - let wallet_carol = create_test_wallet_for_mint(mint_bob.clone()).await?; + let wallet_carol = create_test_wallet_arc_for_mint(mint_bob.clone()).await?; let received_amount = wallet_carol .receive_proofs(token.proofs(), SplitTarget::None, &[], &[]) .await?; @@ -43,3 +42,44 @@ async fn test_swap_to_send() -> anyhow::Result<()> { Ok(()) } + +/// Pure integration tests related to NUT-06 (Mint Information) +mod nut06 { + use std::str::FromStr; + use std::sync::Arc; + + use anyhow::Result; + use cashu::mint_url::MintUrl; + use cashu::Amount; + use cdk_integration_tests::init_pure_tests::*; + + #[tokio::test] + async fn test_swap_to_send() -> Result<()> { + setup_tracing(); + let mint_bob = create_and_start_test_mint().await?; + let wallet_alice_guard = create_test_wallet_arc_mut_for_mint(mint_bob.clone()).await?; + let mut wallet_alice = wallet_alice_guard.lock().await; + + // Alice gets 64 sats + fund_wallet(Arc::new(wallet_alice.clone()), 64).await?; + let balance_alice = wallet_alice.total_balance().await?; + assert_eq!(Amount::from(64), balance_alice); + + let initial_mint_url = wallet_alice.mint_url.clone(); + let mint_info_before = wallet_alice.get_mint_info().await?.unwrap(); + assert!(mint_info_before + .urls + .unwrap() + .contains(&initial_mint_url.to_string())); + + // Wallet updates mint URL + let new_mint_url = MintUrl::from_str("https://new-mint-url")?; + wallet_alice.update_mint_url(new_mint_url.clone()).await?; + + // Check balance after mint URL was updated + let balance_alice_after = wallet_alice.total_balance().await?; + assert_eq!(Amount::from(64), balance_alice_after); + + Ok(()) + } +} diff --git a/crates/cdk/src/mint/builder.rs b/crates/cdk/src/mint/builder.rs index 1913d6e7..659e44a0 100644 --- a/crates/cdk/src/mint/builder.rs +++ b/crates/cdk/src/mint/builder.rs @@ -78,6 +78,12 @@ impl MintBuilder { self } + /// Set initial mint URLs + pub fn with_urls(mut self, urls: Vec) -> Self { + self.mint_info.urls = Some(urls); + self + } + /// Set icon url pub fn with_icon_url(mut self, icon_url: String) -> Self { self.mint_info.icon_url = Some(icon_url); diff --git a/crates/cdk/src/wallet/mod.rs b/crates/cdk/src/wallet/mod.rs index 6032823b..fdf88cd0 100644 --- a/crates/cdk/src/wallet/mod.rs +++ b/crates/cdk/src/wallet/mod.rs @@ -218,13 +218,14 @@ impl Wallet { /// its URL #[instrument(skip(self))] pub async fn update_mint_url(&mut self, new_mint_url: MintUrl) -> Result<(), Error> { - self.mint_url = new_mint_url.clone(); - // Where the mint_url is in the database it must be updated + // Update the mint URL in the wallet DB self.localstore - .update_mint_url(self.mint_url.clone(), new_mint_url) + .update_mint_url(self.mint_url.clone(), new_mint_url.clone()) .await?; - self.localstore.remove_mint(self.mint_url.clone()).await?; + // Update the mint URL in the wallet struct field + self.mint_url = new_mint_url; + Ok(()) }