gift_wrap.rs (8222B)
1 #[path = "../src/test_fixtures.rs"] 2 mod test_fixtures; 3 4 use radroots_events::gift_wrap::{RadrootsGiftWrap, RadrootsGiftWrapRecipient}; 5 use radroots_events::kinds::{KIND_GIFT_WRAP, KIND_MESSAGE}; 6 7 use radroots_events_codec::error::{EventEncodeError, EventParseError}; 8 use radroots_events_codec::gift_wrap::decode::{ 9 data_from_event, gift_wrap_from_tags, parsed_from_event, 10 }; 11 use radroots_events_codec::gift_wrap::encode::{ 12 gift_wrap_build_tags, to_wire_parts, to_wire_parts_with_kind, 13 }; 14 use test_fixtures::RELAY_PRIMARY_WSS; 15 16 fn sample_gift_wrap() -> RadrootsGiftWrap { 17 RadrootsGiftWrap { 18 recipient: RadrootsGiftWrapRecipient { 19 public_key: "pubkey".to_string(), 20 relay_url: Some(RELAY_PRIMARY_WSS.to_string()), 21 }, 22 content: "encrypted".to_string(), 23 expiration: Some(1700000000), 24 } 25 } 26 27 #[test] 28 fn gift_wrap_build_tags_requires_recipient() { 29 let mut gift_wrap = sample_gift_wrap(); 30 gift_wrap.recipient.public_key = " ".to_string(); 31 32 let err = gift_wrap_build_tags(&gift_wrap).unwrap_err(); 33 assert!(matches!( 34 err, 35 EventEncodeError::EmptyRequiredField("recipient.public_key") 36 )); 37 } 38 39 #[test] 40 fn gift_wrap_to_wire_parts_sets_kind_content_and_tags() { 41 let gift_wrap = sample_gift_wrap(); 42 let parts = to_wire_parts(&gift_wrap).unwrap(); 43 44 assert_eq!(parts.kind, KIND_GIFT_WRAP); 45 assert_eq!(parts.content, "encrypted"); 46 assert_eq!( 47 parts.tags, 48 vec![ 49 vec![ 50 "p".to_string(), 51 "pubkey".to_string(), 52 RELAY_PRIMARY_WSS.to_string() 53 ], 54 vec!["expiration".to_string(), "1700000000".to_string()], 55 ] 56 ); 57 } 58 59 #[test] 60 fn gift_wrap_from_tags_rejects_wrong_kind() { 61 let gift_wrap = sample_gift_wrap(); 62 let parts = to_wire_parts(&gift_wrap).unwrap(); 63 64 let err = gift_wrap_from_tags(KIND_MESSAGE, &parts.tags, &parts.content).unwrap_err(); 65 assert!(matches!( 66 err, 67 EventParseError::InvalidKind { 68 expected: "1059", 69 got: KIND_MESSAGE 70 } 71 )); 72 } 73 74 #[test] 75 fn gift_wrap_from_tags_requires_p_tag() { 76 let err = gift_wrap_from_tags(KIND_GIFT_WRAP, &[], "payload").unwrap_err(); 77 assert!(matches!(err, EventParseError::MissingTag("p"))); 78 79 let err = gift_wrap_from_tags(KIND_GIFT_WRAP, &[vec!["p".to_string()]], "payload").unwrap_err(); 80 assert!(matches!(err, EventParseError::InvalidTag("p"))); 81 } 82 83 #[test] 84 fn gift_wrap_from_tags_rejects_invalid_expiration_and_relay() { 85 let err = gift_wrap_from_tags( 86 KIND_GIFT_WRAP, 87 &[ 88 vec![ 89 "p".to_string(), 90 "pubkey".to_string(), 91 RELAY_PRIMARY_WSS.to_string(), 92 ], 93 vec!["expiration".to_string(), " ".to_string()], 94 ], 95 "payload", 96 ) 97 .unwrap_err(); 98 assert!(matches!(err, EventParseError::InvalidTag("expiration"))); 99 100 let err = gift_wrap_from_tags( 101 KIND_GIFT_WRAP, 102 &[ 103 vec![ 104 "p".to_string(), 105 "pubkey".to_string(), 106 RELAY_PRIMARY_WSS.to_string(), 107 ], 108 vec!["expiration".to_string(), "invalid".to_string()], 109 ], 110 "payload", 111 ) 112 .unwrap_err(); 113 assert!(matches!( 114 err, 115 EventParseError::InvalidNumber("expiration", _) 116 )); 117 118 let err = gift_wrap_from_tags( 119 KIND_GIFT_WRAP, 120 &[vec!["p".to_string(), "pubkey".to_string(), " ".to_string()]], 121 "payload", 122 ) 123 .unwrap_err(); 124 assert!(matches!(err, EventParseError::InvalidTag("p"))); 125 } 126 127 #[test] 128 fn gift_wrap_metadata_and_index_from_event_roundtrip() { 129 let gift_wrap = sample_gift_wrap(); 130 let parts = to_wire_parts(&gift_wrap).unwrap(); 131 132 let metadata = data_from_event( 133 "id".to_string(), 134 "author".to_string(), 135 11, 136 parts.kind, 137 parts.content.clone(), 138 parts.tags.clone(), 139 ) 140 .unwrap(); 141 assert_eq!(metadata.id, "id"); 142 assert_eq!(metadata.author, "author"); 143 assert_eq!(metadata.published_at, 11); 144 assert_eq!( 145 metadata.data.recipient.public_key, 146 gift_wrap.recipient.public_key 147 ); 148 assert_eq!( 149 metadata.data.recipient.relay_url, 150 gift_wrap.recipient.relay_url 151 ); 152 assert_eq!(metadata.data.content, gift_wrap.content); 153 assert_eq!(metadata.data.expiration, gift_wrap.expiration); 154 155 let index = parsed_from_event( 156 "id".to_string(), 157 "author".to_string(), 158 11, 159 parts.kind, 160 parts.content, 161 parts.tags, 162 "sig".to_string(), 163 ) 164 .unwrap(); 165 assert_eq!(index.event.kind, KIND_GIFT_WRAP); 166 assert_eq!(index.event.sig, "sig"); 167 assert_eq!(index.data.data.recipient.public_key, "pubkey"); 168 } 169 170 #[test] 171 fn gift_wrap_build_tags_handles_optional_expiration_and_invalid_relay() { 172 let mut gift_wrap = sample_gift_wrap(); 173 gift_wrap.expiration = None; 174 let tags = gift_wrap_build_tags(&gift_wrap).unwrap(); 175 assert_eq!( 176 tags, 177 vec![vec![ 178 "p".to_string(), 179 "pubkey".to_string(), 180 RELAY_PRIMARY_WSS.to_string() 181 ]] 182 ); 183 184 let mut gift_wrap = sample_gift_wrap(); 185 gift_wrap.recipient.relay_url = Some(" ".to_string()); 186 let err = gift_wrap_build_tags(&gift_wrap).unwrap_err(); 187 assert!(matches!( 188 err, 189 EventEncodeError::EmptyRequiredField("recipient.relay_url") 190 )); 191 192 let mut gift_wrap = sample_gift_wrap(); 193 gift_wrap.recipient.relay_url = None; 194 let tags = gift_wrap_build_tags(&gift_wrap).unwrap(); 195 assert_eq!(tags[0], vec!["p".to_string(), "pubkey".to_string()]); 196 } 197 198 #[test] 199 fn gift_wrap_to_wire_parts_requires_content_and_accepts_default_kind() { 200 let mut gift_wrap = sample_gift_wrap(); 201 gift_wrap.content = " ".to_string(); 202 let err = to_wire_parts(&gift_wrap).unwrap_err(); 203 assert!(matches!( 204 err, 205 EventEncodeError::EmptyRequiredField("content") 206 )); 207 208 let parts = to_wire_parts_with_kind(&sample_gift_wrap(), KIND_GIFT_WRAP).unwrap(); 209 assert_eq!(parts.kind, KIND_GIFT_WRAP); 210 assert_eq!(parts.content, "encrypted"); 211 212 let mut gift_wrap = sample_gift_wrap(); 213 gift_wrap.recipient.public_key = " ".to_string(); 214 let err = to_wire_parts(&gift_wrap).unwrap_err(); 215 assert!(matches!( 216 err, 217 EventEncodeError::EmptyRequiredField("recipient.public_key") 218 )); 219 } 220 221 #[test] 222 fn gift_wrap_to_wire_parts_with_kind_rejects_wrong_kind() { 223 let err = to_wire_parts_with_kind(&sample_gift_wrap(), KIND_MESSAGE).unwrap_err(); 224 assert!(matches!(err, EventEncodeError::InvalidKind(KIND_MESSAGE))); 225 } 226 227 #[test] 228 fn gift_wrap_from_tags_handles_missing_expiration_and_rejects_empty_fields() { 229 let parsed = gift_wrap_from_tags( 230 KIND_GIFT_WRAP, 231 &[vec!["p".to_string(), "pubkey".to_string()]], 232 "payload", 233 ) 234 .unwrap(); 235 assert_eq!(parsed.recipient.public_key, "pubkey"); 236 assert!(parsed.recipient.relay_url.is_none()); 237 assert!(parsed.expiration.is_none()); 238 239 let err = gift_wrap_from_tags( 240 KIND_GIFT_WRAP, 241 &[vec!["p".to_string(), " ".to_string()]], 242 "payload", 243 ) 244 .unwrap_err(); 245 assert!(matches!(err, EventParseError::InvalidTag("p"))); 246 247 let err = gift_wrap_from_tags( 248 KIND_GIFT_WRAP, 249 &[vec!["p".to_string(), "pubkey".to_string()]], 250 " ", 251 ) 252 .unwrap_err(); 253 assert!(matches!(err, EventParseError::InvalidTag("content"))); 254 } 255 256 #[test] 257 fn gift_wrap_metadata_and_index_propagate_parse_errors() { 258 let tags = vec![vec!["p".to_string(), "pubkey".to_string()]]; 259 let err = data_from_event( 260 "id".to_string(), 261 "author".to_string(), 262 11, 263 KIND_GIFT_WRAP, 264 " ".to_string(), 265 tags.clone(), 266 ) 267 .unwrap_err(); 268 assert!(matches!(err, EventParseError::InvalidTag("content"))); 269 270 let err = parsed_from_event( 271 "id".to_string(), 272 "author".to_string(), 273 11, 274 KIND_GIFT_WRAP, 275 " ".to_string(), 276 tags, 277 "sig".to_string(), 278 ) 279 .unwrap_err(); 280 assert!(matches!(err, EventParseError::InvalidTag("content"))); 281 }