lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

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 }