event_ref.rs (11096B)
1 mod common; 2 #[path = "../src/test_fixtures.rs"] 3 mod test_fixtures; 4 5 use radroots_events::kinds::KIND_POST; 6 use radroots_events_codec::error::EventParseError; 7 use radroots_events_codec::event_ref::{ 8 build_event_ref_tag, find_event_ref_tag, parse_event_ref_tag, parse_nip10_ref_tags, 9 push_nip10_ref_tags, 10 }; 11 use test_fixtures::{RELAY_PRIMARY_WSS, RELAY_SECONDARY_WSS, RELAY_TERTIARY_WSS}; 12 13 #[test] 14 fn build_and_parse_roundtrip_with_d_tag_and_relays() { 15 let event = common::event_ref_with_d( 16 "id", 17 "author", 18 42, 19 "d-tag", 20 Some(vec!["wss://relay".to_string()]), 21 ); 22 23 let tag = build_event_ref_tag("e", &event); 24 let parsed = parse_event_ref_tag(&tag, "e").unwrap(); 25 26 assert_eq!(parsed.id, event.id); 27 assert_eq!(parsed.author, event.author); 28 assert_eq!(parsed.kind, event.kind); 29 assert_eq!(parsed.d_tag, event.d_tag); 30 assert_eq!(parsed.relays, event.relays); 31 } 32 33 #[test] 34 fn build_and_parse_roundtrip_without_d_tag_or_relays() { 35 let event = common::event_ref("id", "author", KIND_POST); 36 let tag = build_event_ref_tag("e", &event); 37 38 assert_eq!(tag.len(), 5); 39 assert_eq!(tag[4], ""); 40 41 let parsed = parse_event_ref_tag(&tag, "e").unwrap(); 42 assert_eq!(parsed.id, event.id); 43 assert_eq!(parsed.author, event.author); 44 assert_eq!(parsed.kind, event.kind); 45 assert!(parsed.d_tag.is_none()); 46 assert!(parsed.relays.is_none()); 47 } 48 49 #[test] 50 fn parse_event_ref_tag_allows_relay_only_fifth_entry() { 51 let tag = vec![ 52 "e".to_string(), 53 "id".to_string(), 54 "author".to_string(), 55 KIND_POST.to_string(), 56 "wss://relay".to_string(), 57 ]; 58 59 let parsed = parse_event_ref_tag(&tag, "e").unwrap(); 60 assert!(parsed.d_tag.is_none()); 61 assert_eq!(parsed.relays, Some(vec!["wss://relay".to_string()])); 62 63 let ws_tag = vec![ 64 "e".to_string(), 65 "id".to_string(), 66 "author".to_string(), 67 KIND_POST.to_string(), 68 "ws://relay".to_string(), 69 ]; 70 let parsed = parse_event_ref_tag(&ws_tag, "e").unwrap(); 71 assert!(parsed.d_tag.is_none()); 72 assert_eq!(parsed.relays, Some(vec!["ws://relay".to_string()])); 73 } 74 75 #[test] 76 fn parse_event_ref_tag_rejects_invalid_kind() { 77 let tag = vec![ 78 "e".to_string(), 79 "id".to_string(), 80 "author".to_string(), 81 "bad".to_string(), 82 ]; 83 84 let err = parse_event_ref_tag(&tag, "e").unwrap_err(); 85 assert!(matches!(err, EventParseError::InvalidNumber("e", _))); 86 } 87 88 #[test] 89 fn parse_event_ref_tag_rejects_wrong_tag_name_and_missing_fields() { 90 let tag = vec!["p".to_string(), "id".to_string()]; 91 let err = parse_event_ref_tag(&tag, "e").unwrap_err(); 92 assert!(matches!(err, EventParseError::InvalidTag("e"))); 93 94 let tag = vec![ 95 "e".to_string(), 96 "id".to_string(), 97 "author".to_string(), 98 KIND_POST.to_string(), 99 ]; 100 let parsed = parse_event_ref_tag(&tag, "e").unwrap(); 101 assert!(parsed.d_tag.is_none()); 102 assert!(parsed.relays.is_none()); 103 } 104 105 #[test] 106 fn parse_event_ref_tag_rejects_missing_required_values() { 107 let err = parse_event_ref_tag(&["e".to_string()], "e").unwrap_err(); 108 assert!(matches!(err, EventParseError::InvalidTag("e"))); 109 110 let err = parse_event_ref_tag(&["e".to_string(), "id".to_string()], "e").unwrap_err(); 111 assert!(matches!(err, EventParseError::InvalidTag("e"))); 112 113 let err = parse_event_ref_tag( 114 &["e".to_string(), "id".to_string(), "author".to_string()], 115 "e", 116 ) 117 .unwrap_err(); 118 assert!(matches!(err, EventParseError::InvalidTag("e"))); 119 } 120 121 #[test] 122 fn find_event_ref_tag_locates_first_match() { 123 let event = common::event_ref("id", "author", KIND_POST); 124 let tags = vec![ 125 vec!["p".to_string(), "pubkey".to_string()], 126 build_event_ref_tag("e", &event), 127 ]; 128 129 let found = find_event_ref_tag(&tags, "e").unwrap(); 130 assert_eq!(found[0], "e"); 131 assert_eq!(found[1], "id"); 132 } 133 134 #[test] 135 fn push_and_parse_nip10_ref_tags_roundtrip_with_and_without_a_tag() { 136 let event = common::event_ref_with_d( 137 "id", 138 "author", 139 KIND_POST, 140 "AAAAAAAAAAAAAAAAAAAAAA", 141 Some(vec![RELAY_PRIMARY_WSS.to_string()]), 142 ); 143 let mut tags = Vec::new(); 144 push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a"); 145 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 146 assert_eq!(parsed.id, event.id); 147 assert_eq!(parsed.author, event.author); 148 assert_eq!(parsed.kind, event.kind); 149 assert_eq!(parsed.d_tag, event.d_tag); 150 assert_eq!(parsed.relays, event.relays); 151 152 let event = common::event_ref("id2", "author2", KIND_POST); 153 let mut tags = Vec::new(); 154 push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a"); 155 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 156 assert_eq!(parsed.id, event.id); 157 assert_eq!(parsed.author, event.author); 158 assert_eq!(parsed.kind, event.kind); 159 assert!(parsed.d_tag.is_none()); 160 161 let event = 162 common::event_ref_with_d("id3", "author3", KIND_POST, "AAAAAAAAAAAAAAAAAAAAAA", None); 163 let mut tags = Vec::new(); 164 push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a"); 165 let a_tag = tags 166 .iter() 167 .find(|tag| tag.first().map(|v| v.as_str()) == Some("a")) 168 .expect("a tag"); 169 assert_eq!(a_tag.len(), 2); 170 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 171 assert_eq!(parsed.d_tag, event.d_tag); 172 assert!(parsed.relays.is_none()); 173 } 174 175 #[test] 176 fn parse_nip10_ref_tags_rejects_missing_or_invalid_required_tags() { 177 let err = parse_nip10_ref_tags(&[], "e", "p", "k", "a").unwrap_err(); 178 assert!(matches!(err, EventParseError::MissingTag("e"))); 179 180 let tags = vec![ 181 vec!["e".to_string(), "".to_string()], 182 vec!["p".to_string(), "author".to_string()], 183 vec!["k".to_string(), KIND_POST.to_string()], 184 ]; 185 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 186 assert!(matches!(err, EventParseError::InvalidTag("e"))); 187 188 let tags = vec![ 189 vec!["e".to_string(), "id".to_string()], 190 vec!["p".to_string(), "".to_string()], 191 vec!["k".to_string(), KIND_POST.to_string()], 192 ]; 193 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 194 assert!(matches!(err, EventParseError::InvalidTag("p"))); 195 196 let tags = vec![ 197 vec!["e".to_string(), "id".to_string()], 198 vec!["p".to_string(), "author".to_string()], 199 vec!["k".to_string(), "bad-kind".to_string()], 200 ]; 201 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 202 assert!(matches!(err, EventParseError::InvalidNumber("k", _))); 203 } 204 205 #[test] 206 fn parse_nip10_ref_tags_rejects_missing_required_values() { 207 let tags = vec![ 208 vec!["e".to_string()], 209 vec!["p".to_string(), "author".to_string()], 210 vec!["k".to_string(), KIND_POST.to_string()], 211 ]; 212 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 213 assert!(matches!(err, EventParseError::InvalidTag("e"))); 214 215 let tags = vec![ 216 vec!["e".to_string(), "id".to_string()], 217 vec!["p".to_string()], 218 vec!["k".to_string(), KIND_POST.to_string()], 219 ]; 220 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 221 assert!(matches!(err, EventParseError::InvalidTag("p"))); 222 223 let tags = vec![ 224 vec!["e".to_string(), "id".to_string()], 225 vec!["p".to_string(), "author".to_string()], 226 vec!["k".to_string()], 227 ]; 228 let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err(); 229 assert!(matches!(err, EventParseError::InvalidTag("k"))); 230 } 231 232 #[test] 233 fn parse_nip10_ref_tags_rejects_missing_p_and_k_tags() { 234 let missing_p = vec![ 235 vec!["e".to_string(), "id".to_string()], 236 vec!["k".to_string(), KIND_POST.to_string()], 237 ]; 238 let err = parse_nip10_ref_tags(&missing_p, "e", "p", "k", "a").unwrap_err(); 239 assert!(matches!(err, EventParseError::MissingTag("p"))); 240 241 let missing_k = vec![ 242 vec!["e".to_string(), "id".to_string()], 243 vec!["p".to_string(), "author".to_string()], 244 ]; 245 let err = parse_nip10_ref_tags(&missing_k, "e", "p", "k", "a").unwrap_err(); 246 assert!(matches!(err, EventParseError::MissingTag("k"))); 247 } 248 249 #[test] 250 fn parse_nip10_ref_tags_prefers_e_relays_and_can_fall_back_to_a_relays() { 251 let tags = vec![ 252 vec!["e".to_string(), "id".to_string()], 253 vec!["p".to_string(), "author".to_string()], 254 vec!["k".to_string(), KIND_POST.to_string()], 255 vec![ 256 "a".to_string(), 257 format!("{}:{}:{}", KIND_POST, "author", "AAAAAAAAAAAAAAAAAAAAAA"), 258 RELAY_SECONDARY_WSS.to_string(), 259 ], 260 ]; 261 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 262 assert_eq!(parsed.d_tag.as_deref(), Some("AAAAAAAAAAAAAAAAAAAAAA")); 263 assert_eq!(parsed.relays, Some(vec![RELAY_SECONDARY_WSS.to_string()])); 264 265 let tags = vec![ 266 vec![ 267 "e".to_string(), 268 "id".to_string(), 269 RELAY_PRIMARY_WSS.to_string(), 270 ], 271 vec!["p".to_string(), "author".to_string()], 272 vec!["k".to_string(), KIND_POST.to_string()], 273 vec![ 274 "a".to_string(), 275 format!("{}:{}:{}", KIND_POST, "author", "AAAAAAAAAAAAAAAAAAAAAA"), 276 RELAY_SECONDARY_WSS.to_string(), 277 ], 278 ]; 279 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 280 assert_eq!(parsed.relays, Some(vec![RELAY_PRIMARY_WSS.to_string()])); 281 } 282 283 #[test] 284 fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() { 285 let tags = vec![ 286 vec!["e".to_string(), "id".to_string()], 287 vec!["p".to_string(), "author".to_string()], 288 vec!["k".to_string(), KIND_POST.to_string()], 289 vec!["a".to_string()], 290 vec![ 291 "a".to_string(), 292 format!( 293 "{}:{}:{}", 294 KIND_POST + 1, 295 "author", 296 "AAAAAAAAAAAAAAAAAAAAAA" 297 ), 298 RELAY_PRIMARY_WSS.to_string(), 299 ], 300 vec![ 301 "a".to_string(), 302 format!( 303 "{}:{}:{}", 304 KIND_POST, "other-author", "AAAAAAAAAAAAAAAAAAAAAA" 305 ), 306 RELAY_SECONDARY_WSS.to_string(), 307 ], 308 vec![ 309 "a".to_string(), 310 format!("{}:{}:", KIND_POST, "author"), 311 RELAY_TERTIARY_WSS.to_string(), 312 ], 313 ]; 314 315 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 316 assert!(parsed.d_tag.is_none()); 317 assert_eq!(parsed.relays, Some(vec![RELAY_TERTIARY_WSS.to_string()])); 318 319 let tags = vec![ 320 vec!["e".to_string(), "id".to_string()], 321 vec!["p".to_string(), "author".to_string()], 322 vec!["k".to_string(), KIND_POST.to_string()], 323 vec!["a".to_string(), format!("{}:{}", KIND_POST, "author")], 324 ]; 325 let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap(); 326 assert!(parsed.d_tag.is_none()); 327 assert!(parsed.relays.is_none()); 328 }