+
+ #[test]
+ fn fails_requesting_invoice_with_unknown_required_features() {
+ match OfferBuilder::new("foo".into(), pubkey(42))
+ .features_unchecked(OfferFeatures::unknown())
+ .build().unwrap()
+ .request_invoice(vec![1; 32], pubkey(43))
+ {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, SemanticError::UnknownRequiredFeatures),
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_chains() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .chain(Network::Bitcoin)
+ .chain(Network::Testnet)
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_amount() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .amount(Amount::Bitcoin { amount_msats: 1000 })
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let mut tlv_stream = offer.as_tlv_stream();
+ tlv_stream.amount = Some(1000);
+ tlv_stream.currency = Some(b"USD");
+
+ let mut encoded_offer = Vec::new();
+ tlv_stream.write(&mut encoded_offer).unwrap();
+
+ if let Err(e) = Offer::try_from(encoded_offer) {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let mut tlv_stream = offer.as_tlv_stream();
+ tlv_stream.amount = None;
+ tlv_stream.currency = Some(b"USD");
+
+ let mut encoded_offer = Vec::new();
+ tlv_stream.write(&mut encoded_offer).unwrap();
+
+ match Offer::try_from(encoded_offer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingAmount)),
+ }
+
+ let mut tlv_stream = offer.as_tlv_stream();
+ tlv_stream.amount = Some(MAX_VALUE_MSAT + 1);
+ tlv_stream.currency = None;
+
+ let mut encoded_offer = Vec::new();
+ tlv_stream.write(&mut encoded_offer).unwrap();
+
+ match Offer::try_from(encoded_offer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::InvalidSemantics(SemanticError::InvalidAmount)),
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_description() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let mut tlv_stream = offer.as_tlv_stream();
+ tlv_stream.description = None;
+
+ let mut encoded_offer = Vec::new();
+ tlv_stream.write(&mut encoded_offer).unwrap();
+
+ match Offer::try_from(encoded_offer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingDescription));
+ },
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_paths() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .path(BlindedPath {
+ introduction_node_id: pubkey(40),
+ blinding_point: pubkey(41),
+ blinded_hops: vec![
+ BlindedHop { blinded_node_id: pubkey(43), encrypted_payload: vec![0; 43] },
+ BlindedHop { blinded_node_id: pubkey(44), encrypted_payload: vec![0; 44] },
+ ],
+ })
+ .path(BlindedPath {
+ introduction_node_id: pubkey(40),
+ blinding_point: pubkey(41),
+ blinded_hops: vec![
+ BlindedHop { blinded_node_id: pubkey(45), encrypted_payload: vec![0; 45] },
+ BlindedHop { blinded_node_id: pubkey(46), encrypted_payload: vec![0; 46] },
+ ],
+ })
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let mut builder = OfferBuilder::new("foo".into(), pubkey(42));
+ builder.offer.paths = Some(vec![]);
+
+ let offer = builder.build().unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_quantity() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .supported_quantity(Quantity::One)
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .supported_quantity(Quantity::Unbounded)
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .supported_quantity(Quantity::Bounded(NonZeroU64::new(10).unwrap()))
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let offer = OfferBuilder::new("foo".into(), pubkey(42))
+ .supported_quantity(Quantity::Bounded(NonZeroU64::new(1).unwrap()))
+ .build()
+ .unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+ }
+
+ #[test]
+ fn parses_offer_with_node_id() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+ if let Err(e) = offer.to_string().parse::<Offer>() {
+ panic!("error parsing offer: {:?}", e);
+ }
+
+ let mut tlv_stream = offer.as_tlv_stream();
+ tlv_stream.node_id = None;
+
+ let mut encoded_offer = Vec::new();
+ tlv_stream.write(&mut encoded_offer).unwrap();
+
+ match Offer::try_from(encoded_offer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => {
+ assert_eq!(e, ParseError::InvalidSemantics(SemanticError::MissingSigningPubkey));
+ },
+ }
+ }
+
+ #[test]
+ fn fails_parsing_offer_with_extra_tlv_records() {
+ let offer = OfferBuilder::new("foo".into(), pubkey(42)).build().unwrap();
+
+ let mut encoded_offer = Vec::new();
+ offer.write(&mut encoded_offer).unwrap();
+ BigSize(80).write(&mut encoded_offer).unwrap();
+ BigSize(32).write(&mut encoded_offer).unwrap();
+ [42u8; 32].write(&mut encoded_offer).unwrap();
+
+ match Offer::try_from(encoded_offer) {
+ Ok(_) => panic!("expected error"),
+ Err(e) => assert_eq!(e, ParseError::Decode(DecodeError::InvalidValue)),
+ }
+ }