Skip to content

Commit

Permalink
fix: itests
Browse files Browse the repository at this point in the history
  • Loading branch information
thesimplekid committed Dec 24, 2024
1 parent a429e53 commit c74b98e
Show file tree
Hide file tree
Showing 4 changed files with 318 additions and 5 deletions.
8 changes: 5 additions & 3 deletions crates/cdk-integration-tests/tests/fake_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ async fn test_fake_mint_with_witness() -> Result<()> {
Arc::new(WalletMemoryDatabase::default()),
&Mnemonic::generate(12)?.to_seed_normalized(""),
None,
None,
)?;
let mint_quote = wallet.mint_quote(100.into(), None).await?;

Expand All @@ -412,6 +413,7 @@ async fn test_fake_mint_without_witness() -> Result<()> {
Arc::new(WalletMemoryDatabase::default()),
&Mnemonic::generate(12)?.to_seed_normalized(""),
None,
None,
)?;

let mint_quote = wallet.mint_quote(100.into(), None).await?;
Expand All @@ -431,7 +433,7 @@ async fn test_fake_mint_without_witness() -> Result<()> {
signature: None,
};

let response = http_client.post_mint(request.clone()).await;
let response = http_client.post_mint(request.clone(), None).await;

match response {
Err(cdk::error::Error::SignatureMissingOrInvalid) => Ok(()),
Expand All @@ -440,7 +442,6 @@ async fn test_fake_mint_without_witness() -> Result<()> {
}
}

