Skip to main content

mostro_core/
lib.rs

1pub mod dispute;
2pub mod error;
3pub mod message;
4pub mod order;
5pub mod prelude;
6pub mod rating;
7pub mod user;
8#[cfg(test)]
9mod test {
10    use crate::error::CantDoReason;
11    use crate::message::{Action, Message, MessageKind, Payload, Peer};
12    use crate::order::{Kind, SmallOrder, Status};
13    use nostr_sdk::Keys;
14    use uuid::uuid;
15
16    #[test]
17    fn test_status_string() {
18        assert_eq!(Status::Active.to_string(), "active");
19        assert_eq!(Status::CompletedByAdmin.to_string(), "completed-by-admin");
20        assert_eq!(Status::FiatSent.to_string(), "fiat-sent");
21        assert_ne!(Status::Pending.to_string(), "Pending");
22    }
23
24    #[test]
25    fn test_kind_string() {
26        assert_ne!(Kind::Sell.to_string(), "active");
27        assert_eq!(Kind::Sell.to_string(), "sell");
28        assert_eq!(Kind::Buy.to_string(), "buy");
29        assert_ne!(Kind::Buy.to_string(), "active");
30    }
31
32    #[test]
33    fn test_order_message() {
34        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
35        let payment_methods = "SEPA,Bank transfer".to_string();
36        let payload = Payload::Order(SmallOrder::new(
37            Some(uuid),
38            Some(Kind::Sell),
39            Some(Status::Pending),
40            100,
41            "eur".to_string(),
42            None,
43            None,
44            100,
45            payment_methods,
46            1,
47            None,
48            None,
49            None,
50            Some(1627371434),
51            None,
52        ));
53
54        let test_message = Message::Order(MessageKind::new(
55            Some(uuid),
56            Some(1),
57            Some(2),
58            Action::NewOrder,
59            Some(payload),
60        ));
61        let test_message_json = test_message.as_json().unwrap();
62        let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":2,"action":"new-order","payload":{"order":{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"pending","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"SEPA,Bank transfer","premium":1,"created_at":1627371434}}}}"#;
63        let message = Message::from_json(sample_message).unwrap();
64        assert!(message.verify());
65        let message_json = message.as_json().unwrap();
66        assert_eq!(message_json, test_message_json);
67    }
68
69    #[test]
70    fn test_payment_request_payload_message() {
71        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
72        let test_message = Message::Order(MessageKind::new(
73            Some(uuid),
74            Some(1),
75            Some(3),
76            Action::PayInvoice,
77            Some(Payload::PaymentRequest(
78                Some(SmallOrder::new(
79                    Some(uuid),
80                    Some(Kind::Sell),
81                    Some(Status::WaitingPayment),
82                    100,
83                    "eur".to_string(),
84                    None,
85                    None,
86                    100,
87                    "Face to face".to_string(),
88                    1,
89                    None,
90                    None,
91                    None,
92                    Some(1627371434),
93                    None,
94                )),
95                "lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e".to_string(),
96                None,
97            )),
98        ));
99        let sample_message = r#"{"order":{"version":1,"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","request_id":1,"trade_index":3,"action":"pay-invoice","payload":{"payment_request":[{"id":"308e1272-d5f4-47e6-bd97-3504baea9c23","kind":"sell","status":"waiting-payment","amount":100,"fiat_code":"eur","fiat_amount":100,"payment_method":"Face to face","premium":1,"created_at":1627371434},"lnbcrt78510n1pj59wmepp50677g8tffdqa2p8882y0x6newny5vtz0hjuyngdwv226nanv4uzsdqqcqzzsxqyz5vqsp5skn973360gp4yhlpmefwvul5hs58lkkl3u3ujvt57elmp4zugp4q9qyyssqw4nzlr72w28k4waycf27qvgzc9sp79sqlw83j56txltz4va44j7jda23ydcujj9y5k6k0rn5ms84w8wmcmcyk5g3mhpqepf7envhdccp72nz6e",null]}}}"#;
100        let message = Message::from_json(sample_message).unwrap();
101        assert!(message.verify());
102        let message_json = message.as_json().unwrap();
103        let test_message_json = test_message.as_json().unwrap();
104        assert_eq!(message_json, test_message_json);
105    }
106
107    #[test]
108    fn test_message_payload_signature() {
109        let uuid = uuid!("308e1272-d5f4-47e6-bd97-3504baea9c23");
110        let peer = Peer::new(
111            "npub1testjsf0runcqdht5apkfcalajxkf8txdxqqk5kgm0agc38ke4vsfsgzf8".to_string(),
112            None,
113        );
114        let payload = Payload::Peer(peer);
115        let test_message = Message::Order(MessageKind::new(
116            Some(uuid),
117            Some(1),
118            Some(2),
119            Action::FiatSentOk,
120            Some(payload),
121        ));
122        assert!(test_message.verify());
123        let test_message_json = test_message.as_json().unwrap();
124        // Message should be signed with the trade keys
125        let trade_keys =
126            Keys::parse("110e43647eae221ab1da33ddc17fd6ff423f2b2f49d809b9ffa40794a2ab996c")
127                .unwrap();
128        let sig = Message::sign(test_message_json.clone(), &trade_keys);
129
130        assert!(Message::verify_signature(
131            test_message_json,
132            trade_keys.public_key(),
133            sig
134        ));
135    }
136
137    #[test]
138    fn test_cant_do_message_serialization() {
139        // Test all CantDoReason variants
140        let reasons = vec![
141            CantDoReason::InvalidSignature,
142            CantDoReason::InvalidTradeIndex,
143            CantDoReason::InvalidAmount,
144            CantDoReason::InvalidInvoice,
145            CantDoReason::InvalidPaymentRequest,
146            CantDoReason::InvalidPeer,
147            CantDoReason::InvalidRating,
148            CantDoReason::InvalidTextMessage,
149            CantDoReason::InvalidOrderStatus,
150            CantDoReason::InvalidPubkey,
151            CantDoReason::InvalidParameters,
152            CantDoReason::OrderAlreadyCanceled,
153            CantDoReason::CantCreateUser,
154            CantDoReason::IsNotYourOrder,
155            CantDoReason::NotAllowedByStatus,
156            CantDoReason::OutOfRangeFiatAmount,
157            CantDoReason::OutOfRangeSatsAmount,
158            CantDoReason::IsNotYourDispute,
159            CantDoReason::NotFound,
160            CantDoReason::InvalidFiatCurrency,
161            CantDoReason::TooManyRequests,
162        ];
163
164        for reason in reasons {
165            let cant_do = Message::CantDo(MessageKind::new(
166                None,
167                None,
168                None,
169                Action::CantDo,
170                Some(Payload::CantDo(Some(reason.clone()))),
171            ));
172            let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
173            assert!(message.verify());
174            assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
175        }
176
177        // Test None case
178        let cant_do = Message::CantDo(MessageKind::new(
179            None,
180            None,
181            None,
182            Action::CantDo,
183            Some(Payload::CantDo(None)),
184        ));
185        let message = Message::from_json(&cant_do.as_json().unwrap()).unwrap();
186        assert!(message.verify());
187        assert_eq!(message.as_json().unwrap(), cant_do.as_json().unwrap());
188    }
189}