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:
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(),