Skip to content

Commit

Permalink
Merge pull request #15 from MutinyWallet/zap-tests
Browse files Browse the repository at this point in the history
Fix zaps database error
  • Loading branch information
TonyGiorgio authored Apr 4, 2024
2 parents 9eb8e46 + 4e8ea17 commit 949155e
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 31 deletions.
6 changes: 3 additions & 3 deletions src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use mockall::{automock, predicate::*};
use crate::models::{
app_user::{AppUser, NewAppUser},
invoice::{Invoice, NewInvoice},
zaps::{NewZap, Zap},
zaps::Zap,
};

#[cfg_attr(test, automock)]
Expand All @@ -23,7 +23,7 @@ pub(crate) trait DBConnection {
fn get_user_by_name(&self, name: String) -> anyhow::Result<Option<AppUser>>;
fn get_user_by_id(&self, id: i32) -> anyhow::Result<Option<AppUser>>;
fn get_user_and_increment_counter(&self, name: &str) -> anyhow::Result<Option<AppUser>>;
fn insert_new_zap(&self, new_zap: NewZap) -> anyhow::Result<Zap>;
fn insert_new_zap(&self, new_zap: Zap) -> anyhow::Result<Zap>;
fn get_zap_by_id(&self, id: i32) -> anyhow::Result<Option<Zap>>;
fn set_zap_event_id(&self, zap: Zap, event_id: String) -> anyhow::Result<()>;
}
Expand Down Expand Up @@ -91,7 +91,7 @@ impl DBConnection for PostgresConnection {
invoice.set_state(conn, s)
}

