|
@@ -14,7 +14,8 @@ use cdk::nuts::{
|
|
PreMintSecrets, State,
|
|
PreMintSecrets, State,
|
|
};
|
|
};
|
|
use cdk::wallet::client::{HttpClient, MintConnector};
|
|
use cdk::wallet::client::{HttpClient, MintConnector};
|
|
-use cdk::wallet::{Wallet, WalletSubscription};
|
|
|
|
|
|
+use cdk::wallet::Wallet;
|
|
|
|
+use cdk::WalletSubscription;
|
|
use cdk_integration_tests::init_regtest::{
|
|
use cdk_integration_tests::init_regtest::{
|
|
get_cln_dir, get_lnd_cert_file_path, get_lnd_dir, get_lnd_macaroon_path, get_mint_port,
|
|
get_cln_dir, get_lnd_cert_file_path, get_lnd_dir, get_lnd_macaroon_path, get_mint_port,
|
|
get_mint_url, get_mint_ws_url, LND_RPC_ADDR, LND_TWO_RPC_ADDR,
|
|
get_mint_url, get_mint_ws_url, LND_RPC_ADDR, LND_TWO_RPC_ADDR,
|
|
@@ -140,7 +141,8 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
|
|
NotificationPayload::MeltQuoteBolt11Response(melt) => melt,
|
|
NotificationPayload::MeltQuoteBolt11Response(melt) => melt,
|
|
_ => panic!("Wrong payload"),
|
|
_ => panic!("Wrong payload"),
|
|
};
|
|
};
|
|
- assert_eq!(payload.amount + payload.fee_reserve, 100.into());
|
|
|
|
|
|
+
|
|
|
|
+ assert_eq!(payload.amount + payload.fee_reserve, 50.into());
|
|
assert_eq!(payload.quote.to_string(), melt.id);
|
|
assert_eq!(payload.quote.to_string(), melt.id);
|
|
assert_eq!(payload.state, MeltQuoteState::Unpaid);
|
|
assert_eq!(payload.state, MeltQuoteState::Unpaid);
|
|
|
|
|
|
@@ -151,7 +153,7 @@ async fn test_regtest_mint_melt_round_trip() -> Result<()> {
|
|
NotificationPayload::MeltQuoteBolt11Response(melt) => melt,
|
|
NotificationPayload::MeltQuoteBolt11Response(melt) => melt,
|
|
_ => panic!("Wrong payload"),
|
|
_ => panic!("Wrong payload"),
|
|
};
|
|
};
|
|
- assert_eq!(payload.amount + payload.fee_reserve, 100.into());
|
|
|
|
|
|
+ assert_eq!(payload.amount + payload.fee_reserve, 50.into());
|
|
assert_eq!(payload.quote.to_string(), melt.id);
|
|
assert_eq!(payload.quote.to_string(), melt.id);
|
|
assert_eq!(payload.state, MeltQuoteState::Paid);
|
|
assert_eq!(payload.state, MeltQuoteState::Paid);
|
|
|
|
|
|
@@ -422,19 +424,7 @@ async fn test_cached_mint() -> Result<()> {
|
|
let quote = wallet.mint_quote(mint_amount, None).await?;
|
|
let quote = wallet.mint_quote(mint_amount, None).await?;
|
|
lnd_client.pay_invoice(quote.request).await?;
|
|
lnd_client.pay_invoice(quote.request).await?;
|
|
|
|
|
|
- let mut subscription = wallet
|
|
|
|
- .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![quote
|
|
|
|
- .id
|
|
|
|
- .clone()]))
|
|
|
|
- .await;
|
|
|
|
-
|
|
|
|
- while let Some(msg) = subscription.recv().await {
|
|
|
|
- if let NotificationPayload::MintQuoteBolt11Response(response) = msg {
|
|
|
|
- if response.state == MintQuoteState::Paid {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ wait_for_mint_to_be_paid(&wallet, "e.id, 60).await?;
|
|
|
|
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
let active_keyset_id = wallet.get_active_mint_keyset().await?.id;
|
|
let http_client = HttpClient::new(get_mint_url("0").as_str().parse()?);
|
|
let http_client = HttpClient::new(get_mint_url("0").as_str().parse()?);
|
|
@@ -459,6 +449,59 @@ async fn test_cached_mint() -> Result<()> {
|
|
}
|
|
}
|
|
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
|
+async fn test_websocket_connection() -> Result<()> {
|
|
|
|
+ let wallet = Wallet::new(
|
|
|
|
+ &get_mint_url("0"),
|
|
|
|
+ CurrencyUnit::Sat,
|
|
|
|
+ Arc::new(WalletMemoryDatabase::default()),
|
|
|
|
+ &Mnemonic::generate(12)?.to_seed_normalized(""),
|
|
|
|
+ None,
|
|
|
|
+ )?;
|
|
|
|
+
|
|
|
|
+ // Create a small mint quote to test notifications
|
|
|
|
+ let mint_quote = wallet.mint_quote(10.into(), None).await?;
|
|
|
|
+
|
|
|
|
+ // Subscribe to notifications for this quote
|
|
|
|
+ let mut subscription = wallet
|
|
|
|
+ .subscribe(WalletSubscription::Bolt11MintQuoteState(vec![mint_quote
|
|
|
|
+ .id
|
|
|
|
+ .clone()]))
|
|
|
|
+ .await;
|
|
|
|
+
|
|
|
|
+ // First check we get the unpaid state
|
|
|
|
+ let msg = timeout(Duration::from_secs(10), subscription.recv())
|
|
|
|
+ .await
|
|
|
|
+ .expect("timeout waiting for unpaid notification")
|
|
|
|
+ .ok_or_else(|| anyhow::anyhow!("No unpaid notification received"))?;
|
|
|
|
+
|
|
|
|
+ match msg {
|
|
|
|
+ NotificationPayload::MintQuoteBolt11Response(response) => {
|
|
|
|
+ assert_eq!(response.quote.to_string(), mint_quote.id);
|
|
|
|
+ assert_eq!(response.state, MintQuoteState::Unpaid);
|
|
|
|
+ }
|
|
|
|
+ _ => bail!("Unexpected notification type"),
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ let lnd_client = init_lnd_client().await;
|
|
|
|
+ lnd_client.pay_invoice(mint_quote.request).await?;
|
|
|
|
+
|
|
|
|
+ // Wait for paid notification with 10 second timeout
|
|
|
|
+ let msg = timeout(Duration::from_secs(10), subscription.recv())
|
|
|
|
+ .await
|
|
|
|
+ .expect("timeout waiting for paid notification")
|
|
|
|
+ .ok_or_else(|| anyhow::anyhow!("No paid notification received"))?;
|
|
|
|
+
|
|
|
|
+ match msg {
|
|
|
|
+ NotificationPayload::MintQuoteBolt11Response(response) => {
|
|
|
|
+ assert_eq!(response.quote.to_string(), mint_quote.id);
|
|
|
|
+ assert_eq!(response.state, MintQuoteState::Paid);
|
|
|
|
+ Ok(())
|
|
|
|
+ }
|
|
|
|
+ _ => bail!("Unexpected notification type"),
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
async fn test_multimint_melt() -> Result<()> {
|
|
async fn test_multimint_melt() -> Result<()> {
|
|
let lnd_client = init_lnd_client().await;
|
|
let lnd_client = init_lnd_client().await;
|
|
|
|
|
|
@@ -482,26 +525,14 @@ async fn test_multimint_melt() -> Result<()> {
|
|
// Fund the wallets
|
|
// Fund the wallets
|
|
let quote = wallet1.mint_quote(mint_amount, None).await?;
|
|
let quote = wallet1.mint_quote(mint_amount, None).await?;
|
|
lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
- loop {
|
|
|
|
- let quote_status = wallet1.mint_quote_state("e.id).await?;
|
|
|
|
- if quote_status.state == MintQuoteState::Paid {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- tracing::debug!("Quote not yet paid");
|
|
|
|
- }
|
|
|
|
|
|
+ wait_for_mint_to_be_paid(&wallet1, "e.id, 60).await?;
|
|
wallet1
|
|
wallet1
|
|
.mint("e.id, SplitTarget::default(), None)
|
|
.mint("e.id, SplitTarget::default(), None)
|
|
.await?;
|
|
.await?;
|
|
|
|
|
|
let quote = wallet2.mint_quote(mint_amount, None).await?;
|
|
let quote = wallet2.mint_quote(mint_amount, None).await?;
|
|
lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
lnd_client.pay_invoice(quote.request.clone()).await?;
|
|
- loop {
|
|
|
|
- let quote_status = wallet2.mint_quote_state("e.id).await?;
|
|
|
|
- if quote_status.state == MintQuoteState::Paid {
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- tracing::debug!("Quote not yet paid");
|
|
|
|
- }
|
|
|
|
|
|
+ wait_for_mint_to_be_paid(&wallet2, "e.id, 60).await?;
|
|
wallet2
|
|
wallet2
|
|
.mint("e.id, SplitTarget::default(), None)
|
|
.mint("e.id, SplitTarget::default(), None)
|
|
.await?;
|
|
.await?;
|
|
@@ -538,3 +569,38 @@ async fn test_multimint_melt() -> Result<()> {
|
|
assert!(result1.state == MeltQuoteState::Paid);
|
|
assert!(result1.state == MeltQuoteState::Paid);
|
|
Ok(())
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
|
|
|
|
+async fn test_database_type() -> Result<()> {
|
|
|
|
+ // Get the database type and work dir from environment
|
|
|
|
+ let db_type = std::env::var("MINT_DATABASE").expect("MINT_DATABASE env var should be set");
|
|
|
|
+ let work_dir =
|
|
|
|
+ std::env::var("CDK_MINTD_WORK_DIR").expect("CDK_MINTD_WORK_DIR env var should be set");
|
|
|
|
+
|
|
|
|
+ // Check that the correct database file exists
|
|
|
|
+ match db_type.as_str() {
|
|
|
|
+ "REDB" => {
|
|
|
|
+ let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.redb");
|
|
|
|
+ assert!(
|
|
|
|
+ db_path.exists(),
|
|
|
|
+ "Expected redb database file to exist at {:?}",
|
|
|
|
+ db_path
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ "SQLITE" => {
|
|
|
|
+ let db_path = std::path::Path::new(&work_dir).join("cdk-mintd.sqlite");
|
|
|
|
+ assert!(
|
|
|
|
+ db_path.exists(),
|
|
|
|
+ "Expected sqlite database file to exist at {:?}",
|
|
|
|
+ db_path
|
|
|
|
+ );
|
|
|
|
+ }
|
|
|
|
+ "MEMORY" => {
|
|
|
|
+ // Memory database has no file to check
|
|
|
|
+ println!("Memory database in use - no file to check");
|
|
|
|
+ }
|
|
|
|
+ _ => bail!("Unknown database type: {}", db_type),
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ Ok(())
|
|
|
|
+}
|