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:
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,
}
}