Переглянути джерело

Test fees (#698)

* feat: Add Docker container setup for Nutshell mint in test-nutshell recipe

* test: Add wait mechanism for Nutshell docker container startup

* test: Modify Nutshell wallet tests to run sequentially

* fix: mintd set input fee pkk

* feat: fee tests

* fix: melt returning fee in change

* fix: fee tests

* fix: fee tests
thesimplekid 3 тижнів тому
батько
коміт
7fbe55ea02

+ 1 - 6
.github/workflows/nutshell_itest.yml

@@ -7,11 +7,6 @@ jobs:
     name: Nutshell Mint Integration Tests
     runs-on: ubuntu-latest
     steps:
-      - name: Pull and start mint
-        run: |
-          docker run -d -p 3338:3338 --name nutshell -e MINT_LIGHTNING_BACKEND=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY cashubtc/nutshell:latest poetry run mint
-      - name: Check running containers
-        run: docker ps
       - name: checkout
         uses: actions/checkout@v4
       - name: Install Nix
@@ -21,7 +16,7 @@ jobs:
       - name: Rust Cache
         uses: Swatinem/rust-cache@v2
       - name: Test Nutshell
-        run: nix develop -i -L .#stable --command just test-nutshell
+        run: nix develop -i -L .#integration --command just test-nutshell
       - name: Show logs if tests fail
         if: failure()
         run: docker logs nutshell

+ 1 - 1
crates/cdk-fake-wallet/src/lib.rs

@@ -207,7 +207,7 @@ impl MintPayment for FakeWallet {
             payment_proof: Some("".to_string()),
             payment_lookup_id: payment_hash,
             status: payment_status,
-            total_spent: melt_quote.amount,
+            total_spent: melt_quote.amount + 1.into(),
             unit: melt_quote.unit,
         })
     }

+ 72 - 1
crates/cdk-integration-tests/src/lib.rs

@@ -2,11 +2,14 @@ use std::env;
 use std::sync::Arc;
 
 use anyhow::{anyhow, bail, Result};
+use cashu::Bolt11Invoice;
 use cdk::amount::{Amount, SplitTarget};
 use cdk::nuts::{MintQuoteState, NotificationPayload, State};
 use cdk::wallet::WalletSubscription;
 use cdk::Wallet;
-use init_regtest::get_mint_url;
+use cdk_fake_wallet::create_fake_invoice;
+use init_regtest::{get_lnd_dir, get_mint_url, LND_RPC_ADDR};
+use ln_regtest_rs::ln_client::{LightningClient, LndClient};
 use tokio::time::{sleep, timeout, Duration};
 
 pub mod init_auth_mint;
@@ -145,3 +148,71 @@ pub fn get_second_mint_url_from_env() -> String {
         Err(_) => get_mint_url("1"),
     }
 }
+
+// This is the ln wallet we use to send/receive ln payements as the wallet
+pub async fn init_lnd_client() -> LndClient {
+    let lnd_dir = get_lnd_dir("one");
+    let cert_file = lnd_dir.join("tls.cert");
+    let macaroon_file = lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon");
+    LndClient::new(
+        format!("https://{}", LND_RPC_ADDR),
+        cert_file,
+        macaroon_file,
+    )
+    .await
+    .unwrap()
+}
+
+/// Pays a Bolt11Invoice if it's on the regtest network, otherwise returns Ok
+///
+/// This is useful for tests that need to pay invoices in regtest mode but
+/// should be skipped in other environments.
+pub async fn pay_if_regtest(invoice: &Bolt11Invoice) -> Result<()> {
+    // Check if the invoice is for the regtest network
+    if invoice.network() == bitcoin::Network::Regtest {
+        println!("Regtest invoice");
+        let lnd_client = init_lnd_client().await;
+        lnd_client.pay_invoice(invoice.to_string()).await?;
+        Ok(())
+    } else {
+        // Not a regtest invoice, just return Ok
+        Ok(())
+    }
+}
+
+/// Determines if we're running in regtest mode based on environment variable
+///
+/// Checks the CDK_TEST_REGTEST environment variable:
+/// - If set to "1", "true", or "yes" (case insensitive), returns true
+/// - Otherwise returns false
+pub fn is_regtest_env() -> bool {
+    match env::var("CDK_TEST_REGTEST") {
+        Ok(val) => {
+            let val = val.to_lowercase();
+            val == "1" || val == "true" || val == "yes"
+        }
+        Err(_) => false,
+    }
+}
+
+/// Creates a real invoice if in regtest mode, otherwise returns a fake invoice
+///
+/// Uses the is_regtest_env() function to determine whether to
+/// create a real regtest invoice or a fake one for testing.
+pub async fn create_invoice_for_env(amount_sat: Option<u64>) -> Result<String> {
+    if is_regtest_env() {
+        // In regtest mode, create a real invoice
+        let lnd_client = init_lnd_client().await;
+        lnd_client
+            .create_invoice(amount_sat)
+            .await
+            .map_err(|e| anyhow!("Failed to create regtest invoice: {}", e))
+    } else {
+        // Not in regtest mode, create a fake invoice
+        let fake_invoice = create_fake_invoice(
+            amount_sat.expect("Amount must be defined") * 1_000,
+            "".to_string(),
+        );
+        Ok(fake_invoice.to_string())
+    }
+}

