mostro_core/
lib.rs

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