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 }