cli

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

commit 9ddae2a6d3feab350d1cd745101a99b08e4215dc
parent 91c01c095e4841939bbfee910a15ddfddecce1b6
Author: triesap <tyson@radroots.org>
Date:   Thu, 30 Apr 2026 02:29:52 +0000

tests: cover order lifecycle edges

Diffstat:
Msrc/runtime/order.rs | 213+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 213 insertions(+), 0 deletions(-)

diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -7522,6 +7522,61 @@ mod tests { } #[test] + fn order_status_from_receipt_reports_request_cancellation_decision_fork() { + let fixture = order_status_fixture(); + let decision_event = signed_order_decision_event( + &fixture.seller, + &fixture.request_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsTradeOrderDecision::Accepted { + inventory_commitments: vec![RadrootsTradeInventoryCommitment { + bin_id: "bin-1".to_owned(), + bin_count: 2, + }], + }, + ); + let cancellation_event = signed_order_cancellation_event( + &fixture.buyer, + &fixture.request_event, + &fixture.request_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + "buyer cancelled", + ); + let mut expected_event_ids = vec![ + decision_event.id.to_string(), + cancellation_event.id.to_string(), + ]; + expected_event_ids.sort(); + let receipt = DirectRelayFetchReceipt { + target_relays: vec!["ws://relay.test".to_owned()], + connected_relays: vec!["ws://relay.test".to_owned()], + failed_relays: Vec::new(), + events: vec![ + fixture.request_event.clone(), + decision_event, + cancellation_event, + ], + }; + + let view = order_status_from_receipt(fixture.order_id.as_str(), receipt); + let lifecycle = view.lifecycle.as_ref().expect("lifecycle view"); + + assert_eq!(view.state, "invalid"); + assert_eq!(lifecycle.phase, "invalid"); + assert_eq!(lifecycle.terminal, true); + assert_eq!(view.reducer_issues.len(), 1); + assert_eq!(view.reducer_issues[0].code, "forked_lifecycle"); + assert_eq!(view.reducer_issues[0].event_ids, expected_event_ids); + assert_eq!(lifecycle.issues[0].code, "forked_lifecycle"); + } + + #[test] fn order_cancellation_event_parts_chain_from_request_or_decision() { let fixture = order_status_fixture(); let dir = tempdir().expect("tempdir"); @@ -7780,6 +7835,86 @@ mod tests { } #[test] + fn order_cancellation_preflight_rejects_completed_order_as_terminal() { + let dir = tempdir().expect("tempdir"); + let mut config = sample_config(dir.path()); + config.relay.urls = vec!["ws://relay.test".to_owned()]; + let fixture = order_status_fixture(); + let decision_event = signed_order_decision_event( + &fixture.seller, + &fixture.request_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsTradeOrderDecision::Accepted { + inventory_commitments: vec![RadrootsTradeInventoryCommitment { + bin_id: "bin-1".to_owned(), + bin_count: 2, + }], + }, + ); + let fulfillment_event = signed_fulfillment_update_event( + &fixture.seller, + &fixture.request_event, + &decision_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsActiveTradeFulfillmentState::Delivered, + ); + let receipt_event = signed_buyer_receipt_event( + &fixture.buyer, + &fixture.request_event, + &fulfillment_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + true, + None, + ); + let receipt_event_id = receipt_event.id.to_string(); + let status_view = order_status_from_receipt( + fixture.order_id.as_str(), + DirectRelayFetchReceipt { + target_relays: vec!["ws://relay.test".to_owned()], + connected_relays: vec!["ws://relay.test".to_owned()], + failed_relays: Vec::new(), + events: vec![ + fixture.request_event.clone(), + decision_event, + fulfillment_event, + receipt_event, + ], + }, + ); + let args = cancel_args_for_fixture(&fixture, "buyer cancelled"); + + let view = order_cancellation_preflight_view_from_status( + &config, + &args, + &status_view, + fixture.buyer_pubkey.as_str(), + ) + .expect("completed cancellation preflight"); + + assert_eq!(view.state, "terminal"); + assert_eq!( + view.prev_event_id.as_deref(), + Some(receipt_event_id.as_str()) + ); + assert_eq!(view.event_id, None); + assert!( + view.reason + .as_deref() + .expect("reason") + .contains("already terminal") + ); + } + + #[test] fn order_status_from_receipt_reports_completed_buyer_receipt() { let fixture = order_status_fixture(); let decision_event = signed_order_decision_event( @@ -7955,6 +8090,84 @@ mod tests { } #[test] + fn order_status_from_receipt_reports_receipt_fulfillment_fork() { + let fixture = order_status_fixture(); + let decision_event = signed_order_decision_event( + &fixture.seller, + &fixture.request_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsTradeOrderDecision::Accepted { + inventory_commitments: vec![RadrootsTradeInventoryCommitment { + bin_id: "bin-1".to_owned(), + bin_count: 2, + }], + }, + ); + let ready_fulfillment_event = signed_fulfillment_update_event( + &fixture.seller, + &fixture.request_event, + &decision_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsActiveTradeFulfillmentState::ReadyForPickup, + ); + let delivered_fulfillment_event = signed_fulfillment_update_event( + &fixture.seller, + &fixture.request_event, + &ready_fulfillment_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + RadrootsActiveTradeFulfillmentState::Delivered, + ); + let receipt_event = signed_buyer_receipt_event( + &fixture.buyer, + &fixture.request_event, + &ready_fulfillment_event, + fixture.order_id.as_str(), + fixture.listing_addr.as_str(), + fixture.buyer_pubkey.as_str(), + fixture.seller_pubkey.as_str(), + true, + None, + ); + let mut expected_event_ids = vec![ + delivered_fulfillment_event.id.to_string(), + receipt_event.id.to_string(), + ]; + expected_event_ids.sort(); + let receipt = DirectRelayFetchReceipt { + target_relays: vec!["ws://relay.test".to_owned()], + connected_relays: vec!["ws://relay.test".to_owned()], + failed_relays: Vec::new(), + events: vec![ + fixture.request_event.clone(), + decision_event, + ready_fulfillment_event, + delivered_fulfillment_event, + receipt_event, + ], + }; + + let view = order_status_from_receipt(fixture.order_id.as_str(), receipt); + let lifecycle = view.lifecycle.as_ref().expect("lifecycle view"); + + assert_eq!(view.state, "invalid"); + assert_eq!(lifecycle.phase, "invalid"); + assert_eq!(lifecycle.terminal, true); + assert_eq!(view.reducer_issues.len(), 1); + assert_eq!(view.reducer_issues[0].code, "forked_lifecycle"); + assert_eq!(view.reducer_issues[0].event_ids, expected_event_ids); + assert_eq!(lifecycle.issues[0].code, "forked_lifecycle"); + } + + #[test] fn order_receipt_event_parts_chain_from_latest_eligible_fulfillment() { let fixture = order_status_fixture(); let dir = tempdir().expect("tempdir");