radrootsd

JSON-RPC bridge for Radroots event publishing
git clone https://radroots.dev/git/radrootsd.git
Log | Files | Refs | README | LICENSE

commit 08b11e422e1ed26a146aa66ab17d3f8f0fc1ffc2
parent 8dbd0e0c004dd79dd833b39cb14e4e028f9bee09
Author: triesap <triesap@radroots.dev>
Date:   Sat,  3 Jan 2026 17:20:14 +0000

post: extract row builder and add tests

- Factor post event mapping and created_at sorting into build_post_rows
- Keep handler focused on filter construction and fetch execution
- Add unit test for descending created_at ordering
- Add unit test to verify content and tags are preserved

Diffstat:
Msrc/api/jsonrpc/methods/events/post/list.rs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 69 insertions(+), 2 deletions(-)

diff --git a/src/api/jsonrpc/methods/events/post/list.rs b/src/api/jsonrpc/methods/events/post/list.rs @@ -13,6 +13,7 @@ use crate::api::jsonrpc::params::{ }; use crate::api::jsonrpc::{MethodRegistry, RpcContext, RpcError}; use radroots_nostr::prelude::{ + RadrootsNostrEvent, RadrootsNostrFilter, RadrootsNostrKind, }; @@ -22,6 +23,15 @@ struct PostListResponse { posts: Vec<NostrEventView>, } +fn build_post_rows<I>(events: I) -> Vec<NostrEventView> +where + I: IntoIterator<Item = RadrootsNostrEvent>, +{ + let mut items = events.into_iter().map(|ev| event_view(&ev)).collect::<Vec<_>>(); + items.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + items +} + pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Result<()> { registry.track("events.post.list"); m.register_async_method("events.post.list", |params, ctx, _| async move { @@ -59,11 +69,68 @@ pub fn register(m: &mut RpcModule<RpcContext>, registry: &MethodRegistry) -> Res .await .map_err(|e| RpcError::Other(format!("fetch failed: {e}")))?; - let mut items = events.into_iter().map(|ev| event_view(&ev)).collect::<Vec<_>>(); - items.sort_by(|a, b| b.created_at.cmp(&a.created_at)); + let items = build_post_rows(events); Ok::<PostListResponse, RpcError>(PostListResponse { posts: items }) })?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::build_post_rows; + use radroots_nostr::prelude::RadrootsNostrEvent; + use serde_json::json; + + fn post_event( + id: &str, + pubkey: &str, + created_at: u64, + content: &str, + tags: Vec<Vec<String>>, + ) -> RadrootsNostrEvent { + let sig = format!("{:0128x}", 4); + let event_json = json!({ + "id": id, + "pubkey": pubkey, + "created_at": created_at, + "kind": 1, + "tags": tags, + "content": content, + "sig": sig, + }); + serde_json::from_value(event_json).expect("event") + } + + #[test] + fn post_list_sorts_by_created_at_desc() { + let pubkey = "1bdebe7b23fccb167fc8843280b789839dfa296ae9fd86cc9769b4813d76d8a4"; + let old_id = format!("{:064x}", 1); + let new_id = format!("{:064x}", 2); + let older = post_event(&old_id, pubkey, 100, "old", Vec::new()); + let newer = post_event(&new_id, pubkey, 200, "new", Vec::new()); + + let posts = build_post_rows(vec![older, newer]); + + assert_eq!(posts.len(), 2); + assert_eq!(posts[0].id, new_id); + assert_eq!(posts[0].created_at, 200); + assert_eq!(posts[1].id, old_id); + assert_eq!(posts[1].created_at, 100); + } + + #[test] + fn post_list_preserves_content_and_tags() { + let pubkey = "2bdebe7b23fccb167fc8843280b789839dfa296ae9fd86cc9769b4813d76d8a4"; + let id = format!("{:064x}", 3); + let tags = vec![vec!["t".to_string(), "radroots".to_string()]]; + let event = post_event(&id, pubkey, 300, "hello", tags.clone()); + + let posts = build_post_rows(vec![event]); + + assert_eq!(posts.len(), 1); + assert_eq!(posts[0].content, "hello"); + assert_eq!(posts[0].tags, tags); + } +}