cli

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

commit 2b5e5f404e01bfacd0341b7020813d68271235aa
parent 7a9e205d44cc009b6e9fbfd1b94cfd2f44852a28
Author: triesap <tyson@radroots.org>
Date:   Sun, 10 May 2026 02:46:39 +0000

order: bind new drafts to buyer actors

- add buyer_actor order draft schema and creation output fields
- require a resolved local buyer account before quote draft creation
- preserve protocol buyer_pubkey from the bound buyer actor
- cover quote creation, dry-run preview, and missing buyer account errors

Diffstat:
Msrc/domain/runtime.rs | 10++++++++++
Msrc/operation_basket.rs | 70++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msrc/runtime/order.rs | 192+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
Mtests/target_cli.rs | 10++++++++++
4 files changed, 228 insertions(+), 54 deletions(-)

diff --git a/src/domain/runtime.rs b/src/domain/runtime.rs @@ -1164,6 +1164,8 @@ pub struct OrderNewView { #[serde(skip_serializing_if = "Option::is_none")] pub buyer_pubkey: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] + pub buyer_actor_source: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] pub seller_pubkey: Option<String>, pub ready_for_submit: bool, #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -1205,6 +1207,8 @@ pub struct OrderGetView { #[serde(skip_serializing_if = "Option::is_none")] pub buyer_pubkey: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] + pub buyer_actor_source: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] pub seller_pubkey: Option<String>, pub ready_for_submit: bool, #[serde(default, skip_serializing_if = "Vec::is_empty")] @@ -1271,6 +1275,8 @@ pub struct OrderSubmitView { #[serde(skip_serializing_if = "Option::is_none")] pub buyer_pubkey: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] + pub buyer_actor_source: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] pub seller_pubkey: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub event_id: Option<String>, @@ -2225,6 +2231,10 @@ pub struct OrderSummaryView { pub listing_event_id: Option<String>, #[serde(skip_serializing_if = "Option::is_none")] pub buyer_account_id: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub buyer_pubkey: Option<String>, + #[serde(skip_serializing_if = "Option::is_none")] + pub buyer_actor_source: Option<String>, pub item_count: usize, #[serde(skip_serializing_if = "Option::is_none")] pub economics: Option<RadrootsTradeOrderEconomics>, diff --git a/src/operation_basket.rs b/src/operation_basket.rs @@ -17,7 +17,6 @@ use crate::operation_adapter::{ BasketValidateResult, OperationAdapterError, OperationRequest, OperationRequestData, OperationRequestPayload, OperationResult, OperationResultData, OperationService, }; -use crate::runtime::RuntimeError; use crate::runtime::config::RuntimeConfig; use crate::runtime_args::{OrderDraftAdjustmentArgs, OrderDraftCreateArgs}; @@ -468,7 +467,7 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> { .expect("validated basket has one item") .clone(); if request.context.dry_run { - let order = map_runtime(crate::runtime::order::scaffold_preflight( + let order = crate::runtime::order::scaffold_preflight( self.config, &OrderDraftCreateArgs { listing: item.listing.clone(), @@ -477,7 +476,10 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> { bin_count: Some(item.quantity), adjustments: order_adjustments_from_basket(&loaded.document), }, - ))?; + ) + .map_err(|error| { + OperationAdapterError::runtime_failure(request.operation_id(), error) + })?; return json_operation_result::<BasketQuoteCreateResult>(json!({ "state": "dry_run", "source": BASKET_QUOTE_SOURCE, @@ -489,7 +491,7 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> { })); } - let order = map_runtime(crate::runtime::order::scaffold( + let order = crate::runtime::order::scaffold( self.config, &OrderDraftCreateArgs { listing: item.listing.clone(), @@ -498,7 +500,8 @@ impl OperationService<BasketQuoteCreateRequest> for BasketOperationService<'_> { bin_count: Some(item.quantity), adjustments: order_adjustments_from_basket(&loaded.document), }, - ))?; + ) + .map_err(|error| OperationAdapterError::runtime_failure(request.operation_id(), error))?; let quote_economics = order.economics.clone(); let quote = BasketQuote { quote_id: quote_economics @@ -1032,10 +1035,6 @@ where OperationResult::new(R::from_value(value)) } -fn map_runtime<T>(result: Result<T, RuntimeError>) -> Result<T, OperationAdapterError> { - result.map_err(|error| OperationAdapterError::Runtime(error.to_string())) -} - fn string_input<P>(request: &OperationRequest<P>, key: &str) -> Option<String> where P: OperationRequestPayload + OperationRequestData, @@ -1071,6 +1070,7 @@ mod tests { BasketListRequest, BasketQuoteCreateRequest, BasketValidateRequest, OperationAdapter, OperationContext, OperationData, OperationRequest, }; + use crate::runtime::accounts; use crate::runtime::config::{ AccountConfig, AccountSecretContractConfig, HyfConfig, IdentityConfig, InteractionConfig, LocalConfig, LoggingConfig, MigrationConfig, MycConfig, OutputConfig, OutputFormat, @@ -1242,6 +1242,7 @@ mod tests { fn basket_quote_create_materializes_order_draft() { let dir = tempdir().expect("tempdir"); let config = sample_config(dir.path()); + accounts::create_or_migrate_default_account(&config).expect("create buyer account"); let service = OperationAdapter::new(BasketOperationService::new(&config)); create_basket(&service, "basket_quote"); add_listing_item(&service, "basket_quote"); @@ -1265,13 +1266,36 @@ mod tests { .unwrap() .starts_with("ord_") ); - assert!(PathBuf::from(envelope.result["quote"]["order_file"].as_str().unwrap()).exists()); + assert!( + envelope.result["order"]["buyer_account_id"] + .as_str() + .expect("buyer account id") + .len() + > 8 + ); + assert!( + envelope.result["order"]["buyer_pubkey"] + .as_str() + .expect("buyer pubkey") + .len() + == 64 + ); + assert_eq!( + envelope.result["order"]["buyer_actor_source"], + "resolved_account" + ); + let order_file = PathBuf::from(envelope.result["quote"]["order_file"].as_str().unwrap()); + assert!(order_file.exists()); + let draft = std::fs::read_to_string(order_file).expect("read order draft"); + assert!(draft.contains("[buyer_actor]")); + assert!(draft.contains("source = \"resolved_account\"")); } #[test] fn basket_quote_create_dry_run_skips_order_draft() { let dir = tempdir().expect("tempdir"); let config = sample_config(dir.path()); + accounts::create_or_migrate_default_account(&config).expect("create buyer account"); let service = OperationAdapter::new(BasketOperationService::new(&config)); create_basket(&service, "basket_dry_run"); add_listing_item(&service, "basket_dry_run"); @@ -1293,9 +1317,35 @@ mod tests { assert_eq!(envelope.dry_run, true); assert_eq!(envelope.result["state"], "dry_run"); assert_eq!(envelope.result["order"]["state"], "dry_run"); + assert_eq!( + envelope.result["order"]["buyer_actor_source"], + "resolved_account" + ); assert!(!PathBuf::from(envelope.result["order"]["file"].as_str().unwrap()).exists()); } + #[test] + fn basket_quote_create_requires_resolved_buyer_account() { + let dir = tempdir().expect("tempdir"); + let config = sample_config(dir.path()); + let service = OperationAdapter::new(BasketOperationService::new(&config)); + create_basket(&service, "basket_no_buyer"); + add_listing_item(&service, "basket_no_buyer"); + + let quote = OperationRequest::new( + OperationContext::default(), + BasketQuoteCreateRequest::from_data(data(&[("basket_id", "basket_no_buyer")])), + ) + .expect("basket quote request"); + let error = service.execute(quote).expect_err("missing buyer account"); + + let output_error = error.to_output_error(); + assert_eq!(output_error.code, "account_unresolved"); + let detail = output_error.detail.expect("account detail"); + assert_eq!(detail["buyer_actor_source"], "resolved_account"); + assert_eq!(detail["actions"][0], "radroots account create"); + } + fn create_basket(service: &OperationAdapter<BasketOperationService<'_>>, basket_id: &str) { let request = OperationRequest::new( OperationContext::default(), diff --git a/src/runtime/order.rs b/src/runtime/order.rs @@ -78,6 +78,7 @@ use radroots_trade::order::{ reduce_active_order_events, reduce_listing_inventory_accounting, }; use serde::{Deserialize, Serialize}; +use serde_json::json; use crate::domain::runtime::{ OrderCancellationView, OrderDecisionView, OrderDraftItemView, OrderEventListEntryView, @@ -123,6 +124,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_LIST_RELAY_ACTION: &str = "radroots --relay wss://relay.example.com order event list"; +const ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT: &str = "resolved_account"; +const ORDER_BUYER_ACTOR_SOURCE_REBIND: &str = "order_rebind"; const ORDERS_DIR: &str = "orders/drafts"; static ORDER_COUNTER: AtomicU64 = AtomicU64::new(0); @@ -133,10 +136,9 @@ struct OrderDraftDocument { version: u32, kind: String, order: OrderDraft, + buyer_actor: OrderDraftBuyerActor, #[serde(default, skip_serializing_if = "Option::is_none")] listing_lookup: Option<String>, - #[serde(default, skip_serializing_if = "Option::is_none")] - buyer_account_id: Option<String>, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -164,6 +166,14 @@ struct OrderDraftItem { bin_count: u32, } +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +struct OrderDraftBuyerActor { + account_id: String, + pubkey: String, + source: String, +} + #[derive(Debug, Clone)] struct LoadedOrderDraft { file: PathBuf, @@ -289,14 +299,8 @@ pub fn scaffold( explicit_listing_addr.as_deref(), )?; - let selected_account = accounts::resolve_account(config)?; - let buyer_account_id = selected_account - .as_ref() - .map(|account| account.record.account_id.to_string()); - let buyer_pubkey = selected_account - .as_ref() - .map(|account| account.record.public_identity.public_key_hex.clone()) - .unwrap_or_default(); + let buyer_actor = resolve_initial_buyer_actor(config)?; + let buyer_pubkey = buyer_actor.pubkey.clone(); let listing_addr = resolved_listing .as_ref() @@ -342,8 +346,8 @@ pub fn scaffold( items, economics, }, + buyer_actor, listing_lookup, - buyer_account_id, }; save_draft(file.as_path(), &document)?; @@ -373,14 +377,8 @@ pub fn scaffold_preflight( explicit_listing_addr.as_deref(), )?; - let selected_account = accounts::resolve_account(config)?; - let buyer_account_id = selected_account - .as_ref() - .map(|account| account.record.account_id.to_string()); - let buyer_pubkey = selected_account - .as_ref() - .map(|account| account.record.public_identity.public_key_hex.clone()) - .unwrap_or_default(); + let buyer_actor = resolve_initial_buyer_actor(config)?; + let buyer_pubkey = buyer_actor.pubkey.clone(); let listing_addr = resolved_listing .as_ref() @@ -423,8 +421,8 @@ pub fn scaffold_preflight( items, economics, }, + buyer_actor, listing_lookup, - buyer_account_id, }; let mut view: OrderNewView = view_from_loaded(LoadedOrderDraft { @@ -455,6 +453,7 @@ pub fn get(config: &RuntimeConfig, args: &RecordLookupArgs) -> Result<OrderGetVi listing_event_id: None, buyer_account_id: None, buyer_pubkey: None, + buyer_actor_source: None, seller_pubkey: None, ready_for_submit: false, items: Vec::new(), @@ -484,6 +483,7 @@ pub fn get(config: &RuntimeConfig, args: &RecordLookupArgs) -> Result<OrderGetVi listing_event_id: None, buyer_account_id: None, buyer_pubkey: None, + buyer_actor_source: None, seller_pubkey: None, ready_for_submit: false, items: Vec::new(), @@ -568,6 +568,7 @@ pub fn submit( listing_event_id: None, buyer_account_id: None, buyer_pubkey: None, + buyer_actor_source: None, seller_pubkey: None, event_id: None, event_kind: None, @@ -604,6 +605,7 @@ pub fn submit( listing_event_id: None, buyer_account_id: None, buyer_pubkey: None, + buyer_actor_source: None, seller_pubkey: None, event_id: None, event_kind: None, @@ -640,8 +642,9 @@ pub fn submit( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: None, @@ -8741,8 +8744,9 @@ fn view_from_loaded(loaded: LoadedOrderDraft) -> OrderGetView { listing_lookup: loaded.document.listing_lookup.clone(), listing_addr, listing_event_id, - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey, ready_for_submit, items: loaded @@ -8783,7 +8787,9 @@ fn summary_from_loaded(loaded: &LoadedOrderDraft) -> OrderSummaryView { listing_lookup: loaded.document.listing_lookup.clone(), listing_addr, listing_event_id, - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), + buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), item_count: loaded.document.order.items.len(), economics: loaded.document.order.economics.clone(), updated_at_unix: loaded.updated_at_unix, @@ -8807,6 +8813,8 @@ fn summary_for_invalid_file(path: &Path, reason: String) -> OrderSummaryView { listing_addr: None, listing_event_id: None, buyer_account_id: None, + buyer_pubkey: None, + buyer_actor_source: None, item_count: 0, economics: None, updated_at_unix: modified_unix(path).unwrap_or_default(), @@ -8946,15 +8954,48 @@ fn collect_issues(document: &OrderDraftDocument) -> Vec<OrderIssueView> { )), } - if document - .buyer_account_id - .as_deref() - .is_none_or(|value| value.trim().is_empty()) - && document.order.buyer_pubkey.trim().is_empty() + if document.buyer_actor.account_id.trim().is_empty() { + issues.push(issue( + "buyer_actor.account_id", + "buyer_actor account_id is required before order submit", + )); + } + if document.buyer_actor.pubkey.trim().is_empty() { + issues.push(issue( + "buyer_actor.pubkey", + "buyer_actor pubkey is required before order submit", + )); + } + if document.buyer_actor.source.trim().is_empty() { + issues.push(issue( + "buyer_actor.source", + "buyer_actor source is required before order submit", + )); + } else if !matches!( + document.buyer_actor.source.as_str(), + ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT | ORDER_BUYER_ACTOR_SOURCE_REBIND + ) { + issues.push(issue( + "buyer_actor.source", + format!( + "unsupported buyer_actor source `{}`", + document.buyer_actor.source + ), + )); + } + if document.order.buyer_pubkey.trim().is_empty() { + issues.push(issue( + "order.buyer_pubkey", + "order buyer_pubkey is required before order submit", + )); + } else if !document + .order + .buyer_pubkey + .eq_ignore_ascii_case(document.buyer_actor.pubkey.as_str()) { issues.push(issue( - "buyer_account_id", - "buyer account or buyer_pubkey is required before order submit", + "order.buyer_pubkey", + "order buyer_pubkey must match buyer_actor pubkey", )); } @@ -8989,8 +9030,18 @@ fn actions_for_document( "edit {} and fill the remaining draft fields", file.display() )); - if document.buyer_account_id.is_none() && document.order.buyer_pubkey.trim().is_empty() { - actions.push("radroots account create".to_owned()); + if document.buyer_actor.account_id.trim().is_empty() + || document.buyer_actor.pubkey.trim().is_empty() + || document.order.buyer_pubkey.trim().is_empty() + || !document + .order + .buyer_pubkey + .eq_ignore_ascii_case(document.buyer_actor.pubkey.as_str()) + { + actions.push(format!( + "radroots order rebind {} <selector>", + document.order.order_id + )); } if document.order.items.is_empty() || issues @@ -9002,6 +9053,38 @@ fn actions_for_document( actions } +fn resolve_initial_buyer_actor( + config: &RuntimeConfig, +) -> Result<OrderDraftBuyerActor, RuntimeError> { + let resolution = accounts::resolve_account_resolution(config)?; + let Some(account) = resolution.resolved_account else { + return Err(accounts::AccountRuntimeFailure::unresolved_with_detail( + accounts::unresolved_account_reason(config)?, + json!({ + "buyer_actor_source": ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT, + "actions": [ + "radroots account create", + "radroots account import <path>", + ], + }), + ) + .into()); + }; + Ok(OrderDraftBuyerActor { + account_id: account.record.account_id.to_string(), + pubkey: account.record.public_identity.public_key_hex, + source: ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT.to_owned(), + }) +} + +fn buyer_account_id(document: &OrderDraftDocument) -> Option<String> { + non_empty_string(document.buyer_actor.account_id.clone()) +} + +fn buyer_actor_source(document: &OrderDraftDocument) -> Option<String> { + non_empty_string(document.buyer_actor.source.clone()) +} + fn order_submit_listing_freshness_view( config: &RuntimeConfig, loaded: &LoadedOrderDraft, @@ -9246,8 +9329,9 @@ fn order_submit_unconfigured_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: None, @@ -9283,8 +9367,9 @@ fn order_submit_invalid_quantity_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: None, @@ -9553,8 +9638,9 @@ fn order_submit_deduplicated_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: Some(request.request_event_id.clone()), event_kind: Some(KIND_TRADE_ORDER_REQUEST), @@ -9591,8 +9677,9 @@ fn order_submit_dry_run_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: None, @@ -9635,8 +9722,9 @@ fn order_submit_invalid_existing_request_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: Some(KIND_TRADE_ORDER_REQUEST), @@ -9767,8 +9855,9 @@ fn published_order_submit_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: event_id.or(Some(relay.event_id)), event_kind: event_kind.or(Some(relay.event_kind)), @@ -9811,8 +9900,9 @@ fn order_binding_error_view( listing_lookup: loaded.document.listing_lookup.clone(), listing_addr: non_empty_string(loaded.document.order.listing_addr.clone()), listing_event_id: non_empty_string(loaded.document.order.listing_event_id.clone()), - buyer_account_id: loaded.document.buyer_account_id.clone(), + buyer_account_id: buyer_account_id(&loaded.document), buyer_pubkey: non_empty_string(loaded.document.order.buyer_pubkey.clone()), + buyer_actor_source: buyer_actor_source(&loaded.document), seller_pubkey: non_empty_string(loaded.document.order.seller_pubkey.clone()), event_id: None, event_kind: None, @@ -10327,6 +10417,7 @@ impl From<OrderGetView> for OrderNewView { listing_event_id: view.listing_event_id, buyer_account_id: view.buyer_account_id, buyer_pubkey: view.buyer_pubkey, + buyer_actor_source: view.buyer_actor_source, seller_pubkey: view.seller_pubkey, ready_for_submit: view.ready_for_submit, items: view.items, @@ -10383,8 +10474,9 @@ mod tests { use tempfile::tempdir; use super::{ - LoadedOrderDraft, ORDER_DRAFT_KIND, ORDER_SUBMIT_SOURCE, OrderDraft, OrderDraftDocument, - OrderDraftItem, OrderStatusContext, ResolvedOrderEconomicsProduct, ResolvedOrderListing, + LoadedOrderDraft, ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT, ORDER_DRAFT_KIND, + ORDER_SUBMIT_SOURCE, OrderDraft, OrderDraftBuyerActor, OrderDraftDocument, OrderDraftItem, + OrderStatusContext, ResolvedOrderEconomicsProduct, ResolvedOrderListing, ResolvedSellerOrderRequest, SellerOrderRequestResolution, accepted_order_decision_payload_from_request, active_request_record_from_resolved, canonical_order_request_payload_from_loaded, collect_issues, @@ -10456,8 +10548,12 @@ mod tests { 2, )), }, + buyer_actor: OrderDraftBuyerActor { + account_id: "acct_demo".to_owned(), + pubkey: "a".repeat(64), + source: ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT.to_owned(), + }, listing_lookup: Some("fresh-eggs".to_owned()), - buyer_account_id: Some("acct_demo".to_owned()), }; let rendered = toml::to_string_pretty(&document).expect("render draft"); @@ -10661,8 +10757,12 @@ mod tests { 2, )), }, + buyer_actor: OrderDraftBuyerActor { + account_id: "acct_demo".to_owned(), + pubkey: "a".repeat(64), + source: ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT.to_owned(), + }, listing_lookup: Some("fresh-eggs".to_owned()), - buyer_account_id: Some("acct_demo".to_owned()), }; let inspection = inspect_document(&document); @@ -15832,8 +15932,12 @@ mod tests { 2, )), }, + buyer_actor: OrderDraftBuyerActor { + account_id: "acct_test".to_owned(), + pubkey: fixture.buyer_pubkey.clone(), + source: ORDER_BUYER_ACTOR_SOURCE_RESOLVED_ACCOUNT.to_owned(), + }, listing_lookup: Some("test-listing".to_owned()), - buyer_account_id: Some("acct_test".to_owned()), }, } } diff --git a/tests/target_cli.rs b/tests/target_cli.rs @@ -3906,10 +3906,16 @@ fn buyer_target_flow_acceptance_uses_target_operations() { quote_economics.clone() ); let order_draft = fs::read_to_string(order_file).expect("read order draft"); + assert!(order_draft.contains("[buyer_actor]")); + assert!(order_draft.contains("source = \"resolved_account\"")); assert!(order_draft.contains("[order.economics]")); assert!(order_draft.contains("pricing_basis = \"listing_event\"")); assert_eq!(quote["result"]["order"]["buyer_account_id"], account_id); assert_eq!( + quote["result"]["order"]["buyer_actor_source"], + "resolved_account" + ); + assert_eq!( quote["result"]["order"]["listing_event_id"], listing_event_id ); @@ -3929,6 +3935,10 @@ fn buyer_target_flow_acceptance_uses_target_operations() { account_id ); assert_eq!( + orders["result"]["orders"][0]["buyer_actor_source"], + "resolved_account" + ); + assert_eq!( orders["result"]["orders"][0]["economics"], quote_economics.clone() );