Files
cdk/crates/cdk-integration-tests/tests/nutshell_wallet.rs
2025-05-01 03:29:43 +05:30

251 lines
7.6 KiB
Rust

use std::time::Duration;
use cdk_fake_wallet::create_fake_invoice;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use tokio::time::sleep;
/// Response from the invoice creation endpoint
#[derive(Debug, Serialize, Deserialize)]
struct InvoiceResponse {
payment_request: String,
checking_id: Option<String>,
}
/// Maximum number of attempts to check invoice payment status
const MAX_PAYMENT_CHECK_ATTEMPTS: u8 = 20;
/// Delay between payment status checks in milliseconds
const PAYMENT_CHECK_DELAY_MS: u64 = 500;
/// Default test amount in satoshis
const DEFAULT_TEST_AMOUNT: u64 = 10000;
/// Helper function to mint tokens via Lightning invoice
async fn mint_tokens(base_url: &str, amount: u64) -> String {
let client = Client::new();
// Create an invoice for the specified amount
let invoice_url = format!("{}/lightning/create_invoice?amount={}", base_url, amount);
let invoice_response = client
.post(&invoice_url)
.send()
.await
.expect("Failed to send invoice creation request")
.json::<InvoiceResponse>()
.await
.expect("Failed to parse invoice response");
println!("Created invoice: {}", invoice_response.payment_request);
invoice_response.payment_request
}
/// Helper function to wait for payment confirmation
async fn wait_for_payment_confirmation(base_url: &str, payment_request: &str) {
let client = Client::new();
let check_url = format!(
"{}/lightning/invoice_state?payment_request={}",
base_url, payment_request
);
let mut payment_confirmed = false;
for attempt in 1..=MAX_PAYMENT_CHECK_ATTEMPTS {
println!(
"Checking invoice state (attempt {}/{})...",
attempt, MAX_PAYMENT_CHECK_ATTEMPTS
);
let response = client
.get(&check_url)
.send()
.await
.expect("Failed to send payment check request");
if response.status().is_success() {
let state: Value = response
.json()
.await
.expect("Failed to parse payment state response");
println!("Payment state: {:?}", state);
if let Some(result) = state.get("result") {
if result == 1 {
payment_confirmed = true;
break;
}
}
} else {
println!("Failed to check payment state: {}", response.status());
}
sleep(Duration::from_millis(PAYMENT_CHECK_DELAY_MS)).await;
}
if !payment_confirmed {
panic!("Payment not confirmed after maximum attempts");
}
}
/// Helper function to get the current wallet balance
async fn get_wallet_balance(base_url: &str) -> u64 {
let client = Client::new();
let balance_url = format!("{}/balance", base_url);
let balance_response = client
.get(&balance_url)
.send()
.await
.expect("Failed to send balance request");
let balance: Value = balance_response
.json()
.await
.expect("Failed to parse balance response");
balance["balance"]
.as_u64()
.expect("Could not parse balance as u64")
}
/// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
#[tokio::test]
async fn test_nutshell_wallet_mint() {
// Get the wallet URL from environment variable
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
// Step 1: Create an invoice and mint tokens
let amount = DEFAULT_TEST_AMOUNT;
let payment_request = mint_tokens(&base_url, amount).await;
// Step 2: Wait for the invoice to be paid
wait_for_payment_confirmation(&base_url, &payment_request).await;
// Step 3: Check the wallet balance
let available_balance = get_wallet_balance(&base_url).await;
// Verify the balance is at least the amount we minted
assert!(
available_balance >= amount,
"Balance should be at least {} but was {}",
amount,
available_balance
);
}
/// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
#[tokio::test]
async fn test_nutshell_wallet_swap() {
// Get the wallet URL from environment variable
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
// Step 1: Create an invoice and mint tokens
let amount = DEFAULT_TEST_AMOUNT;
let payment_request = mint_tokens(&base_url, amount).await;
// Step 2: Wait for the invoice to be paid
wait_for_payment_confirmation(&base_url, &payment_request).await;
let send_amount = 100;
let send_url = format!("{}/send?amount={}", base_url, send_amount);
let client = Client::new();
let response: Value = client
.post(&send_url)
.send()
.await
.expect("Failed to send payment check request")
.json()
.await
.expect("Valid json");
// Extract the token and remove the surrounding quotes
let token_with_quotes = response
.get("token")
.expect("Missing token")
.as_str()
.expect("Token is not a string");
let token = token_with_quotes.trim_matches('"');
let receive_url = format!("{}/receive?token={}", base_url, token);
let response: Value = client
.post(&receive_url)
.send()
.await
.expect("Failed to receive request")
.json()
.await
.expect("Valid json");
let balance = response
.get("balance")
.expect("Bal in response")
.as_u64()
.expect("Valid num");
let initial_balance = response
.get("initial_balance")
.expect("Bal in response")
.as_u64()
.expect("Valid num");
let token_received = balance - initial_balance;
let fee = 1;
assert_eq!(token_received, send_amount - fee);
}
/// Test the Nutshell wallet's ability to melt tokens to pay a Lightning invoice
#[tokio::test]
async fn test_nutshell_wallet_melt() {
// Get the wallet URL from environment variable
let base_url = std::env::var("WALLET_URL").expect("Wallet url is not set");
// Step 1: Create an invoice and mint tokens
let amount = DEFAULT_TEST_AMOUNT;
let payment_request = mint_tokens(&base_url, amount).await;
// Step 2: Wait for the invoice to be paid
wait_for_payment_confirmation(&base_url, &payment_request).await;
// Get initial balance
let initial_balance = get_wallet_balance(&base_url).await;
println!("Initial balance: {}", initial_balance);
// Step 3: Create a fake invoice to pay
let payment_amount = 1000; // 1000 sats
let fake_invoice = create_fake_invoice(payment_amount, "Test payment".to_string());
let pay_url = format!("{}/lightning/pay_invoice?bolt11={}", base_url, fake_invoice);
let client = Client::new();
// Step 4: Pay the invoice
let _response: Value = client
.post(&pay_url)
.send()
.await
.expect("Failed to send pay request")
.json()
.await
.expect("Failed to parse pay response");
let final_balance = get_wallet_balance(&base_url).await;
println!("Final balance: {}", final_balance);
assert!(
initial_balance > final_balance,
"Balance should decrease after payment"
);
let balance_difference = initial_balance - final_balance;
println!("Balance decreased by: {}", balance_difference);
// The balance difference should be at least the payment amount
assert!(
balance_difference >= (payment_amount / 1000),
"Balance should decrease by at least the payment amount ({}) but decreased by {}",
payment_amount,
balance_difference
);
}