async_melt.rs 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. //! Async Melt Integration Tests
  2. //!
  3. //! This file contains tests for async melt functionality using the Prefer: respond-async header.
  4. //!
  5. //! Test Scenarios:
  6. //! - Async melt returns PENDING state immediately
  7. //! - Synchronous melt still works correctly (backward compatibility)
  8. //! - Background task completion
  9. //! - Quote polling pattern
  10. use std::sync::Arc;
  11. use bip39::Mnemonic;
  12. use cdk::amount::SplitTarget;
  13. use cdk::nuts::{CurrencyUnit, MeltQuoteState};
  14. use cdk::wallet::Wallet;
  15. use cdk::StreamExt;
  16. use cdk_fake_wallet::{create_fake_invoice, FakeInvoiceDescription};
  17. use cdk_sqlite::wallet::memory;
  18. const MINT_URL: &str = "http://127.0.0.1:8086";
  19. /// Test: Async melt returns PENDING state immediately
  20. ///
  21. /// This test validates that when calling melt with Prefer: respond-async header,
  22. /// the mint returns immediately with PENDING state.
  23. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  24. async fn test_async_melt_returns_pending() {
  25. let wallet = Wallet::new(
  26. MINT_URL,
  27. CurrencyUnit::Sat,
  28. Arc::new(memory::empty().await.unwrap()),
  29. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  30. None,
  31. )
  32. .expect("failed to create new wallet");
  33. // Step 1: Mint some tokens
  34. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  35. let mut proof_streams = wallet.proof_stream(mint_quote.clone(), SplitTarget::default(), None);
  36. let _proofs = proof_streams
  37. .next()
  38. .await
  39. .expect("payment")
  40. .expect("no error");
  41. let balance = wallet.total_balance().await.unwrap();
  42. assert_eq!(balance, 100.into());
  43. // Step 2: Create a melt quote
  44. let fake_invoice_description = FakeInvoiceDescription {
  45. pay_invoice_state: MeltQuoteState::Paid,
  46. check_payment_state: MeltQuoteState::Paid,
  47. pay_err: false,
  48. check_err: false,
  49. };
  50. let invoice = create_fake_invoice(
  51. 50_000, // 50 sats in millisats
  52. serde_json::to_string(&fake_invoice_description).unwrap(),
  53. );
  54. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  55. // Step 3: Call melt (wallet handles proof selection internally)
  56. let start_time = std::time::Instant::now();
  57. // This should complete and return the final state
  58. // TODO: Add Prefer: respond-async header support to wallet.melt()
  59. let melt_response = wallet.melt(&melt_quote.id).await.unwrap();
  60. let elapsed = start_time.elapsed();
  61. // For now, this is synchronous, so it will take longer
  62. println!("Melt took {:?}", elapsed);
  63. // Step 4: Verify the melt completed successfully
  64. assert_eq!(
  65. melt_response.state,
  66. MeltQuoteState::Paid,
  67. "Melt should complete with PAID state"
  68. );
  69. }
  70. /// Test: Synchronous melt still works correctly
  71. ///
  72. /// This test ensures backward compatibility - melt without Prefer header
  73. /// still blocks until completion and returns the final state.
  74. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  75. async fn test_sync_melt_completes_fully() {
  76. let wallet = Wallet::new(
  77. MINT_URL,
  78. CurrencyUnit::Sat,
  79. Arc::new(memory::empty().await.unwrap()),
  80. Mnemonic::generate(12).unwrap().to_seed_normalized(""),
  81. None,
  82. )
  83. .expect("failed to create new wallet");
  84. // Step 1: Mint some tokens
  85. let mint_quote = wallet.mint_quote(100.into(), None).await.unwrap();
  86. let mut proof_streams = wallet.proof_stream(mint_quote.clone(), SplitTarget::default(), None);
  87. let _proofs = proof_streams
  88. .next()
  89. .await
  90. .expect("payment")
  91. .expect("no error");
  92. let balance = wallet.total_balance().await.unwrap();
  93. assert_eq!(balance, 100.into());
  94. // Step 2: Create a melt quote
  95. let fake_invoice_description = FakeInvoiceDescription {
  96. pay_invoice_state: MeltQuoteState::Paid,
  97. check_payment_state: MeltQuoteState::Paid,
  98. pay_err: false,
  99. check_err: false,
  100. };
  101. let invoice = create_fake_invoice(
  102. 50_000, // 50 sats in millisats
  103. serde_json::to_string(&fake_invoice_description).unwrap(),
  104. );
  105. let melt_quote = wallet.melt_quote(invoice.to_string(), None).await.unwrap();
  106. // Step 3: Call synchronous melt
  107. let melt_response = wallet.melt(&melt_quote.id).await.unwrap();
  108. // Step 5: Verify response shows payment completed
  109. assert_eq!(
  110. melt_response.state,
  111. MeltQuoteState::Paid,
  112. "Synchronous melt should return PAID state"
  113. );
  114. // Step 6: Verify the quote is PAID in the mint
  115. let quote_state = wallet.melt_quote_status(&melt_quote.id).await.unwrap();
  116. assert_eq!(
  117. quote_state.state,
  118. MeltQuoteState::Paid,
  119. "Quote should be PAID"
  120. );
  121. }