cli

Command-line interface for Radroots
git clone https://radroots.dev/git/cli.git
Log | Files | Refs | README | LICENSE

commit 8e121e956007df5772e5777728c177a4a081be4a
parent 2b6ae34c1c4b4e4f53e526cf9ebef53ca922b725
Author: triesap <tyson@radroots.org>
Date:   Fri,  8 May 2026 15:37:33 +0000

order: fix event list recovery actions

- split missing relay and missing seller account event list actions
- expose relay rerun recovery for unconfigured order status
- keep seller account recovery on the account setup path
- cover event list recovery actions in order adapter tests

Diffstat:
Msrc/operation_order.rs | 70+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msrc/runtime/order.rs | 12++++++++++--
2 files changed, 71 insertions(+), 11 deletions(-)

diff --git a/src/operation_order.rs b/src/operation_order.rs @@ -1833,6 +1833,15 @@ mod tests { assert_eq!(detail["fetched_count"], 0); assert_eq!(detail["decoded_count"], 0); assert_eq!(detail["skipped_count"], 0); + let envelope = crate::output_contract::OutputEnvelope::failure( + "order.status.get", + output_error, + OperationContext::default().envelope_context("req_order_status"), + ); + assert_eq!( + envelope.next_actions[0].command, + "radroots --relay wss://relay.example.com order status get ord_pending" + ); } #[test] @@ -1840,19 +1849,62 @@ mod tests { let dir = tempdir().expect("tempdir"); let config = sample_config(dir.path()); let service = OperationAdapter::new(OrderOperationService::new(&config)); - let request = OperationRequest::new( - OperationContext::default(), - OrderEventListRequest::default(), - ) - .expect("order event list request"); - let envelope = service + let context = OperationContext::default(); + let request = OperationRequest::new(context.clone(), OrderEventListRequest::default()) + .expect("order event list request"); + let output_error = service .execute(request) .expect_err("order event list unconfigured") .to_output_error(); + let envelope = crate::output_contract::OutputEnvelope::failure( + "order.event.list", + output_error, + context.envelope_context("req_order_event_list"), + ); - assert_eq!(envelope.code, "operation_unavailable"); - assert_eq!(envelope.exit_code, 3); - assert!(envelope.message.contains("configured relay")); + assert_eq!(envelope.errors[0].code, "operation_unavailable"); + assert_eq!(envelope.errors[0].exit_code, 3); + assert!(envelope.errors[0].message.contains("configured relay")); + assert_eq!( + envelope.errors[0].detail.as_ref().unwrap()["actions"][0], + "radroots --relay wss://relay.example.com order event list" + ); + assert_eq!( + envelope.next_actions[0].command, + "radroots --relay wss://relay.example.com order event list" + ); + } + + #[test] + fn order_event_list_requires_seller_account_with_account_action() { + let dir = tempdir().expect("tempdir"); + let mut config = sample_config(dir.path()); + config.relay.urls = vec!["ws://127.0.0.1:9".to_owned()]; + let service = OperationAdapter::new(OrderOperationService::new(&config)); + let context = OperationContext::default(); + let request = OperationRequest::new(context.clone(), OrderEventListRequest::default()) + .expect("order event list request"); + let output_error = service + .execute(request) + .expect_err("order event list missing account") + .to_output_error(); + let envelope = crate::output_contract::OutputEnvelope::failure( + "order.event.list", + output_error, + context.envelope_context("req_order_event_list"), + ); + + assert_eq!(envelope.errors[0].code, "operation_unavailable"); + assert!( + envelope.errors[0] + .message + .contains("selected seller account") + ); + assert_eq!( + envelope.errors[0].detail.as_ref().unwrap()["actions"][0], + "radroots account create" + ); + assert_eq!(envelope.next_actions[0].command, "radroots account create"); } #[test] diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -118,6 +118,8 @@ const ORDER_EVENT_LIST_SOURCE: &str = "direct Nostr relay fetch · selected sell const ORDER_STATUS_SOURCE: &str = "direct Nostr relay status fetch · active order reducer"; const ORDER_EVENT_WATCH_UNAVAILABLE_REASON: &str = "relay-backed order event watch is not implemented"; +const ORDER_EVENT_LIST_RELAY_ACTION: &str = + "radroots --relay wss://relay.example.com order event list"; const ORDERS_DIR: &str = "orders/drafts"; static ORDER_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -772,6 +774,7 @@ pub fn history( None, "order event list requires at least one configured relay".to_owned(), Vec::new(), + vec![ORDER_EVENT_LIST_RELAY_ACTION.to_owned()], )); } @@ -782,6 +785,7 @@ pub fn history( None, "order event list requires a selected seller account".to_owned(), config.relay.urls.clone(), + vec!["radroots account create".to_owned()], )); } }; @@ -1713,7 +1717,10 @@ pub fn status( decoded_count: 0, skipped_count: 0, reason: Some("order status get requires at least one configured relay".to_owned()), - actions: Vec::new(), + actions: vec![format!( + "radroots --relay wss://relay.example.com order status get {}", + args.key + )], }); } @@ -4098,6 +4105,7 @@ fn order_history_unconfigured( seller_pubkey: Option<String>, reason: String, target_relays: Vec<String>, + actions: Vec<String>, ) -> OrderHistoryView { OrderHistoryView { state: "unconfigured".to_owned(), @@ -4112,7 +4120,7 @@ fn order_history_unconfigured( count: 0, reason: Some(reason), orders: Vec::new(), - actions: vec!["radroots account create".to_owned()], + actions, } }