lib

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

parser_properties.rs (4047B)


      1 use std::{fmt::Debug, str::FromStr};
      2 
      3 use radroots_blossom::{
      4     AuthorizationClaim, BlobUrl, MediaType, Sha256,
      5     authorization::{AuthorizationAction, AuthorizationContent, ServerDomain},
      6     hash::{FileExtension, HashPath},
      7 };
      8 
      9 const ASCII_MUTATION_ALPHABET: &[u8] =
     10     b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._/:?#%\\ @\t\n\r\0;=+";
     11 
     12 #[test]
     13 fn upload_url_preserves_origin_without_replacing_or_approving_the_blob() {
     14     let hash = Sha256::digest(b"bound upload bytes");
     15     for (origin, expected) in [
     16         ("https://media.example", "https://media.example/upload"),
     17         ("https://media.example:443", "https://media.example/upload"),
     18         (
     19             "https://media.example:8443",
     20             "https://media.example:8443/upload",
     21         ),
     22         ("http://127.0.0.1:21100", "http://127.0.0.1:21100/upload"),
     23         ("http://[::1]:21100", "http://[::1]:21100/upload"),
     24         (
     25             "https://[2001:db8::1]:8443",
     26             "https://[2001:db8::1]:8443/upload",
     27         ),
     28         ("http://insecure.example", "http://insecure.example/upload"),
     29     ] {
     30         for extension in ["", ".png", ".jpeg"] {
     31             let blob = BlobUrl::parse(&format!("{origin}/{hash}{extension}")).unwrap();
     32             let original = blob.to_string();
     33             let approved_before = blob.clone().approve();
     34             assert_eq!(blob.upload_url(), expected);
     35             assert_eq!(blob.upload_url(), expected);
     36             assert_eq!(blob.to_string(), original);
     37             assert_eq!(blob.hash_path().hash(), hash);
     38             assert_eq!(blob.clone().approve(), approved_before);
     39             assert!(BlobUrl::parse(&blob.upload_url()).is_err());
     40         }
     41     }
     42 }
     43 
     44 #[test]
     45 fn deterministic_parser_mutation_corpus_never_panics_and_round_trips_successes() {
     46     let fixed = [
     47         "",
     48         "get",
     49         "cdn.example.com",
     50         "text/plain",
     51         "png",
     52         "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
     53         "/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.png",
     54         "https://cdn.example.com/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.png",
     55         "upload image",
     56     ];
     57 
     58     for value in fixed
     59         .into_iter()
     60         .map(str::to_owned)
     61         .chain((0_u64..4_096).map(mutation_string))
     62     {
     63         assert_parse_display_round_trip::<Sha256>(&value);
     64         assert_parse_display_round_trip::<FileExtension>(&value);
     65         assert_parse_display_round_trip::<HashPath>(&value);
     66         assert_parse_display_round_trip::<BlobUrl>(&value);
     67         assert_parse_display_round_trip::<MediaType>(&value);
     68         assert_parse_display_round_trip::<AuthorizationAction>(&value);
     69         assert_parse_display_round_trip::<AuthorizationContent>(&value);
     70         assert_parse_display_round_trip::<ServerDomain>(&value);
     71 
     72         let tags = vec![
     73             vec!["t".to_owned(), value.clone()],
     74             vec!["expiration".to_owned(), value.clone()],
     75             vec!["server".to_owned(), value.clone()],
     76             vec!["x".to_owned(), value.clone()],
     77         ];
     78         let _ = AuthorizationClaim::parse(&value, 1_800_000_000, &tags);
     79     }
     80 }
     81 
     82 fn assert_parse_display_round_trip<T>(value: &str)
     83 where
     84     T: FromStr + ToString + PartialEq + Debug,
     85     T::Err: Debug,
     86 {
     87     let Ok(parsed) = value.parse::<T>() else {
     88         return;
     89     };
     90     let canonical = parsed.to_string();
     91     assert_eq!(canonical.parse::<T>().unwrap(), parsed);
     92 }
     93 
     94 fn mutation_string(seed: u64) -> String {
     95     let mut state = seed.wrapping_add(1).wrapping_mul(0x9e37_79b9_7f4a_7c15);
     96     let length = usize::try_from(next(&mut state) % 385).unwrap();
     97     let mut value = String::with_capacity(length);
     98     for _ in 0..length {
     99         let index = usize::try_from(next(&mut state)).unwrap() % ASCII_MUTATION_ALPHABET.len();
    100         value.push(char::from(ASCII_MUTATION_ALPHABET[index]));
    101     }
    102     value
    103 }
    104 
    105 fn next(state: &mut u64) -> u64 {
    106     *state ^= *state << 13;
    107     *state ^= *state >> 7;
    108     *state ^= *state << 17;
    109     *state
    110 }