lib

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

event_ref.rs (12826B)


      1 mod common;
      2 #[path = "../src/test_fixtures.rs"]
      3 mod test_fixtures;
      4 
      5 use radroots_event::envelope::kind::KIND_POST;
      6 use radroots_event_codec::decode::EventParseError;
      7 use radroots_event_codec::decode::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 fn hex_64(character: char) -> String {
     14     core::iter::repeat_n(character, 64).collect()
     15 }
     16 
     17 #[test]
     18 fn build_and_parse_roundtrip_with_d_tag_and_relays() {
     19     let event = common::event_ref_with_d(
     20         &hex_64('1'),
     21         &hex_64('2'),
     22         42,
     23         "d-tag",
     24         Some(vec!["wss://relay".to_string()]),
     25     );
     26 
     27     let tag = build_event_ref_tag("e", &event);
     28     let parsed = parse_event_ref_tag(&tag, "e").unwrap();
     29 
     30     assert_eq!(parsed.id, event.id);
     31     assert_eq!(parsed.author, event.author);
     32     assert_eq!(parsed.kind, event.kind);
     33     assert_eq!(parsed.d_tag, event.d_tag);
     34     assert_eq!(parsed.relays, event.relays);
     35 }
     36 
     37 #[test]
     38 fn build_and_parse_roundtrip_without_d_tag_or_relays() {
     39     let event = common::event_ref(&hex_64('3'), &hex_64('4'), KIND_POST);
     40     let tag = build_event_ref_tag("e", &event);
     41 
     42     assert_eq!(tag.len(), 5);
     43     assert_eq!(tag[4], "");
     44 
     45     let parsed = parse_event_ref_tag(&tag, "e").unwrap();
     46     assert_eq!(parsed.id, event.id);
     47     assert_eq!(parsed.author, event.author);
     48     assert_eq!(parsed.kind, event.kind);
     49     assert!(parsed.d_tag.is_none());
     50     assert!(parsed.relays.is_none());
     51 }
     52 
     53 #[test]
     54 fn parse_event_ref_tag_rejects_noncanonical_event_pointer_shapes() {
     55     let tag = vec![
     56         "e".to_string(),
     57         hex_64('5'),
     58         hex_64('6'),
     59         KIND_POST.to_string(),
     60         "wss://relay".to_string(),
     61     ];
     62     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
     63     assert!(matches!(err, EventParseError::InvalidTag("e")));
     64 
     65     let tag = vec![
     66         "e".to_string(),
     67         hex_64('5'),
     68         common::AUTHOR.to_string(),
     69         KIND_POST.to_string(),
     70         RELAY_PRIMARY_WSS.to_string(),
     71     ];
     72     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
     73     assert!(matches!(err, EventParseError::InvalidTag("e")));
     74 
     75     let tag = vec![
     76         "e".to_string(),
     77         hex_64('5'),
     78         hex_64('6'),
     79         KIND_POST.to_string(),
     80     ];
     81     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
     82     assert!(matches!(err, EventParseError::InvalidTag("e")));
     83 
     84     let tag = vec![
     85         "e".to_string(),
     86         "not-hex".to_string(),
     87         hex_64('6'),
     88         KIND_POST.to_string(),
     89         String::new(),
     90     ];
     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         hex_64('5'),
     97         "not-hex".to_string(),
     98         KIND_POST.to_string(),
     99         String::new(),
    100     ];
    101     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    102     assert!(matches!(err, EventParseError::InvalidTag("e")));
    103 
    104     let tag = vec![
    105         "e".to_string(),
    106         hex_64('5'),
    107         hex_64('6'),
    108         KIND_POST.to_string(),
    109         "bad d-tag".to_string(),
    110     ];
    111     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    112     assert!(matches!(err, EventParseError::InvalidTag("e")));
    113 
    114     let tag = vec![
    115         "e".to_string(),
    116         hex_64('5'),
    117         hex_64('6'),
    118         KIND_POST.to_string(),
    119         String::new(),
    120         "https://relay".to_string(),
    121     ];
    122     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    123     assert!(matches!(err, EventParseError::InvalidTag("e")));
    124 }
    125 
    126 #[test]
    127 fn parse_event_ref_tag_rejects_invalid_kind() {
    128     let tag = vec![
    129         "e".to_string(),
    130         hex_64('7'),
    131         hex_64('8'),
    132         "bad".to_string(),
    133         String::new(),
    134     ];
    135 
    136     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    137     assert!(matches!(err, EventParseError::InvalidNumber("e", _)));
    138 }
    139 
    140 #[test]
    141 fn parse_event_ref_tag_rejects_wrong_tag_name_and_missing_fields() {
    142     let tag = vec!["p".to_string(), "id".to_string()];
    143     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    144     assert!(matches!(err, EventParseError::InvalidTag("e")));
    145 
    146     let tag = vec![
    147         "e".to_string(),
    148         hex_64('9'),
    149         hex_64('a'),
    150         KIND_POST.to_string(),
    151     ];
    152     let err = parse_event_ref_tag(&tag, "e").unwrap_err();
    153     assert!(matches!(err, EventParseError::InvalidTag("e")));
    154 }
    155 
    156 #[test]
    157 fn parse_event_ref_tag_rejects_missing_required_values() {
    158     let err = parse_event_ref_tag(&["e".to_string()], "e").unwrap_err();
    159     assert!(matches!(err, EventParseError::InvalidTag("e")));
    160 
    161     let err = parse_event_ref_tag(&["e".to_string(), "id".to_string()], "e").unwrap_err();
    162     assert!(matches!(err, EventParseError::InvalidTag("e")));
    163 
    164     let err = parse_event_ref_tag(
    165         &["e".to_string(), "id".to_string(), "author".to_string()],
    166         "e",
    167     )
    168     .unwrap_err();
    169     assert!(matches!(err, EventParseError::InvalidTag("e")));
    170 }
    171 
    172 #[test]
    173 fn find_event_ref_tag_locates_first_match() {
    174     let id = hex_64('b');
    175     let event = common::event_ref(&id, common::AUTHOR, KIND_POST);
    176     let tags = vec![
    177         vec!["p".to_string(), "pubkey".to_string()],
    178         build_event_ref_tag("e", &event),
    179     ];
    180 
    181     let found = find_event_ref_tag(&tags, "e").unwrap();
    182     assert_eq!(found[0], "e");
    183     assert_eq!(found[1], id);
    184 }
    185 
    186 #[test]
    187 fn push_and_parse_nip10_ref_tags_roundtrip_with_and_without_a_tag() {
    188     let event = common::event_ref_with_d(
    189         "id",
    190         common::AUTHOR,
    191         KIND_POST,
    192         "AAAAAAAAAAAAAAAAAAAAAA",
    193         Some(vec![RELAY_PRIMARY_WSS.to_string()]),
    194     );
    195     let mut tags = Vec::new();
    196     push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a");
    197     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    198     assert_eq!(parsed.id, event.id);
    199     assert_eq!(parsed.author, event.author);
    200     assert_eq!(parsed.kind, event.kind);
    201     assert_eq!(parsed.d_tag, event.d_tag);
    202     assert_eq!(parsed.relays, event.relays);
    203 
    204     let event = common::event_ref("id2", common::AUTHOR, KIND_POST);
    205     let mut tags = Vec::new();
    206     push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a");
    207     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    208     assert_eq!(parsed.id, event.id);
    209     assert_eq!(parsed.author, event.author);
    210     assert_eq!(parsed.kind, event.kind);
    211     assert!(parsed.d_tag.is_none());
    212 
    213     let event = common::event_ref_with_d(
    214         "id3",
    215         common::AUTHOR,
    216         KIND_POST,
    217         "AAAAAAAAAAAAAAAAAAAAAA",
    218         None,
    219     );
    220     let mut tags = Vec::new();
    221     push_nip10_ref_tags(&mut tags, &event, "e", "p", "k", "a");
    222     let a_tag = tags
    223         .iter()
    224         .find(|tag| tag.first().map(|v| v.as_str()) == Some("a"))
    225         .expect("a tag");
    226     assert_eq!(a_tag.len(), 2);
    227     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    228     assert_eq!(parsed.d_tag, event.d_tag);
    229     assert!(parsed.relays.is_none());
    230 }
    231 
    232 #[test]
    233 fn parse_nip10_ref_tags_rejects_missing_or_invalid_required_tags() {
    234     let err = parse_nip10_ref_tags(&[], "e", "p", "k", "a").unwrap_err();
    235     assert!(matches!(err, EventParseError::MissingTag("e")));
    236 
    237     let tags = vec![
    238         vec!["e".to_string(), "".to_string()],
    239         vec!["p".to_string(), "author".to_string()],
    240         vec!["k".to_string(), KIND_POST.to_string()],
    241     ];
    242     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    243     assert!(matches!(err, EventParseError::InvalidTag("e")));
    244 
    245     let tags = vec![
    246         vec!["e".to_string(), "id".to_string()],
    247         vec!["p".to_string(), "".to_string()],
    248         vec!["k".to_string(), KIND_POST.to_string()],
    249     ];
    250     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    251     assert!(matches!(err, EventParseError::InvalidTag("p")));
    252 
    253     let tags = vec![
    254         vec!["e".to_string(), "id".to_string()],
    255         vec!["p".to_string(), common::AUTHOR.to_string()],
    256         vec!["k".to_string(), "bad-kind".to_string()],
    257     ];
    258     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    259     assert!(matches!(err, EventParseError::InvalidNumber("k", _)));
    260 }
    261 
    262 #[test]
    263 fn parse_nip10_ref_tags_rejects_missing_required_values() {
    264     let tags = vec![
    265         vec!["e".to_string()],
    266         vec!["p".to_string(), "author".to_string()],
    267         vec!["k".to_string(), KIND_POST.to_string()],
    268     ];
    269     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    270     assert!(matches!(err, EventParseError::InvalidTag("e")));
    271 
    272     let tags = vec![
    273         vec!["e".to_string(), "id".to_string()],
    274         vec!["p".to_string()],
    275         vec!["k".to_string(), KIND_POST.to_string()],
    276     ];
    277     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    278     assert!(matches!(err, EventParseError::InvalidTag("p")));
    279 
    280     let tags = vec![
    281         vec!["e".to_string(), "id".to_string()],
    282         vec!["p".to_string(), common::AUTHOR.to_string()],
    283         vec!["k".to_string()],
    284     ];
    285     let err = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap_err();
    286     assert!(matches!(err, EventParseError::InvalidTag("k")));
    287 }
    288 
    289 #[test]
    290 fn parse_nip10_ref_tags_rejects_missing_p_and_k_tags() {
    291     let missing_p = vec![
    292         vec!["e".to_string(), "id".to_string()],
    293         vec!["k".to_string(), KIND_POST.to_string()],
    294     ];
    295     let err = parse_nip10_ref_tags(&missing_p, "e", "p", "k", "a").unwrap_err();
    296     assert!(matches!(err, EventParseError::MissingTag("p")));
    297 
    298     let missing_k = vec![
    299         vec!["e".to_string(), "id".to_string()],
    300         vec!["p".to_string(), common::AUTHOR.to_string()],
    301     ];
    302     let err = parse_nip10_ref_tags(&missing_k, "e", "p", "k", "a").unwrap_err();
    303     assert!(matches!(err, EventParseError::MissingTag("k")));
    304 }
    305 
    306 #[test]
    307 fn parse_nip10_ref_tags_prefers_e_relays_and_can_fall_back_to_a_relays() {
    308     let tags = vec![
    309         vec!["e".to_string(), "id".to_string()],
    310         vec!["p".to_string(), common::AUTHOR.to_string()],
    311         vec!["k".to_string(), KIND_POST.to_string()],
    312         vec![
    313             "a".to_string(),
    314             format!(
    315                 "{}:{}:{}",
    316                 KIND_POST,
    317                 common::AUTHOR,
    318                 "AAAAAAAAAAAAAAAAAAAAAA"
    319             ),
    320             RELAY_SECONDARY_WSS.to_string(),
    321         ],
    322     ];
    323     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    324     assert_eq!(parsed.d_tag.as_deref(), Some("AAAAAAAAAAAAAAAAAAAAAA"));
    325     assert_eq!(parsed.relays, Some(vec![RELAY_SECONDARY_WSS.to_string()]));
    326 
    327     let tags = vec![
    328         vec![
    329             "e".to_string(),
    330             "id".to_string(),
    331             RELAY_PRIMARY_WSS.to_string(),
    332         ],
    333         vec!["p".to_string(), common::AUTHOR.to_string()],
    334         vec!["k".to_string(), KIND_POST.to_string()],
    335         vec![
    336             "a".to_string(),
    337             format!(
    338                 "{}:{}:{}",
    339                 KIND_POST,
    340                 common::AUTHOR,
    341                 "AAAAAAAAAAAAAAAAAAAAAA"
    342             ),
    343             RELAY_SECONDARY_WSS.to_string(),
    344         ],
    345     ];
    346     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    347     assert_eq!(parsed.relays, Some(vec![RELAY_PRIMARY_WSS.to_string()]));
    348 }
    349 
    350 #[test]
    351 fn parse_nip10_ref_tags_skips_invalid_a_tags_until_match() {
    352     let tags = vec![
    353         vec!["e".to_string(), "id".to_string()],
    354         vec!["p".to_string(), common::AUTHOR.to_string()],
    355         vec!["k".to_string(), KIND_POST.to_string()],
    356         vec!["a".to_string()],
    357         vec![
    358             "a".to_string(),
    359             format!(
    360                 "{}:{}:{}",
    361                 KIND_POST + 1,
    362                 common::AUTHOR,
    363                 "AAAAAAAAAAAAAAAAAAAAAA"
    364             ),
    365             RELAY_PRIMARY_WSS.to_string(),
    366         ],
    367         vec![
    368             "a".to_string(),
    369             format!(
    370                 "{}:{}:{}",
    371                 KIND_POST, "other-author", "AAAAAAAAAAAAAAAAAAAAAA"
    372             ),
    373             RELAY_SECONDARY_WSS.to_string(),
    374         ],
    375         vec![
    376             "a".to_string(),
    377             format!("{}:{}:", KIND_POST, common::AUTHOR),
    378             RELAY_TERTIARY_WSS.to_string(),
    379         ],
    380     ];
    381 
    382     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    383     assert!(parsed.d_tag.is_none());
    384     assert_eq!(parsed.relays, Some(vec![RELAY_TERTIARY_WSS.to_string()]));
    385 
    386     let tags = vec![
    387         vec!["e".to_string(), "id".to_string()],
    388         vec!["p".to_string(), common::AUTHOR.to_string()],
    389         vec!["k".to_string(), KIND_POST.to_string()],
    390         vec!["a".to_string(), format!("{}:{}", KIND_POST, common::AUTHOR)],
    391     ];
    392     let parsed = parse_nip10_ref_tags(&tags, "e", "p", "k", "a").unwrap();
    393     assert!(parsed.d_tag.is_none());
    394     assert!(parsed.relays.is_none());
    395 }