lib

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

mod.rs (8158B)


      1 pub mod decode;
      2 pub mod encode;
      3 
      4 #[cfg(test)]
      5 mod tests {
      6     use radroots_event::{envelope::kind::KIND_POST, social::http_auth::HttpAuth};
      7 
      8     use crate::error::{EventEncodeError, EventParseError};
      9     use crate::http_auth::decode::{data_from_event, http_auth_from_event, parsed_from_event};
     10     use crate::http_auth::encode::{http_auth_build_tags, to_wire_parts, to_wire_parts_with_kind};
     11 
     12     const EVENT_ID: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
     13     const AUTHOR: &str = crate::test_fixtures::FIXTURE_ALICE_PUBLIC_KEY_HEX;
     14     const EVENT_SIG: &str = concat!(
     15         "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc",
     16         "cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc"
     17     );
     18     const PAYLOAD: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
     19 
     20     #[test]
     21     fn http_auth_encodes_and_decodes_get_without_payload() {
     22         let auth = HttpAuth {
     23             url: "https://media.example.invalid/download".to_string(),
     24             method: "GET".to_string(),
     25             payload_sha256: None,
     26         };
     27         let parts = to_wire_parts(&auth).expect("http auth wire parts");
     28 
     29         assert_eq!(parts.kind, 27235);
     30         assert_eq!(parts.content, "");
     31         assert!(parts.tags.contains(&tag("u", auth.url.as_str())));
     32         assert!(parts.tags.contains(&tag("method", "GET")));
     33 
     34         let decoded =
     35             http_auth_from_event(parts.kind, &parts.tags, &parts.content).expect("decode");
     36         assert_eq!(decoded, auth);
     37     }
     38 
     39     #[test]
     40     fn http_auth_encodes_and_decodes_post_with_payload() {
     41         let auth = HttpAuth {
     42             url: "https://media.example.invalid/upload".to_string(),
     43             method: "POST".to_string(),
     44             payload_sha256: Some(PAYLOAD.to_string()),
     45         };
     46         let parts = to_wire_parts(&auth).expect("http auth wire parts");
     47 
     48         assert!(parts.tags.contains(&tag("payload", PAYLOAD)));
     49         let decoded =
     50             http_auth_from_event(parts.kind, &parts.tags, &parts.content).expect("decode");
     51         assert_eq!(decoded.payload_sha256.as_deref(), Some(PAYLOAD));
     52     }
     53 
     54     #[test]
     55     fn http_auth_rejects_missing_url_missing_method_bad_payload_and_content() {
     56         let auth = HttpAuth {
     57             url: "https://media.example.invalid/upload".to_string(),
     58             method: "POST".to_string(),
     59             payload_sha256: Some(PAYLOAD.to_string()),
     60         };
     61         let parts = to_wire_parts(&auth).expect("http auth wire parts");
     62         let without_url = parts
     63             .tags
     64             .iter()
     65             .filter(|tag| tag.first().map(|value| value.as_str()) != Some("u"))
     66             .cloned()
     67             .collect::<Vec<_>>();
     68         let missing_url = http_auth_from_event(parts.kind, &without_url, "").unwrap_err();
     69         assert!(matches!(missing_url, EventParseError::MissingTag("u")));
     70 
     71         let without_method = parts
     72             .tags
     73             .iter()
     74             .filter(|tag| tag.first().map(|value| value.as_str()) != Some("method"))
     75             .cloned()
     76             .collect::<Vec<_>>();
     77         let missing_method = http_auth_from_event(parts.kind, &without_method, "").unwrap_err();
     78         assert!(matches!(
     79             missing_method,
     80             EventParseError::MissingTag("method")
     81         ));
     82 
     83         let mut bad_payload = auth.clone();
     84         bad_payload.payload_sha256 = Some("ABC".to_string());
     85         let payload_err = http_auth_build_tags(&bad_payload).unwrap_err();
     86         assert!(matches!(
     87             payload_err,
     88             EventEncodeError::InvalidField("payload_sha256")
     89         ));
     90 
     91         let mut empty_url = auth.clone();
     92         empty_url.url.clear();
     93         let url_err = http_auth_build_tags(&empty_url).unwrap_err();
     94         assert!(matches!(
     95             url_err,
     96             EventEncodeError::EmptyRequiredField("url")
     97         ));
     98 
     99         let mut empty_method = auth.clone();
    100         empty_method.method.clear();
    101         let method_err = http_auth_build_tags(&empty_method).unwrap_err();
    102         assert!(matches!(
    103             method_err,
    104             EventEncodeError::EmptyRequiredField("method")
    105         ));
    106 
    107         let mut bad_payload_tag = parts.tags.clone();
    108         replace_tag_value(&mut bad_payload_tag, "payload", "ABC");
    109         let payload_tag_err = http_auth_from_event(parts.kind, &bad_payload_tag, "").unwrap_err();
    110         assert!(matches!(
    111             payload_tag_err,
    112             EventParseError::InvalidTag("payload")
    113         ));
    114 
    115         let mut invalid_url = parts.tags.clone();
    116         replace_tag_value(&mut invalid_url, "u", " ");
    117         let url_tag_err = http_auth_from_event(parts.kind, &invalid_url, "").unwrap_err();
    118         assert!(matches!(url_tag_err, EventParseError::InvalidTag("u")));
    119 
    120         let content_err = http_auth_from_event(parts.kind, &parts.tags, "not empty").unwrap_err();
    121         assert!(matches!(
    122             content_err,
    123             EventParseError::InvalidJson("content")
    124         ));
    125     }
    126 
    127     #[test]
    128     fn http_auth_rejects_wrong_kind() {
    129         let auth = HttpAuth {
    130             url: "https://media.example.invalid/upload".to_string(),
    131             method: "POST".to_string(),
    132             payload_sha256: Some(PAYLOAD.to_string()),
    133         };
    134         let wrong_kind = to_wire_parts_with_kind(&auth, KIND_POST).unwrap_err();
    135         assert!(matches!(
    136             wrong_kind,
    137             EventEncodeError::InvalidKind(KIND_POST)
    138         ));
    139 
    140         let parts = to_wire_parts(&auth).expect("http auth wire parts");
    141         let decode_wrong_kind = http_auth_from_event(KIND_POST, &parts.tags, "").unwrap_err();
    142         assert!(matches!(
    143             decode_wrong_kind,
    144             EventParseError::InvalidKind {
    145                 expected: "27235",
    146                 got: KIND_POST
    147             }
    148         ));
    149     }
    150 
    151     #[test]
    152     #[cfg_attr(coverage_nightly, coverage(off))]
    153     fn http_auth_rejects_duplicate_security_tags() {
    154         let auth = HttpAuth {
    155             url: "https://media.example.invalid/upload".to_string(),
    156             method: "POST".to_string(),
    157             payload_sha256: Some(PAYLOAD.to_string()),
    158         };
    159         let parts = to_wire_parts(&auth).expect("http auth wire parts");
    160 
    161         for key in ["u", "method", "payload"] {
    162             let mut duplicate = parts.tags.clone();
    163             duplicate.push(tag(key, "duplicate"));
    164             let err = http_auth_from_event(parts.kind, &duplicate, "").unwrap_err();
    165             assert!(
    166                 matches!(err, EventParseError::DuplicateTag(tag) if tag == key),
    167                 "expected duplicate {key}, got {err:?}"
    168             );
    169         }
    170     }
    171 
    172     #[test]
    173     fn http_auth_wrappers_preserve_event_metadata() {
    174         let auth = HttpAuth {
    175             url: "https://media.example.invalid/upload".to_string(),
    176             method: "POST".to_string(),
    177             payload_sha256: Some(PAYLOAD.to_string()),
    178         };
    179         let parts = to_wire_parts(&auth).expect("http auth wire parts");
    180 
    181         let data = data_from_event(
    182             EVENT_ID.to_string(),
    183             AUTHOR.to_string(),
    184             99,
    185             parts.kind,
    186             parts.content.clone(),
    187             parts.tags.clone(),
    188         )
    189         .expect("parsed data");
    190         assert_eq!(data.id, EVENT_ID);
    191         assert_eq!(data.author, AUTHOR);
    192         assert_eq!(data.published_at, 99);
    193         assert_eq!(data.data, auth);
    194 
    195         let parsed = parsed_from_event(
    196             EVENT_ID.to_string(),
    197             AUTHOR.to_string(),
    198             99,
    199             parts.kind,
    200             parts.content,
    201             parts.tags,
    202             EVENT_SIG.to_string(),
    203         )
    204         .expect("parsed event");
    205         assert_eq!(parsed.event.signature_hex(), EVENT_SIG);
    206         assert_eq!(parsed.data.data, auth);
    207     }
    208 
    209     fn tag(key: &str, value: &str) -> Vec<String> {
    210         vec![key.to_string(), value.to_string()]
    211     }
    212 
    213     fn replace_tag_value(tags: &mut [Vec<String>], key: &str, value: &str) {
    214         let tag = tags
    215             .iter_mut()
    216             .find(|tag| tag.first().map(String::as_str) == Some(key))
    217             .expect("tag");
    218         tag[1] = value.to_string();
    219     }
    220 }