commit 63599d4d1ed8585f9f10a2097310dd9d385cd474
parent 27a35becfbfae9977533c6700ac77869d0197f1d
Author: triesap <tyson@radroots.org>
Date: Sun, 21 Jun 2026 21:14:02 +0000
events-codec: cover relay list validation
- Cover relay list encode/decode validation for ws relay URLs, non-r tags, missing URLs, and oversized marker shapes.
- Preserve existing NIP-65 roundtrip assertions while covering both ws and wss accepted relay forms.
- Validate the list target, full radroots_events_codec tests, crate check, diff check, and refreshed coverage run.
Diffstat:
1 file changed, 82 insertions(+), 2 deletions(-)
diff --git a/crates/events_codec/tests/list.rs b/crates/events_codec/tests/list.rs
@@ -202,23 +202,74 @@ fn relay_list_kind_roundtrips_nip65_r_tags() {
tag: TAG_R.to_string(),
values: vec!["wss://both.example.test".to_string()],
},
+ RadrootsListEntry {
+ tag: TAG_R.to_string(),
+ values: vec!["ws://local-relay.example.test".to_string()],
+ },
],
};
let parts = to_wire_parts_with_kind(&list, KIND_LIST_READ_WRITE_RELAYS).unwrap();
assert_eq!(parts.kind, KIND_LIST_READ_WRITE_RELAYS);
assert!(parts.content.is_empty());
- assert_eq!(parts.tags.len(), 3);
+ assert_eq!(parts.tags.len(), 4);
let decoded = list_from_tags(parts.kind, parts.content, &parts.tags).unwrap();
- assert_eq!(decoded.entries.len(), 3);
+ assert_eq!(decoded.entries.len(), 4);
assert_eq!(decoded.entries[0].values[1], "read");
assert_eq!(decoded.entries[1].values[1], "write");
assert_eq!(decoded.entries[2].values.len(), 1);
+ assert_eq!(
+ decoded.entries[3].values[0],
+ "ws://local-relay.example.test"
+ );
}
#[test]
fn relay_list_kind_validates_url_shape_and_markers() {
+ let invalid_tag = RadrootsList {
+ content: String::new(),
+ entries: vec![RadrootsListEntry {
+ tag: "p".to_string(),
+ values: vec!["wss://relay.example.test".to_string()],
+ }],
+ };
+ assert!(matches!(
+ to_wire_parts_with_kind(&invalid_tag, KIND_LIST_READ_WRITE_RELAYS),
+ Err(EventEncodeError::InvalidField("relay.tag"))
+ ));
+ assert!(matches!(
+ list_from_tags(
+ KIND_LIST_READ_WRITE_RELAYS,
+ String::new(),
+ &[vec![
+ "p".to_string(),
+ "wss://relay.example.test".to_string()
+ ]]
+ ),
+ Err(EventParseError::InvalidTag(TAG_R))
+ ));
+
+ let missing_url = RadrootsList {
+ content: String::new(),
+ entries: vec![RadrootsListEntry {
+ tag: TAG_R.to_string(),
+ values: Vec::new(),
+ }],
+ };
+ assert!(matches!(
+ to_wire_parts_with_kind(&missing_url, KIND_LIST_READ_WRITE_RELAYS),
+ Err(EventEncodeError::EmptyRequiredField("relay.url"))
+ ));
+ assert!(matches!(
+ list_from_tags(
+ KIND_LIST_READ_WRITE_RELAYS,
+ String::new(),
+ &[vec![TAG_R.to_string()]]
+ ),
+ Err(EventParseError::InvalidTag(TAG_R))
+ ));
+
let invalid_url = RadrootsList {
content: String::new(),
entries: vec![RadrootsListEntry {
@@ -266,6 +317,35 @@ fn relay_list_kind_validates_url_shape_and_markers() {
Err(EventParseError::InvalidTag(TAG_R))
));
+ let extra_marker = RadrootsList {
+ content: String::new(),
+ entries: vec![RadrootsListEntry {
+ tag: TAG_R.to_string(),
+ values: vec![
+ "wss://relay.example.test".to_string(),
+ "read".to_string(),
+ "write".to_string(),
+ ],
+ }],
+ };
+ assert!(matches!(
+ to_wire_parts_with_kind(&extra_marker, KIND_LIST_READ_WRITE_RELAYS),
+ Err(EventEncodeError::InvalidField("relay.marker"))
+ ));
+ assert!(matches!(
+ list_from_tags(
+ KIND_LIST_READ_WRITE_RELAYS,
+ String::new(),
+ &[vec![
+ TAG_R.to_string(),
+ "wss://relay.example.test".to_string(),
+ "read".to_string(),
+ "write".to_string()
+ ]]
+ ),
+ Err(EventParseError::InvalidTag(TAG_R))
+ ));
+
let empty = RadrootsList {
content: String::new(),
entries: Vec::new(),