+ 14 - 78
crates/cdk-integration-tests/tests/happy_path_mint_wallet.rs

@@ -15,93 +15,24 @@ use std::sync::Arc;
 use std::time::Duration;
 use std::{char, env};
 
-use anyhow::{anyhow, bail, Result};
+use anyhow::{bail, Result};
 use bip39::Mnemonic;
 use cashu::{MeltBolt11Request, PreMintSecrets};
 use cdk::amount::{Amount, SplitTarget};
 use cdk::nuts::nut00::ProofsMethods;
 use cdk::nuts::{CurrencyUnit, MeltQuoteState, NotificationPayload, State};
 use cdk::wallet::{HttpClient, MintConnector, Wallet};
-use cdk_fake_wallet::create_fake_invoice;
-use cdk_integration_tests::init_regtest::{get_lnd_dir, LND_RPC_ADDR};
-use cdk_integration_tests::{get_mint_url_from_env, wait_for_mint_to_be_paid};
+use cdk_integration_tests::{
+    create_invoice_for_env, get_mint_url_from_env, pay_if_regtest, wait_for_mint_to_be_paid,
+};
 use cdk_sqlite::wallet::memory;
 use futures::{SinkExt, StreamExt};
 use lightning_invoice::Bolt11Invoice;
-use ln_regtest_rs::ln_client::{LightningClient, LndClient};
 use serde_json::json;
 use tokio::time::timeout;
 use tokio_tungstenite::connect_async;
 use tokio_tungstenite::tungstenite::protocol::Message;
 
