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