lib

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

commit 60b8e80f98e0cfa86ea86eb6093fd916765243ec
parent e798f0e2f3ea54a1b9a4e9d4fdb482ee1d85e8e0
Author: triesap <tyson@radroots.org>
Date:   Sun, 21 Jun 2026 21:02:23 +0000

events-codec: expand social encode coverage

- Cover optional relay, author, kind, hint, and thumbnail encode branches for post, comment, and reaction targets.

- Cover social target mismatch exits for comment and reaction encoders.

- Validate focused post/comment/reaction tests, full radroots_events_codec tests, crate check, diff check, and refreshed coverage run.

Diffstat:
Mcrates/events_codec/tests/comment.rs | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/events_codec/tests/post.rs | 72++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/events_codec/tests/reaction.rs | 81+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 241 insertions(+), 0 deletions(-)

diff --git a/crates/events_codec/tests/comment.rs b/crates/events_codec/tests/comment.rs @@ -120,6 +120,65 @@ fn comment_build_tags_requires_strict_nip22_target_fields() { comment_build_tags(&comment), Err(EventEncodeError::EmptyRequiredField("root")) )); + + let comment = RadrootsComment { + root: RadrootsSocialTarget::Event { + id: ROOT_ID.to_string(), + author: Some(AUTHOR.to_string()), + event_kind: None, + relays: None, + }, + parent: event_target(PARENT_ID, PARENT_AUTHOR, KIND_ARTICLE), + content: "hello".to_string(), + }; + assert!(matches!( + comment_build_tags(&comment), + Err(EventEncodeError::EmptyRequiredField("root")) + )); + + let comment = RadrootsComment { + root: RadrootsSocialTarget::Address { + address: format!("{KIND_ARTICLE}:{AUTHOR}:{D_TAG}"), + author: Some(AUTHOR.to_string()), + event_kind: Some(KIND_COMMENT), + relays: None, + }, + parent: event_target(PARENT_ID, PARENT_AUTHOR, KIND_ARTICLE), + content: "hello".to_string(), + }; + assert!(matches!( + comment_build_tags(&comment), + Err(EventEncodeError::InvalidField("root")) + )); + + let comment = RadrootsComment { + root: RadrootsSocialTarget::Address { + address: format!("{KIND_ARTICLE}:{AUTHOR}:{D_TAG}"), + author: Some("other_author".to_string()), + event_kind: Some(KIND_ARTICLE), + relays: None, + }, + parent: event_target(PARENT_ID, PARENT_AUTHOR, KIND_ARTICLE), + content: "hello".to_string(), + }; + assert!(matches!( + comment_build_tags(&comment), + Err(EventEncodeError::InvalidField("root")) + )); + + let comment = RadrootsComment { + root: RadrootsSocialTarget::External { + id: "external-root".to_string(), + external_kind: "1".to_string(), + hint: None, + }, + parent: event_target(PARENT_ID, PARENT_AUTHOR, KIND_ARTICLE), + content: "hello".to_string(), + }; + assert!(matches!( + comment_build_tags(&comment), + Err(EventEncodeError::InvalidField("root")) + )); } #[test] @@ -245,6 +304,35 @@ fn comment_roundtrips_external_targets() { } #[test] +fn comment_build_tags_covers_optional_target_branches() { + let comment = RadrootsComment { + root: RadrootsSocialTarget::Address { + address: format!("{KIND_ARTICLE}:{AUTHOR}:{D_TAG}"), + author: None, + event_kind: None, + relays: Some(vec!["wss://root-relay.example.test".to_string()]), + }, + parent: RadrootsSocialTarget::External { + id: "https://example.test/parent".to_string(), + external_kind: "web".to_string(), + hint: None, + }, + content: "hello".to_string(), + }; + let tags = comment_build_tags(&comment).unwrap(); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some("A") + && tag + .iter() + .any(|value| value == "wss://root-relay.example.test") + })); + assert!( + tags.iter() + .any(|tag| { tag.first().map(|value| value.as_str()) == Some("i") && tag.len() == 2 }) + ); +} + +#[test] fn comment_from_tags_covers_target_decode_edges() { let mut tags = event_comment_tags(); tags.push(vec![ diff --git a/crates/events_codec/tests/post.rs b/crates/events_codec/tests/post.rs @@ -202,6 +202,78 @@ fn post_to_wire_parts_roundtrips_optional_social_tags() { } #[test] +fn post_build_tags_covers_optional_social_encode_branches() { + let mut post = content_post(); + post.farm = Some(RadrootsSocialFarmAnchor { + farm: RadrootsFarmRef { + pubkey: "farm_pubkey".to_string(), + d_tag: FARM_D_TAG.to_string(), + }, + relays: Some(vec!["wss://farm-relay.example.test".to_string()]), + }); + post.address_refs = Some(vec![RadrootsSocialTarget::Address { + address: format!("30023:article_author:{ARTICLE_D_TAG}"), + author: None, + event_kind: None, + relays: Some(vec!["wss://article-relay.example.test".to_string()]), + }]); + post.quote_refs = Some(vec![ + RadrootsSocialTarget::Event { + id: QUOTE_ID.to_string(), + author: None, + event_kind: None, + relays: Some(vec!["wss://quote-relay.example.test".to_string()]), + }, + RadrootsSocialTarget::Address { + address: format!("30023:quote_author:{ARTICLE_D_TAG}"), + author: None, + event_kind: None, + relays: Some(vec!["wss://quote-address-relay.example.test".to_string()]), + }, + ]); + post.media = Some(vec![RadrootsSocialMediaMetadata { + thumbnails: Some(vec![RadrootsSocialMediaThumbnail { + url: "https://media.example.test/thumb.jpg".to_string(), + dimensions: Some(RadrootsSocialMediaDimensions { + width: 120, + height: 80, + }), + }]), + ..RadrootsSocialMediaMetadata::default() + }]); + + let tags = post_build_tags(&post).unwrap(); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some(TAG_A) + && tag + .iter() + .any(|value| value == "wss://farm-relay.example.test") + })); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some(TAG_A) + && tag + .iter() + .any(|value| value == "wss://article-relay.example.test") + })); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some(TAG_Q) + && tag + .iter() + .any(|value| value == "wss://quote-relay.example.test") + })); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some(TAG_Q) + && tag + .iter() + .any(|value| value == "wss://quote-address-relay.example.test") + })); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some(TAG_IMETA) + && tag.iter().any(|value| value == "dim 120x80") + })); +} + +#[test] fn post_social_tags_reject_malformed_supported_structures() { let mut post = content_post(); post.address_refs = Some(vec![RadrootsSocialTarget::Event { diff --git a/crates/events_codec/tests/reaction.rs b/crates/events_codec/tests/reaction.rs @@ -96,6 +96,48 @@ fn reaction_build_tags_requires_valid_event_or_address_target() { reaction_build_tags(&reaction), Err(EventEncodeError::InvalidField("target")) )); + + let reaction = RadrootsReaction { + target: RadrootsSocialTarget::Event { + id: EVENT_ID.to_string(), + author: Some(" ".to_string()), + event_kind: Some(KIND_ARTICLE), + relays: None, + }, + content: "+".to_string(), + }; + assert!(matches!( + reaction_build_tags(&reaction), + Err(EventEncodeError::EmptyRequiredField("target.author")) + )); + + let reaction = RadrootsReaction { + target: RadrootsSocialTarget::Address { + address: format!("{}:{AUTHOR}:{D_TAG}", KIND_ARTICLE), + author: Some(AUTHOR.to_string()), + event_kind: Some(KIND_COMMENT), + relays: None, + }, + content: "+".to_string(), + }; + assert!(matches!( + reaction_build_tags(&reaction), + Err(EventEncodeError::InvalidField("target.kind")) + )); + + let reaction = RadrootsReaction { + target: RadrootsSocialTarget::Address { + address: format!("{}:{AUTHOR}:{D_TAG}", KIND_ARTICLE), + author: Some("other_author".to_string()), + event_kind: Some(KIND_ARTICLE), + relays: None, + }, + content: "+".to_string(), + }; + assert!(matches!( + reaction_build_tags(&reaction), + Err(EventEncodeError::InvalidField("target.author")) + )); } #[test] @@ -113,6 +155,45 @@ fn reaction_to_wire_parts_accepts_empty_plus_minus_emoji_and_custom_content() { } #[test] +fn reaction_build_tags_covers_optional_target_branches() { + let reaction = RadrootsReaction { + target: RadrootsSocialTarget::Event { + id: EVENT_ID.to_string(), + author: None, + event_kind: None, + relays: Some(vec!["wss://event-relay.example.test".to_string()]), + }, + content: "+".to_string(), + }; + let tags = reaction_build_tags(&reaction).unwrap(); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some("e") + && tag + .iter() + .any(|value| value == "wss://event-relay.example.test") + })); + assert!(!tags.iter().any(|tag| tag[0] == "p")); + assert!(!tags.iter().any(|tag| tag[0] == "k")); + + let reaction = RadrootsReaction { + target: RadrootsSocialTarget::Address { + address: format!("{}:{AUTHOR}:{D_TAG}", KIND_ARTICLE), + author: None, + event_kind: None, + relays: Some(vec!["wss://address-relay.example.test".to_string()]), + }, + content: "+".to_string(), + }; + let tags = reaction_build_tags(&reaction).unwrap(); + assert!(tags.iter().any(|tag| { + tag.first().map(|value| value.as_str()) == Some("a") + && tag + .iter() + .any(|value| value == "wss://address-relay.example.test") + })); +} + +#[test] fn reaction_to_wire_parts_with_kind_rejects_non_reaction_kind() { let reaction = RadrootsReaction { target: event_target(),