// TODO: Rewrite this test to include witness wrong
#[tokio::test(flavor = "multi_thread", worker_threads = 1)]
async fn test_fake_mint_with_wrong_witness() -> Result<()> {
let wallet = Wallet::new(
Expand All @@ -449,6 +450,7 @@ async fn test_fake_mint_with_wrong_witness() -> Result<()> {
Arc::new(WalletMemoryDatabase::default()),
&Mnemonic::generate(12)?.to_seed_normalized(""),
None,
None,
)?;

let mint_quote = wallet.mint_quote(100.into(), None).await?;
Expand All @@ -472,7 +474,7 @@ async fn test_fake_mint_with_wrong_witness() -> Result<()> {

request.sign(secret_key)?;

let response = http_client.post_mint(request.clone()).await;
let response = http_client.post_mint(request.clone(), None).await;

match response {
Err(cdk::error::Error::SignatureMissingOrInvalid) => Ok(()),
Expand Down
1 change: 1 addition & 0 deletions crates/cdk-integration-tests/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

310 changes: 310 additions & 0 deletions crates/cdk-integration-tests/tests/pure_tests/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
use std::assert_eq;
use std::collections::HashMap;
use std::fmt::{Debug, Formatter};
use std::str::FromStr;
use std::sync::Arc;

use async_trait::async_trait;
use cdk::amount::SplitTarget;
use cdk::cdk_database::mint_memory::MintMemoryDatabase;
use cdk::cdk_database::WalletMemoryDatabase;
use cdk::nuts::nutxx1::MintAuthRequest;
use cdk::nuts::{
AuthToken, CheckStateRequest, CheckStateResponse, CurrencyUnit, Id, KeySet, KeysetResponse,
MeltBolt11Request, MeltQuoteBolt11Request, MeltQuoteBolt11Response, MintBolt11Request,
MintBolt11Response, MintInfo, MintQuoteBolt11Request, MintQuoteBolt11Response, MintQuoteState,
Nuts, RestoreRequest, RestoreResponse, SwapRequest, SwapResponse,
};
use cdk::types::QuoteTTL;
use cdk::util::unix_time;
use cdk::wallet::MintConnector;
use cdk::{Amount, Error, Mint, Wallet};
use cdk_integration_tests::create_backends_fake_wallet;
use rand::random;
use tokio::sync::Notify;
use uuid::Uuid;

struct DirectMintConnection {
mint: Arc<Mint>,
}

impl Debug for DirectMintConnection {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"DirectMintConnection {{ mint_info: {:?} }}",
self.mint.mint_info
)
}
}

/// Implements the generic [MintConnector] (i.e. use the interface that expects to communicate
/// to a generic mint, where we don't know that quote ID's are [Uuid]s) for [DirectMintConnection],
/// where we know we're dealing with a mint that uses [Uuid]s for quotes.
/// Convert the requests and responses between the [String] and [Uuid] variants as necessary.
#[async_trait]
impl MintConnector for DirectMintConnection {
async fn get_mint_keys(&self, _auth_token: Option<AuthToken>) -> Result<Vec<KeySet>, Error> {
self.mint.pubkeys().await.map(|pks| pks.keysets)
}

async fn get_mint_keyset(
&self,
keyset_id: Id,
_auth_token: Option<AuthToken>,
) -> Result<KeySet, Error> {
self.mint
.keyset(&keyset_id)
.await
.and_then(|res| res.ok_or(Error::UnknownKeySet))
}

async fn get_mint_keysets(
&self,
_auth_token: Option<AuthToken>,
) -> Result<KeysetResponse, Error> {
self.mint.keysets().await
}

async fn post_mint_quote(
&self,
request: MintQuoteBolt11Request,
_auth_token: Option<AuthToken>,
) -> Result<MintQuoteBolt11Response<String>, Error> {
self.mint
.get_mint_bolt11_quote(None, request)
.await
.map(Into::into)
}

async fn get_mint_quote_status(
&self,
quote_id: &str,
_auth_token: Option<AuthToken>,
) -> Result<MintQuoteBolt11Response<String>, Error> {
let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
self.mint
.check_mint_quote(None, &quote_id_uuid)
.await
.map(Into::into)
}

async fn post_mint(
&self,
request: MintBolt11Request<String>,
_auth_token: Option<AuthToken>,
) -> Result<MintBolt11Response, Error> {
let request_uuid = request.try_into().unwrap();
self.mint.process_mint_request(None, request_uuid).await
}

async fn post_melt_quote(
&self,
request: MeltQuoteBolt11Request,
_auth_token: Option<AuthToken>,
) -> Result<MeltQuoteBolt11Response<String>, Error> {
self.mint
.get_melt_bolt11_quote(None, &request)
.await
.map(Into::into)
}

async fn get_melt_quote_status(
&self,
quote_id: &str,
_auth_token: Option<AuthToken>,
) -> Result<MeltQuoteBolt11Response<String>, Error> {
let quote_id_uuid = Uuid::from_str(quote_id).unwrap();
self.mint
.check_melt_quote(None, &quote_id_uuid)
.await
.map(Into::into)
}

async fn post_melt(
&self,
request: MeltBolt11Request<String>,
_auth_token: Option<AuthToken>,
) -> Result<MeltQuoteBolt11Response<String>, Error> {
let request_uuid = request.try_into().unwrap();
self.mint
.melt_bolt11(None, &request_uuid)
.await
.map(Into::into)
}

async fn post_swap(
&self,
swap_request: SwapRequest,
_auth_token: Option<AuthToken>,
) -> Result<SwapResponse, Error> {
self.mint.process_swap_request(None, swap_request).await
}

async fn get_mint_info(&self) -> Result<MintInfo, Error> {
Ok(self.mint.mint_info().clone().time(unix_time()))
}

async fn post_check_state(
&self,
request: CheckStateRequest,
_auth_token: Option<AuthToken>,
) -> Result<CheckStateResponse, Error> {
self.mint.check_state(&request).await
}

async fn post_restore(
&self,
request: RestoreRequest,
_auth_token: Option<AuthToken>,
) -> Result<RestoreResponse, Error> {
self.mint.restore(request).await
}

/// Get Blind Auth keys
async fn get_mint_blind_auth_keys(&self) -> Result<Vec<KeySet>, Error> {
todo!();
}
/// Get Blind Auth Keyset
async fn get_mint_blind_auth_keyset(&self, _keyset_id: Id) -> Result<KeySet, Error> {
todo!();
}
/// Get Blind Auth keysets
async fn get_mint_blind_auth_keysets(&self) -> Result<KeysetResponse, Error> {
todo!();
}
/// Post mint blind auth
async fn post_mint_blind_auth(
&self,
_request: MintAuthRequest,
) -> Result<MintBolt11Response, Error> {
todo!();
}
}

fn get_mint_connector(mint: Arc<Mint>) -> DirectMintConnection {
DirectMintConnection { mint }
}

async fn create_and_start_test_mint() -> anyhow::Result<Arc<Mint>> {
let fee: u64 = 0;
let mut supported_units = HashMap::new();
supported_units.insert(CurrencyUnit::Sat, (fee, 32));

let nuts = Nuts::new()
.nut07(true)
.nut08(true)
.nut09(true)
.nut10(true)
.nut11(true)
.nut12(true)
.nut14(true);

let mint_info = MintInfo::new().nuts(nuts);

let quote_ttl = QuoteTTL::new(10000, 10000);

let mint_url = "http://aaa";

let seed = random::<[u8; 32]>();
let mint: Mint = Mint::new(
mint_url,
&seed,
mint_info,
quote_ttl,
Arc::new(MintMemoryDatabase::default()),
create_backends_fake_wallet(),
supported_units,
HashMap::new(),
HashMap::new(),
)
.await?;

let mint_arc = Arc::new(mint);

let mint_arc_clone = Arc::clone(&mint_arc);
let shutdown = Arc::new(Notify::new());
tokio::spawn({
let shutdown = Arc::clone(&shutdown);
async move { mint_arc_clone.wait_for_paid_invoices(shutdown).await }
});

Ok(mint_arc)
}

fn create_test_wallet_for_mint(mint: Arc<Mint>) -> anyhow::Result<Arc<Wallet>> {
let connector = get_mint_connector(mint);

let seed = random::<[u8; 32]>();
let mint_url = connector.mint.mint_url.to_string();
let unit = CurrencyUnit::Sat;

let localstore = WalletMemoryDatabase::default();
let mut wallet = Wallet::new(&mint_url, unit, Arc::new(localstore), &seed, None, None)?;

wallet.set_client(connector);

Ok(Arc::new(wallet))
}

/// Creates a mint quote for the given amount and checks its state in a loop. Returns when
/// amount is minted.
async fn receive(wallet: Arc<Wallet>, amount: u64) -> anyhow::Result<Amount> {
let desired_amount = Amount::from(amount);
let quote = wallet.mint_quote(desired_amount, None).await?;

loop {
let status = wallet.mint_quote_state(&quote.id).await?;
if status.state == MintQuoteState::Paid {
break;
}
}

wallet
.mint(&quote.id, SplitTarget::default(), None)
.await
.map_err(Into::into)
}

mod nut03 {
use cdk::nuts::nut00::ProofsMethods;
use cdk::wallet::SendKind;

use crate::integration_tests_pure::*;

#[tokio::test]
async fn test_swap_to_send() -> anyhow::Result<()> {
let mint_bob = create_and_start_test_mint().await?;
let wallet_alice = create_test_wallet_for_mint(mint_bob.clone())?;

// Alice gets 64 sats
receive(wallet_alice.clone(), 64).await?;
let balance_alice = wallet_alice.total_balance().await?;
assert_eq!(Amount::from(64), balance_alice);

// Alice wants to send 40 sats, which internally swaps
let token = wallet_alice
.send(
Amount::from(40),
None,
None,
&SplitTarget::None,
&SendKind::OnlineExact,
false,
)
.await?;
assert_eq!(Amount::from(40), token.proofs().total_amount()?);
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())?;
let received_amount = wallet_carol
.receive_proofs(token.proofs(), SplitTarget::None, &[], &[])
.await?;

assert_eq!(Amount::from(40), received_amount);
assert_eq!(Amount::from(40), wallet_carol.total_balance().await?);

Ok(())
}
}
4 changes: 2 additions & 2 deletions crates/cdk-integration-tests/tests/regtest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,8 +398,8 @@ async fn test_cached_mint() -> Result<()> {

request.sign(secret_key.expect("Secret key on quote"))?;

let response = http_client.post_mint(request.clone()).await?;
let response1 = http_client.post_mint(request).await?;
let response = http_client.post_mint(request.clone(), None).await?;
let response1 = http_client.post_mint(request, None).await?;

assert!(response == response1);
Ok(())
Expand Down

0 comments on commit c74b98e

Please sign in to comment.