-// This is the ln wallet we use to send/receive ln payements as the wallet
-async fn init_lnd_client() -> LndClient {
-    let lnd_dir = get_lnd_dir("one");
-    let cert_file = lnd_dir.join("tls.cert");
-    let macaroon_file = lnd_dir.join("data/chain/bitcoin/regtest/admin.macaroon");
-    LndClient::new(
-        format!("https://{}", LND_RPC_ADDR),
-        cert_file,
-        macaroon_file,
-    )
-    .await
-    .unwrap()
-}
-
-/// Pays a Bolt11Invoice if it's on the regtest network, otherwise returns Ok
-///
-/// This is useful for tests that need to pay invoices in regtest mode but
-/// should be skipped in other environments.
-async fn pay_if_regtest(invoice: &Bolt11Invoice) -> Result<()> {
-    // Check if the invoice is for the regtest network
-    if invoice.network() == bitcoin::Network::Regtest {
-        println!("Regtest invoice");
-        let lnd_client = init_lnd_client().await;
-        lnd_client.pay_invoice(invoice.to_string()).await?;
-        Ok(())
-    } else {
-        // Not a regtest invoice, just return Ok
-        Ok(())
-    }
-}
-
-/// Determines if we're running in regtest mode based on environment variable
-///
-/// Checks the CDK_TEST_REGTEST environment variable:
-/// - If set to "1", "true", or "yes" (case insensitive), returns true
-/// - Otherwise returns false
-fn is_regtest_env() -> bool {
-    match env::var("CDK_TEST_REGTEST") {
-        Ok(val) => {
-            let val = val.to_lowercase();
-            val == "1" || val == "true" || val == "yes"
-        }
-        Err(_) => false,
-    }
-}
-
-/// Creates a real invoice if in regtest mode, otherwise returns a fake invoice
-///
-/// Uses the is_regtest_env() function to determine whether to
-/// create a real regtest invoice or a fake one for testing.
-async fn create_invoice_for_env(amount_sat: Option<u64>) -> Result<String> {
-    if is_regtest_env() {
-        // In regtest mode, create a real invoice
-        let lnd_client = init_lnd_client().await;
-        lnd_client
-            .create_invoice(amount_sat)
-            .await
-            .map_err(|e| anyhow!("Failed to create regtest invoice: {}", e))
-    } else {
-        // Not in regtest mode, create a fake invoice
-        let fake_invoice = create_fake_invoice(
-            amount_sat.expect("Amount must be defined") * 1_000,
-            "".to_string(),
-        );
-        Ok(fake_invoice.to_string())
-    }
-}
-
 async fn get_notification<T: StreamExt<Item = Result<Message, E>> + Unpin, E: Debug>(
     reader: &mut T,
     timeout_to_wait: Duration,
@@ -263,7 +194,7 @@ async fn test_happy_mint_melt_round_trip() -> Result<()> {
 ///
 /// This ensures the basic minting flow works correctly from quote to token issuance.
 #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
-async fn test_happy_mint_melt() -> Result<()> {
+async fn test_happy_mint() -> Result<()> {
     let wallet = Wallet::new(
         &get_mint_url_from_env(),
         CurrencyUnit::Sat,
@@ -330,7 +261,7 @@ async fn test_restore() -> Result<()> {
         .mint(&mint_quote.id, SplitTarget::default(), None)
         .await?;
 
-    assert!(wallet.total_balance().await? == 100.into());
+    assert_eq!(wallet.total_balance().await?, 100.into());
 
     let wallet_2 = Wallet::new(
         &get_mint_url_from_env(),
@@ -340,18 +271,23 @@ async fn test_restore() -> Result<()> {
         None,
     )?;
 
-    assert!(wallet_2.total_balance().await? == 0.into());
+    assert_eq!(wallet_2.total_balance().await?, 0.into());
 
     let restored = wallet_2.restore().await?;
     let proofs = wallet_2.get_unspent_proofs().await?;
 
+    let expected_fee = wallet.get_proofs_fee(&proofs).await?;
     wallet_2
         .swap(None, SplitTarget::default(), proofs, None, false)
         .await?;
 
-    assert!(restored == 100.into());
+    assert_eq!(restored, 100.into());
 
-    assert_eq!(wallet_2.total_balance().await?, 100.into());
+    // Since we have to do a swap we expect to restore amount - fee
+    assert_eq!(
+        wallet_2.total_balance().await?,
+        Amount::from(100) - expected_fee
+    );
 
     let proofs = wallet.get_unspent_proofs().await?;
 

+ 44 - 0
crates/cdk-integration-tests/tests/integration_tests_pure.rs

@@ -634,6 +634,50 @@ async fn test_mint_enforce_fee() {
     let _ = mint_bob.process_swap_request(swap_request).await.unwrap();
 }
 
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_mint_change_with_fee_melt() {
+    setup_tracing();
+    let mint_bob = create_and_start_test_mint()
+        .await
+        .expect("Failed to create test mint");
+
+    mint_bob
+        .rotate_keyset(CurrencyUnit::Sat, 1, 32, 1, &HashMap::new())
+        .await
+        .unwrap();
+
+    let wallet_alice = create_test_wallet_for_mint(mint_bob.clone())
+        .await
+        .expect("Failed to create test wallet");
+
+    // Alice gets 100 sats
+    fund_wallet(
+        wallet_alice.clone(),
+        100,
+        Some(SplitTarget::Value(Amount::ONE)),
+    )
+    .await
+    .expect("Failed to fund wallet");
+
+    let proofs = wallet_alice
+        .get_unspent_proofs()
+        .await
+        .expect("Could not get proofs");
+
+    let fake_invoice = create_fake_invoice(1000, "".to_string());
+
+    let melt_quote = wallet_alice
+        .melt_quote(fake_invoice.to_string(), None)
+        .await
+        .unwrap();
+
+    let w = wallet_alice
+        .melt_proofs(&melt_quote.id, proofs)
+        .await
+        .unwrap();
+
+    assert_eq!(w.change.unwrap().total_amount().unwrap(), 97.into());
+}
 /// Tests concurrent double-spending attempts by trying to use the same proofs
 /// in 3 swap transactions simultaneously using tokio tasks
 #[tokio::test(flavor = "multi_thread", worker_threads = 3)]

+ 5 - 4
crates/cdk-integration-tests/tests/nutshell_wallet.rs

@@ -112,7 +112,7 @@ async fn get_wallet_balance(base_url: &str) -> u64 {
 }
 
 /// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[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");
@@ -137,7 +137,7 @@ async fn test_nutshell_wallet_mint() {
 }
 
 /// Test the Nutshell wallet's ability to mint tokens from a Lightning invoice
-#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+#[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");
@@ -194,11 +194,12 @@ async fn test_nutshell_wallet_swap() {
 
     let token_received = balance - initial_balance;
 
-    assert_eq!(token_received, send_amount);
+    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(flavor = "multi_thread", worker_threads = 1)]
+#[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");

+ 125 - 0
crates/cdk-integration-tests/tests/test_fees.rs

@@ -0,0 +1,125 @@
+use std::str::FromStr;
+use std::sync::Arc;
+
+use anyhow::Result;
+use bip39::Mnemonic;
+use cashu::{Bolt11Invoice, ProofsMethods};
+use cdk::amount::{Amount, SplitTarget};
+use cdk::nuts::CurrencyUnit;
+use cdk::wallet::{SendKind, SendOptions, Wallet};
+use cdk_integration_tests::{
+    create_invoice_for_env, get_mint_url_from_env, pay_if_regtest, wait_for_mint_to_be_paid,
+};
+use cdk_sqlite::wallet::memory;
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_swap() -> Result<()> {
+    let seed = Mnemonic::generate(12)?.to_seed_normalized("");
+    let wallet = Wallet::new(
+        &get_mint_url_from_env(),
+        CurrencyUnit::Sat,
+        Arc::new(memory::empty().await?),
+        &seed,
+        None,
+    )?;
+
+    let mint_quote = wallet.mint_quote(100.into(), None).await?;
+
+    let invoice = Bolt11Invoice::from_str(&mint_quote.request)?;
+    pay_if_regtest(&invoice).await?;
+
+    let _mint_amount = wallet
+        .mint(&mint_quote.id, SplitTarget::default(), None)
+        .await?;
+
+    let proofs: Vec<Amount> = wallet
+        .get_unspent_proofs()
+        .await?
+        .iter()
+        .map(|p| p.amount)
+        .collect();
+
+    println!("{:?}", proofs);
+
+    let send = wallet
+        .prepare_send(
+            4.into(),
+            SendOptions {
+                send_kind: SendKind::OfflineExact,
+                ..Default::default()
+            },
+        )
+        .await?;
+
+    let proofs = send.proofs();
+
+    let fee = wallet.get_proofs_fee(&proofs).await?;
+
+    assert_eq!(fee, 1.into());
+
+    let send = wallet.send(send, None).await?;
+
+    let rec_amount = wallet
+        .receive(&send.to_string(), SplitTarget::default(), &[], &[])
+        .await?;
+
+    assert_eq!(rec_amount, 3.into());
+
+    let wallet_balance = wallet.total_balance().await?;
+
+    assert_eq!(wallet_balance, 99.into());
+
+    Ok(())
+}
+
+#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
+async fn test_fake_melt_change_in_quote() -> Result<()> {
+    let wallet = Wallet::new(
+        &get_mint_url_from_env(),
+        CurrencyUnit::Sat,
+        Arc::new(memory::empty().await?),
+        &Mnemonic::generate(12)?.to_seed_normalized(""),
+        None,
+    )?;
+
+    let mint_quote = wallet.mint_quote(100.into(), None).await?;
+
+    let bolt11 = Bolt11Invoice::from_str(&mint_quote.request)?;
+
+    pay_if_regtest(&bolt11).await?;
+
+    wait_for_mint_to_be_paid(&wallet, &mint_quote.id, 60).await?;
+
+    let _mint_amount = wallet
+        .mint(&mint_quote.id, SplitTarget::default(), None)
+        .await?;
+
+    let invoice_amount = 9;
+
+    let invoice = create_invoice_for_env(Some(invoice_amount)).await?;
+
+    let melt_quote = wallet.melt_quote(invoice.to_string(), None).await?;
+
+    let proofs = wallet.get_unspent_proofs().await?;
+
+    let proofs_total = proofs.total_amount().unwrap();
+
+    let fee = wallet.get_proofs_fee(&proofs).await?;
+    let melt = wallet.melt_proofs(&melt_quote.id, proofs.clone()).await?;
+    let change = melt.change.unwrap().total_amount().unwrap();
+    let idk = proofs.total_amount()? - Amount::from(invoice_amount) - change;
+
+    println!("{}", idk);
+    println!("{}", fee);
+    println!("{}", proofs_total);
+    println!("{}", change);
+
+    let ln_fee = 1;
+
+    assert_eq!(
+        wallet.total_balance().await?,
+        Amount::from(100 - invoice_amount - u64::from(fee) - ln_fee)
+    );
+
+    Ok(())
+}

+ 16 - 0
crates/cdk-mintd/src/main.rs

@@ -207,6 +207,10 @@ async fn main() -> anyhow::Result<()> {
                 )
                 .await?;
 
+            if let Some(input_fee) = settings.info.input_fee_ppk {
+                mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
+            }
+
             let nut17_supported = SupportedMethods::new(PaymentMethod::Bolt11, CurrencyUnit::Sat);
 
             mint_builder = mint_builder.add_supported_websockets(nut17_supported);
@@ -226,6 +230,9 @@ async fn main() -> anyhow::Result<()> {
                     Arc::new(lnbits),
                 )
                 .await?;
+            if let Some(input_fee) = settings.info.input_fee_ppk {
+                mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
+            }
 
             let nut17_supported = SupportedMethods::new(PaymentMethod::Bolt11, CurrencyUnit::Sat);
 
@@ -246,6 +253,9 @@ async fn main() -> anyhow::Result<()> {
                     Arc::new(lnd),
                 )
                 .await?;
+            if let Some(input_fee) = settings.info.input_fee_ppk {
+                mint_builder = mint_builder.set_unit_fee(&CurrencyUnit::Sat, input_fee)?;
+            }
 
             let nut17_supported = SupportedMethods::new(PaymentMethod::Bolt11, CurrencyUnit::Sat);
 
@@ -272,6 +282,9 @@ async fn main() -> anyhow::Result<()> {
                         fake.clone(),
                     )
                     .await?;
+                if let Some(input_fee) = settings.info.input_fee_ppk {
+                    mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
+                }
 
                 let nut17_supported = SupportedMethods::new(PaymentMethod::Bolt11, unit);
 
@@ -308,6 +321,9 @@ async fn main() -> anyhow::Result<()> {
                         Arc::new(processor),
                     )
                     .await?;
+                if let Some(input_fee) = settings.info.input_fee_ppk {
+                    mint_builder = mint_builder.set_unit_fee(&unit, input_fee)?;
+                }
 
                 let nut17_supported = SupportedMethods::new(PaymentMethod::Bolt11, unit);
                 mint_builder = mint_builder.add_supported_websockets(nut17_supported);

+ 14 - 0
crates/cdk/src/mint/builder.rs

@@ -292,6 +292,20 @@ impl MintBuilder {
         self
     }
 
+    /// Sets the input fee ppk for a given unit
+    ///
+    /// The unit **MUST** already have been added with a ln backend
+    pub fn set_unit_fee(mut self, unit: &CurrencyUnit, input_fee_ppk: u64) -> Result<Self, Error> {
+        let (input_fee, _max_order) = self
+            .supported_units
+            .get_mut(unit)
+            .ok_or(Error::UnsupportedUnit)?;
+
+        *input_fee = input_fee_ppk;
+
+        Ok(self)
+    }
+
     /// Build mint
     pub async fn build(&self) -> anyhow::Result<Mint> {
         let localstore = self

+ 4 - 1
crates/cdk/src/mint/melt.rs

@@ -669,7 +669,10 @@ impl Mint {
                     return Err(Error::BlindedMessageAlreadySigned);
                 }
 
-                let change_target = melt_request.proofs_amount()? - total_spent;
+                let fee = self.get_proofs_fee(melt_request.inputs()).await?;
+
+                let change_target = melt_request.proofs_amount()? - total_spent - fee;
+
                 let mut amounts = change_target.split();
                 let mut change_sigs = Vec::with_capacity(amounts.len());
 

+ 29 - 7
justfile

@@ -45,7 +45,7 @@ test: build
   cargo test --lib
 
   # Run pure integration tests
-  cargo test -p cdk-integration-tests --test mint
+  cargo test -p cdk-integration-tests --test mint 
 
   
 # run doc tests
@@ -66,12 +66,34 @@ test-all db="memory":
     ./misc/fake_itests.sh "{{db}}"
     
 test-nutshell:
-    #!/usr/bin/env bash
-    export CDK_TEST_MINT_URL=http://127.0.0.1:3338
-    export LN_BACKEND=FAKEWALLET
-    cargo test -p cdk-integration-tests --test happy_path_mint_wallet
-    unset CDK_TEST_MINT_URL
-    unset LN_BACKEND
+  #!/usr/bin/env bash
+  docker run -d -p 3338:3338 --name nutshell -e MINT_LIGHTNING_BACKEND=FakeWallet -e MINT_LISTEN_HOST=0.0.0.0 -e MINT_LISTEN_PORT=3338 -e MINT_PRIVATE_KEY=TEST_PRIVATE_KEY -e MINT_INPUT_FEE_PPK=100  cashubtc/nutshell:latest poetry run mint
+  
+  # Wait for the Nutshell service to be ready
+  echo "Waiting for Nutshell to start..."
+  max_attempts=30
+  attempt=0
+  while ! curl -s http://127.0.0.1:3338/v1/info > /dev/null; do
+    attempt=$((attempt+1))
+    if [ $attempt -ge $max_attempts ]; then
+      echo "Nutshell failed to start after $max_attempts attempts"
+      docker stop nutshell
+      docker rm nutshell
+      exit 1
+    fi
+    echo "Waiting for Nutshell to start (attempt $attempt/$max_attempts)..."
+    sleep 1
+  done
+  echo "Nutshell is ready!"
+  
+  export CDK_TEST_MINT_URL=http://127.0.0.1:3338
+  export LN_BACKEND=FAKEWALLET
+  cargo test -p cdk-integration-tests --test happy_path_mint_wallet
+  cargo test -p cdk-integration-tests --test test_fees
+  unset CDK_TEST_MINT_URL
+  unset LN_BACKEND
+  docker stop nutshell
+  docker rm nutshell
     
 
 # run `cargo clippy` on everything

+ 5 - 1
misc/nutshell_wallet_itest.sh

@@ -33,6 +33,7 @@ cleanup() {
   unset CDK_MINTD_LN_BACKEND CDK_MINTD_FAKE_WALLET_SUPPORTED_UNITS CDK_MINTD_MNEMONIC
   unset CDK_MINTD_FAKE_WALLET_FEE_PERCENT CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN CDK_MINTD_DATABASE
   unset TEST_STATUS
+  unset CDK_MINTD_INPUT_FEE_PPK
   echo "Cleanup complete."
 }
 
@@ -55,6 +56,7 @@ export CDK_MINTD_MNEMONIC="eye survey guilt napkin crystal cup whisper salt lugg
 export CDK_MINTD_FAKE_WALLET_FEE_PERCENT="0"
 export CDK_MINTD_FAKE_WALLET_RESERVE_FEE_MIN="1"
 export CDK_MINTD_DATABASE="redb"
+export CDK_MINTD_INPUT_FEE_PPK="100"
 
 
 echo "Starting fake mintd"
@@ -150,10 +152,12 @@ fi
 # Export URLs as environment variables
 export MINT_URL=${MINT_URL}
 export WALLET_URL=${WALLET_URL}
+export CDK_TEST_MINT_URL=${MINT_URL}
 
 # Run the integration test
 echo "Running integration test..."
-cargo test -p cdk-integration-tests --tests nutshell_wallet
+cargo test -p cdk-integration-tests --test nutshell_wallet
+cargo test -p cdk-integration-tests --test test_fees
 TEST_STATUS=$?
 
 # Exit with the test status