lib

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

commit 032280fc6a1e8b054c719f8fc3a54fa7a7c0418e
parent fae944f17be1400a5e0db3d3fd4e4e829d73c2ff
Author: triesap <tyson@radroots.org>
Date:   Sun, 21 Jun 2026 20:23:18 +0000

events-codec: expand auth coverage

- Cover HTTP auth parsed wrappers, direct wrong-kind decode, invalid payload tags, and empty url or method validation.

- Cover relay auth parsed wrappers, direct wrong-kind decode, missing relay tags, empty relay tag values, and empty challenge validation.

- Validate focused auth tests, full radroots_events_codec tests, crate check, diff check, and refreshed coverage run.

Diffstat:
Mcrates/events_codec/src/http_auth/mod.rs | 86++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcrates/events_codec/src/relay_auth/mod.rs | 82++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 166 insertions(+), 2 deletions(-)

diff --git a/crates/events_codec/src/http_auth/mod.rs b/crates/events_codec/src/http_auth/mod.rs @@ -6,7 +6,7 @@ mod tests { use radroots_events::{http_auth::RadrootsHttpAuth, kinds::KIND_POST}; use crate::error::{EventEncodeError, EventParseError}; - use crate::http_auth::decode::http_auth_from_event; + use crate::http_auth::decode::{data_from_event, http_auth_from_event, parsed_from_event}; use crate::http_auth::encode::{http_auth_build_tags, to_wire_parts, to_wire_parts_with_kind}; const PAYLOAD: &str = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; @@ -82,6 +82,35 @@ mod tests { EventEncodeError::InvalidField("payload_sha256") )); + let mut empty_url = auth.clone(); + empty_url.url.clear(); + let url_err = http_auth_build_tags(&empty_url).unwrap_err(); + assert!(matches!( + url_err, + EventEncodeError::EmptyRequiredField("url") + )); + + let mut empty_method = auth.clone(); + empty_method.method.clear(); + let method_err = http_auth_build_tags(&empty_method).unwrap_err(); + assert!(matches!( + method_err, + EventEncodeError::EmptyRequiredField("method") + )); + + let mut bad_payload_tag = parts.tags.clone(); + replace_tag_value(&mut bad_payload_tag, "payload", "ABC"); + let payload_tag_err = http_auth_from_event(parts.kind, &bad_payload_tag, "").unwrap_err(); + assert!(matches!( + payload_tag_err, + EventParseError::InvalidTag("payload") + )); + + let mut invalid_url = parts.tags.clone(); + replace_tag_value(&mut invalid_url, "u", " "); + let url_tag_err = http_auth_from_event(parts.kind, &invalid_url, "").unwrap_err(); + assert!(matches!(url_tag_err, EventParseError::InvalidTag("u"))); + let content_err = http_auth_from_event(parts.kind, &parts.tags, "not empty").unwrap_err(); assert!(matches!( content_err, @@ -101,9 +130,64 @@ mod tests { wrong_kind, EventEncodeError::InvalidKind(KIND_POST) )); + + let parts = to_wire_parts(&auth).expect("http auth wire parts"); + let decode_wrong_kind = http_auth_from_event(KIND_POST, &parts.tags, "").unwrap_err(); + assert!(matches!( + decode_wrong_kind, + EventParseError::InvalidKind { + expected: "27235", + got: KIND_POST + } + )); + } + + #[test] + fn http_auth_wrappers_preserve_event_metadata() { + let auth = RadrootsHttpAuth { + url: "https://media.example.invalid/upload".to_string(), + method: "POST".to_string(), + payload_sha256: Some(PAYLOAD.to_string()), + }; + let parts = to_wire_parts(&auth).expect("http auth wire parts"); + + let data = data_from_event( + "event-id".to_string(), + "author-pubkey".to_string(), + 99, + parts.kind, + parts.content.clone(), + parts.tags.clone(), + ) + .expect("parsed data"); + assert_eq!(data.id, "event-id"); + assert_eq!(data.author, "author-pubkey"); + assert_eq!(data.published_at, 99); + assert_eq!(data.data, auth); + + let parsed = parsed_from_event( + "event-id".to_string(), + "author-pubkey".to_string(), + 99, + parts.kind, + parts.content, + parts.tags, + "sig".to_string(), + ) + .expect("parsed event"); + assert_eq!(parsed.event.sig, "sig"); + assert_eq!(parsed.data.data, auth); } fn tag(key: &str, value: &str) -> Vec<String> { vec![key.to_string(), value.to_string()] } + + fn replace_tag_value(tags: &mut [Vec<String>], key: &str, value: &str) { + let tag = tags + .iter_mut() + .find(|tag| tag.first().map(String::as_str) == Some(key)) + .expect("tag"); + tag[1] = value.to_string(); + } } diff --git a/crates/events_codec/src/relay_auth/mod.rs b/crates/events_codec/src/relay_auth/mod.rs @@ -6,7 +6,7 @@ mod tests { use radroots_events::{kinds::KIND_POST, relay_auth::RadrootsRelayAuth}; use crate::error::{EventEncodeError, EventParseError}; - use crate::relay_auth::decode::relay_auth_from_event; + use crate::relay_auth::decode::{data_from_event, parsed_from_event, relay_auth_from_event}; use crate::relay_auth::encode::{ relay_auth_build_tags, to_wire_parts, to_wire_parts_with_kind, }; @@ -33,6 +33,19 @@ mod tests { #[test] fn relay_auth_rejects_missing_challenge_non_empty_content_and_wrong_kind() { let parts = to_wire_parts(&sample_auth()).expect("relay auth wire parts"); + let without_relay = parts + .tags + .iter() + .filter(|tag| tag.first().map(|value| value.as_str()) != Some("relay")) + .cloned() + .collect::<Vec<_>>(); + let missing_relay = + relay_auth_from_event(parts.kind, &without_relay, &parts.content).unwrap_err(); + assert!(matches!( + missing_relay, + EventParseError::MissingTag("relay") + )); + let without_challenge = parts .tags .iter() @@ -54,6 +67,15 @@ mod tests { wrong_kind, EventEncodeError::InvalidKind(KIND_POST) )); + + let decode_wrong_kind = relay_auth_from_event(KIND_POST, &parts.tags, "").unwrap_err(); + assert!(matches!( + decode_wrong_kind, + EventParseError::InvalidKind { + expected: "22242", + got: KIND_POST + } + )); } #[test] @@ -65,6 +87,56 @@ mod tests { relay_err, EventEncodeError::EmptyRequiredField("relay") )); + + let mut auth = sample_auth(); + auth.challenge.clear(); + let challenge_err = relay_auth_build_tags(&auth).unwrap_err(); + assert!(matches!( + challenge_err, + EventEncodeError::EmptyRequiredField("challenge") + )); + + let parts = to_wire_parts(&sample_auth()).expect("relay auth wire parts"); + let mut empty_relay = parts.tags.clone(); + replace_tag_value(&mut empty_relay, "relay", " "); + let relay_tag_err = relay_auth_from_event(parts.kind, &empty_relay, "").unwrap_err(); + assert!(matches!( + relay_tag_err, + EventParseError::InvalidTag("relay") + )); + } + + #[test] + fn relay_auth_wrappers_preserve_event_metadata() { + let auth = sample_auth(); + let parts = to_wire_parts(&auth).expect("relay auth wire parts"); + + let data = data_from_event( + "event-id".to_string(), + "author-pubkey".to_string(), + 99, + parts.kind, + parts.content.clone(), + parts.tags.clone(), + ) + .expect("parsed data"); + assert_eq!(data.id, "event-id"); + assert_eq!(data.author, "author-pubkey"); + assert_eq!(data.published_at, 99); + assert_eq!(data.data, auth); + + let parsed = parsed_from_event( + "event-id".to_string(), + "author-pubkey".to_string(), + 99, + parts.kind, + parts.content, + parts.tags, + "sig".to_string(), + ) + .expect("parsed event"); + assert_eq!(parsed.event.sig, "sig"); + assert_eq!(parsed.data.data, auth); } fn sample_auth() -> RadrootsRelayAuth { @@ -77,4 +149,12 @@ mod tests { fn tag(key: &str, value: &str) -> Vec<String> { vec![key.to_string(), value.to_string()] } + + fn replace_tag_value(tags: &mut [Vec<String>], key: &str, value: &str) { + let tag = tags + .iter_mut() + .find(|tag| tag.first().map(String::as_str) == Some(key)) + .expect("tag"); + tag[1] = value.to_string(); + } }