Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion payjoin-test-utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ pub async fn wait_for_service_ready(
}

pub static EXAMPLE_URL: Lazy<Url> =
Lazy::new(|| Url::parse("https://relay.com").expect("invalid URL"));
Lazy::new(|| Url::parse("https://example.com").expect("invalid URL"));

pub const KEY_ID: KeyId = 1;
pub const KEM: Kem = Kem::K256Sha256;
Expand Down
44 changes: 42 additions & 2 deletions payjoin/src/uri/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,8 @@ impl bitcoin_uri::de::DeserializationState<'_> for DeserializationState {
mod tests {
use std::convert::TryFrom;

use bitcoin_uri::SerializeParams;

use super::*;

#[test]
Expand Down Expand Up @@ -269,10 +271,48 @@ mod tests {

#[test]
fn test_unsupported() {
assert!(!Uri::try_from("bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX")
assert!(
!Uri::try_from("bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX")
.unwrap()
.extras
.pj_is_supported(),
"Uri expected a failure with missing pj extras, but it succeeded"
);
}

#[test]
fn test_supported() {
assert!(
Uri::try_from(
"bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?amount=0.01\
&pjos=0&pj=HTTPS://EXAMPLE.COM/\
%23OH1QYPM5JXYNS754Y4R45QWE336QFX6ZR8DQGVQCULVZTV20TFVEYDMFQC"
)
.unwrap()
.extras
.pj_is_supported());
.pj_is_supported(),
"Uri expected a success with a well formatted pj extras, but it failed"
);
}

#[test]
fn test_pj_param_unknown() {
use bitcoin_uri::de::DeserializationState as _;
let uri = "bitcoin:12c6DSiU4Rq3P4ZxziKxzrL5LmMBrzjrJX?pjos=1&pj=HTTPS://EXAMPLE.COM/\
%23OH1QYPM5JXYNS754Y4R45QWE336QFX6ZR8DQGVQCULVZTV20TFVEYDMFQC";
let pjuri = Uri::try_from(uri).unwrap().assume_checked().check_pj_supported().unwrap();
let serialized_params = pjuri.extras.serialize_params();
let pjos_key = serialized_params.clone().next().expect("Missing pjos key").0;
let pj_key = serialized_params.clone().next().expect("Missing pj key").0;

let state = DeserializationState::default();

assert!(state.is_param_known(pjos_key), "The pjos key should match 'pjos', but it failed");
assert!(state.is_param_known(pj_key), "The pj key should match 'pj', but it failed");
assert!(
!state.is_param_known("unknown_param"),
"An unknown_param should not match 'pj' or 'pjos'"
);
}

#[test]
Expand Down
53 changes: 45 additions & 8 deletions payjoin/src/uri/url_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,26 +225,29 @@ impl std::error::Error for ParseReceiverPubkeyParamError {

#[cfg(test)]
mod tests {
use payjoin_test_utils::BoxError;
use payjoin_test_utils::{BoxError, EXAMPLE_URL};

use super::*;
use crate::{Uri, UriExt};

#[test]
fn test_ohttp_get_set() {
let mut url = Url::parse("https://example.com").unwrap();
let mut url = EXAMPLE_URL.clone();

let serialized = "OH1QYPM5JXYNS754Y4R45QWE336QFX6ZR8DQGVQCULVZTV20TFVEYDMFQC";
let ohttp_keys = OhttpKeys::from_str(serialized).unwrap();
url.set_ohttp(ohttp_keys.clone());

assert_eq!(url.fragment(), Some(serialized));
assert_eq!(url.ohttp().unwrap(), ohttp_keys);
assert_eq!(
url.ohttp().expect("Ohttp keys have been set but are missing on get"),
ohttp_keys
);
}

#[test]
fn test_errors_when_parsing_ohttp() {
let missing_ohttp_url = Url::parse("https://example.com").unwrap();
let missing_ohttp_url = EXAMPLE_URL.clone();
assert!(matches!(
missing_ohttp_url.ohttp(),
Err(ParseOhttpKeysParamError::MissingOhttpKeys)
Expand All @@ -261,19 +264,19 @@ mod tests {

#[test]
fn test_exp_get_set() {
let mut url = Url::parse("https://example.com").unwrap();
let mut url = EXAMPLE_URL.clone();

let exp_time =
std::time::SystemTime::UNIX_EPOCH + std::time::Duration::from_secs(1720547781);
url.set_exp(exp_time);
assert_eq!(url.fragment(), Some("EX1C4UC6ES"));

assert_eq!(url.exp().unwrap(), exp_time);
assert_eq!(url.exp().expect("Expiry has been set but is missing on get"), exp_time);
}

#[test]
fn test_errors_when_parsing_exp() {
let missing_exp_url = Url::parse("http://example.com").unwrap();
let missing_exp_url = EXAMPLE_URL.clone();
assert!(matches!(missing_exp_url.exp(), Err(ParseExpParamError::MissingExp)));

let invalid_bech32_exp_url =
Expand All @@ -290,7 +293,41 @@ mod tests {
// Not enough data to decode into a u32
let invalid_timestamp_exp_url =
Url::parse("http://example.com?pj=https://test-payjoin-url#EX10").unwrap();
assert!(matches!(invalid_timestamp_exp_url.exp(), Err(ParseExpParamError::InvalidExp(_))))
assert!(matches!(invalid_timestamp_exp_url.exp(), Err(ParseExpParamError::InvalidExp(_))));
}

Comment thread
benalleng marked this conversation as resolved.
#[test]
fn test_errors_when_parsing_receiver_pubkey() {
let missing_receiver_pubkey_url = EXAMPLE_URL.clone();
assert!(matches!(
missing_receiver_pubkey_url.receiver_pubkey(),
Err(ParseReceiverPubkeyParamError::MissingPubkey)
));

let invalid_bech32_receiver_pubkey_url =
Url::parse("http://example.com?pj=https://test-payjoin-url#RK1invalid_bech_32")
.unwrap();
assert!(matches!(
invalid_bech32_receiver_pubkey_url.receiver_pubkey(),
Err(ParseReceiverPubkeyParamError::DecodeBech32(_))
));

// Since the HRP is everything to the left of the right-most separator, the invalid url in
// this test would have it's HRP being parsed as RK101 instead of the expected RK1
let invalid_hrp_receiver_pubkey_url =
Url::parse("http://example.com?pj=https://test-payjoin-url#RK101").unwrap();
assert!(matches!(
invalid_hrp_receiver_pubkey_url.receiver_pubkey(),
Err(ParseReceiverPubkeyParamError::InvalidHrp(_))
));

// Not enough data to decode into a u32
let invalid_receiver_pubkey_url =
Url::parse("http://example.com?pj=https://test-payjoin-url#RK10").unwrap();
assert!(matches!(
invalid_receiver_pubkey_url.receiver_pubkey(),
Err(ParseReceiverPubkeyParamError::InvalidPubkey(_))
));
}

#[test]
Expand Down