fn insert_new_zap(&self, new_zap: NewZap) -> anyhow::Result<Zap> {
fn insert_new_zap(&self, new_zap: Zap) -> anyhow::Result<Zap> {
let conn = &mut self.db.get()?;
new_zap.insert(conn)
}
Expand Down
220 changes: 214 additions & 6 deletions src/lnurlp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::str::FromStr;
use crate::{
invoice::{spawn_invoice_subscription, InvoiceState},
mint::select_gateway,
models::{invoice::NewInvoice, zaps::NewZap},
models::{invoice::NewInvoice, zaps::Zap},
routes::{LnurlCallbackParams, LnurlCallbackResponse, LnurlVerifyResponse},
State,
};
Expand Down Expand Up @@ -137,7 +137,8 @@ pub async fn lnurl_callback(

// save nostr zap request
if let Some(request) = params.nostr {
let new_zap = NewZap {
let new_zap = Zap {
id: created_invoice.id,
request,
event_id: None,
};
Expand Down Expand Up @@ -195,17 +196,24 @@ pub async fn verify(

#[cfg(all(test, feature = "integration-tests"))]
mod tests_integration {
use nostr::{key::FromSkStr, Keys};
use fedimint_core::api::InviteCode;
use nostr::prelude::{rand, ZapRequestData};
use nostr::{key::FromSkStr, EventBuilder, Keys};
use secp256k1::Secp256k1;
use std::path::PathBuf;
use std::sync::Arc;

use crate::mint::setup_multimint;
use crate::register::generate_random_name;
use crate::{
db::setup_db, lnurlp::well_known_lnurlp, mint::MockMultiMintWrapperTrait,
models::app_user::NewAppUser, register::BlindSigner, State,
db::setup_db, lnurlp::*, mint::MockMultiMintWrapperTrait, models::app_user::NewAppUser,
register::BlindSigner,
};

const INVITE_CODE: &str = "fed11qgqzc2nhwden5te0vejkg6tdd9h8gepwvejkg6tdd9h8garhduhx6at5d9h8jmn9wshxxmmd9uqqzgxg6s3evnr6m9zdxr6hxkdkukexpcs3mn7mj3g5pc5dfh63l4tj6g9zk4er";

#[tokio::test]
pub async fn well_known_nip5_lookup_test() {
pub async fn well_known_lnurlp_lookup_test() {
dotenv::dotenv().ok();
let pg_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db = setup_db(pg_url);
Expand Down Expand Up @@ -257,4 +265,204 @@ mod tests_integration {
Err(e) => panic!("shouldn't error: {e}"),
}
}

#[tokio::test]
pub async fn amt_callback_test() {
dotenv::dotenv().ok();
let pg_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db = setup_db(pg_url);

// nostr
let nostr_nsec_str = std::env::var("NSEC").expect("FM_DB_PATH must be set");
let nostr_sk = Keys::from_sk_str(&nostr_nsec_str).expect("Invalid NOSTR_SK");
let nostr = nostr_sdk::Client::new(&nostr_sk);

// create blind signer
let free_signer = BlindSigner::derive(&[0u8; 32], 0, 0);
let paid_signer = BlindSigner::derive(&[0u8; 32], 0, 0);

// swap out fm with a mock here since that's not what is being tested
let mock_mm = Arc::new(MockMultiMintWrapperTrait::new());

let state = State {
db: db.clone(),
mm: mock_mm,
secp: Secp256k1::new(),
nostr,
free_pk: free_signer.pk,
paid_pk: paid_signer.pk,
domain: "http://hello.com".to_string(),
};

let invite_code = InviteCode::from_str(INVITE_CODE).unwrap();
// generate random username and key
let username = generate_random_name(&state).unwrap();
let pk = Keys::generate().public_key();
let user = NewAppUser {
pubkey: pk.to_string(),
name: username.clone(),
federation_id: invite_code.federation_id().to_string(),
unblinded_msg: pk.to_string(),
federation_invite_code: INVITE_CODE.to_string(),
};

state.db.insert_new_user(user).unwrap();

let params = LnurlCallbackParams {
amount: 1,
..Default::default()
};

match lnurl_callback(&state, username.clone(), params).await {
Ok(_) => panic!("unexpected ok"),
Err(e) => assert!(e.to_string().contains("MIN_AMOUNT")),
}

let params = LnurlCallbackParams {
amount: u64::MAX,
..Default::default()
};

match lnurl_callback(&state, username, params).await {
Ok(_) => panic!("unexpected ok"),
Err(e) => assert!(e.to_string().contains("MAX_AMOUNT")),
}
}

#[tokio::test(flavor = "multi_thread")]
pub async fn callback_test() {
dotenv::dotenv().ok();
let pg_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db = setup_db(pg_url);

// nostr
let nostr_nsec_str = std::env::var("NSEC").expect("FM_DB_PATH must be set");
let nostr_sk = Keys::from_sk_str(&nostr_nsec_str).expect("Invalid NOSTR_SK");
let nostr = nostr_sdk::Client::new(&nostr_sk);

// create blind signer
let free_signer = BlindSigner::derive(&[0u8; 32], 0, 0);
let paid_signer = BlindSigner::derive(&[0u8; 32], 0, 0);

// generate random tmp db path
let tmp_db_path = format!("/tmp/test-{}.db", rand::random::<u64>());

let mm = setup_multimint(PathBuf::from_str(&tmp_db_path).unwrap())
.await
.unwrap();

let state = State {
db: db.clone(),
mm,
secp: Secp256k1::new(),
nostr,
free_pk: free_signer.pk,
paid_pk: paid_signer.pk,
domain: "http://hello.com".to_string(),
};

let invite_code = InviteCode::from_str(INVITE_CODE).unwrap();
// generate random username and key
let username = generate_random_name(&state).unwrap();
let pk = Keys::generate().public_key();
let user = NewAppUser {
pubkey: pk.to_string(),
name: username.clone(),
federation_id: invite_code.federation_id().to_string(),
unblinded_msg: pk.to_string(),
federation_invite_code: INVITE_CODE.to_string(),
};

state.mm.register_new_federation(invite_code).await.unwrap();

state.db.insert_new_user(user).unwrap();

let params = LnurlCallbackParams {
amount: 10_000,
nonce: None,
comment: None,
proofofpayer: None,
nostr: None,
};

match lnurl_callback(&state, username, params).await {
Ok(result) => {
assert_eq!(result.status, LnurlStatus::Ok);
assert!(!result.pr.is_empty());
}
Err(e) => panic!("shouldn't error: {e}"),
}
}

#[tokio::test(flavor = "multi_thread")]
pub async fn callback_with_zap_test() {
dotenv::dotenv().ok();
let pg_url = std::env::var("DATABASE_URL").expect("DATABASE_URL must be set");
let db = setup_db(pg_url);

// nostr
let nostr_nsec_str = std::env::var("NSEC").expect("FM_DB_PATH must be set");
let nostr_sk = Keys::from_sk_str(&nostr_nsec_str).expect("Invalid NOSTR_SK");
let nostr = nostr_sdk::Client::new(&nostr_sk);

// create blind signer
let free_signer = BlindSigner::derive(&[0u8; 32], 0, 0);
let paid_signer = BlindSigner::derive(&[0u8; 32], 0, 0);

// generate random tmp db path
let tmp_db_path = format!("/tmp/test-{}.db", rand::random::<u64>());

let mm = setup_multimint(PathBuf::from_str(&tmp_db_path).unwrap())
.await
.unwrap();

let state = State {
db: db.clone(),
mm,
secp: Secp256k1::new(),
nostr,
free_pk: free_signer.pk,
paid_pk: paid_signer.pk,
domain: "http://hello.com".to_string(),
};

let invite_code = InviteCode::from_str(INVITE_CODE).unwrap();
// generate random username and key
let username = generate_random_name(&state).unwrap();
let pk = Keys::generate().public_key();
let user = NewAppUser {
pubkey: pk.to_string(),
name: username.clone(),
federation_id: invite_code.federation_id().to_string(),
unblinded_msg: pk.to_string(),
federation_invite_code: INVITE_CODE.to_string(),
};

state.mm.register_new_federation(invite_code).await.unwrap();

state.db.insert_new_user(user).unwrap();

let zap_request = {
let data = ZapRequestData::new(pk, vec![]);
EventBuilder::new_zap_request(data)
.to_event(&Keys::generate())
.unwrap()
};

let params = LnurlCallbackParams {
amount: 10_000,
nonce: None,
comment: None,
proofofpayer: None,
nostr: Some(zap_request.as_json()),
};

match lnurl_callback(&state, username, params).await {
Ok(result) => {
assert_eq!(result.status, LnurlStatus::Ok);
assert!(!result.pr.is_empty());
}
Err(e) => panic!("shouldn't error: {e}"),
}
}
}
23 changes: 4 additions & 19 deletions src/models/zaps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ pub struct Zap {
}

impl Zap {
pub fn insert(&self, conn: &mut PgConnection) -> anyhow::Result<()> {
diesel::insert_into(zaps::table)
pub fn insert(&self, conn: &mut PgConnection) -> anyhow::Result<Zap> {
let res = diesel::insert_into(zaps::table)
.values(self)
.execute(conn)?;
.get_result(conn)?;

Ok(())
Ok(res)
}

pub fn get_zaps(conn: &mut PgConnection) -> anyhow::Result<Vec<Zap>> {
Expand All @@ -51,18 +51,3 @@ impl Zap {
}
}

#[derive(Insertable)]
#[diesel(table_name = zaps)]
pub struct NewZap {
pub request: String,
pub event_id: Option<String>,
}

impl NewZap {
pub fn insert(&self, conn: &mut PgConnection) -> anyhow::Result<Zap> {
diesel::insert_into(zaps::table)
.values(self)
.get_result::<Zap>(conn)
.map_err(|e| e.into())
}
}
6 changes: 3 additions & 3 deletions src/routes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,13 @@ pub async fn well_known_nip5_route(
}
}

#[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum LnurlType {
PayRequest,
}

#[derive(Serialize, Deserialize)]
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Ord, PartialOrd, Eq, PartialEq)]
#[serde(rename_all = "UPPERCASE")]
pub enum LnurlStatus {
Ok,
Expand Down Expand Up @@ -144,7 +144,7 @@ pub async fn well_known_lnurlp_route(
}
}

#[derive(Serialize, Deserialize)]
#[derive(Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
pub struct LnurlCallbackParams {
pub amount: u64, // User specified amount in MilliSatoshi
Expand Down

0 comments on commit 949155e

Please sign in to comment.