lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README | LICENSE

commit c52205541e3dff66a1f5f0c9fb4821ea6d0bbc72
parent 4947fcc9f28627ae85711429b3bb1a6ee114127d
Author: triesap <tyson@radroots.org>
Date:   Sat, 13 Jun 2026 04:50:53 -0700

trade: type order domain identifiers

- type trade order records, projections, issues, and inventory accounting identifiers
- update reducer helper boundaries to require validated typed protocol ids
- align deterministic order tests with typed pubkeys, event ids, and order ids
- validate trade, SDK, replica, formatting, diff, and invariant lanes

Diffstat:
Mcrates/trade/src/order.rs | 1171+++++++++++++++++++++++++++++++++++++++++++------------------------------------
1 file changed, 641 insertions(+), 530 deletions(-)

diff --git a/crates/trade/src/order.rs b/crates/trade/src/order.rs @@ -7,7 +7,10 @@ use alloc::{ }; use radroots_core::{RadrootsCoreCurrency, RadrootsCoreDecimal}; -use radroots_events::ids::RadrootsListingAddress; +use radroots_events::ids::{ + RadrootsEconomicsDigest, RadrootsEventId, RadrootsInventoryBinId, RadrootsListingAddress, + RadrootsOrderId, RadrootsOrderQuoteId, RadrootsPublicKey, +}; use radroots_events::kinds::KIND_LISTING; use radroots_events::order::{ RadrootsOrderCancellation, RadrootsOrderDecision, RadrootsOrderDecisionOutcome, @@ -46,88 +49,88 @@ pub enum RadrootsOrderCanonicalizationError { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderRequestRecord { - pub event_id: String, - pub author_pubkey: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, pub payload: RadrootsOrderRequest, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderDecisionRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderDecision, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderRevisionProposalRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderRevisionProposal, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderRevisionDecisionRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderRevisionDecision, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderFulfillmentRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderFulfillmentUpdate, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderCancellationRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderCancellation, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderReceiptRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderReceipt, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderPaymentEventRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderPaymentPayload, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderSettlementRecord { - pub event_id: String, - pub author_pubkey: String, - pub counterparty_pubkey: String, - pub root_event_id: String, - pub prev_event_id: String, + pub event_id: RadrootsEventId, + pub author_pubkey: RadrootsPublicKey, + pub counterparty_pubkey: RadrootsPublicKey, + pub root_event_id: RadrootsEventId, + pub prev_event_id: RadrootsEventId, pub payload: RadrootsOrderSettlementDecision, } @@ -164,138 +167,138 @@ pub enum RadrootsOrderSettlementState { #[derive(Clone, Debug, PartialEq, Eq)] pub enum RadrootsOrderIssue { MissingRequest, - MultipleRequests { event_ids: Vec<String> }, - RequestPayloadInvalid { event_id: String }, - RequestOrderIdMismatch { event_id: String }, - RequestAuthorMismatch { event_id: String }, - RequestListingAddressInvalid { event_id: String }, - RequestSellerListingMismatch { event_id: String }, - DecisionPayloadInvalid { event_id: String }, - DecisionOrderIdMismatch { event_id: String }, - DecisionAuthorMismatch { event_id: String }, - DecisionCounterpartyMismatch { event_id: String }, - DecisionBuyerMismatch { event_id: String }, - DecisionSellerMismatch { event_id: String }, - DecisionListingAddressInvalid { event_id: String }, - DecisionListingMismatch { event_id: String }, - DecisionRootMismatch { event_id: String }, - DecisionPreviousMismatch { event_id: String }, - DecisionMissingInventoryCommitments { event_id: String }, - DecisionInventoryCommitmentMismatch { event_id: String }, - DecisionMissingReason { event_id: String }, - ConflictingDecisions { event_ids: Vec<String> }, - RevisionProposalWithoutAcceptedDecision { event_id: String }, - RevisionProposalPayloadInvalid { event_id: String }, - RevisionProposalOrderIdMismatch { event_id: String }, - RevisionProposalAuthorMismatch { event_id: String }, - RevisionProposalCounterpartyMismatch { event_id: String }, - RevisionProposalBuyerMismatch { event_id: String }, - RevisionProposalSellerMismatch { event_id: String }, - RevisionProposalListingAddressInvalid { event_id: String }, - RevisionProposalListingMismatch { event_id: String }, - RevisionProposalRootMismatch { event_id: String }, - RevisionProposalPreviousMismatch { event_id: String }, - RevisionDecisionWithoutProposal { event_id: String }, - RevisionDecisionPayloadInvalid { event_id: String }, - RevisionDecisionOrderIdMismatch { event_id: String }, - RevisionDecisionAuthorMismatch { event_id: String }, - RevisionDecisionCounterpartyMismatch { event_id: String }, - RevisionDecisionBuyerMismatch { event_id: String }, - RevisionDecisionSellerMismatch { event_id: String }, - RevisionDecisionListingAddressInvalid { event_id: String }, - RevisionDecisionListingMismatch { event_id: String }, - RevisionDecisionRootMismatch { event_id: String }, - RevisionDecisionPreviousMismatch { event_id: String }, - RevisionDecisionRevisionIdMismatch { event_id: String }, - FulfillmentWithoutAcceptedDecision { event_id: String }, - FulfillmentPayloadInvalid { event_id: String }, - FulfillmentOrderIdMismatch { event_id: String }, - FulfillmentAuthorMismatch { event_id: String }, - FulfillmentCounterpartyMismatch { event_id: String }, - FulfillmentBuyerMismatch { event_id: String }, - FulfillmentSellerMismatch { event_id: String }, - FulfillmentListingAddressInvalid { event_id: String }, - FulfillmentListingMismatch { event_id: String }, - FulfillmentRootMismatch { event_id: String }, - FulfillmentPreviousMismatch { event_id: String }, - FulfillmentStatusNotPublishable { event_id: String }, - FulfillmentUnsupportedTransition { event_id: String }, - ForkedFulfillments { event_ids: Vec<String> }, - CancellationWithoutCancellableOrder { event_id: String }, - CancellationPayloadInvalid { event_id: String }, - CancellationOrderIdMismatch { event_id: String }, - CancellationAuthorMismatch { event_id: String }, - CancellationCounterpartyMismatch { event_id: String }, - CancellationBuyerMismatch { event_id: String }, - CancellationSellerMismatch { event_id: String }, - CancellationListingAddressInvalid { event_id: String }, - CancellationListingMismatch { event_id: String }, - CancellationRootMismatch { event_id: String }, - CancellationPreviousMismatch { event_id: String }, - CancellationAfterFulfillment { event_id: String }, - ReceiptWithoutEligibleFulfillment { event_id: String }, - ReceiptPayloadInvalid { event_id: String }, - ReceiptOrderIdMismatch { event_id: String }, - ReceiptAuthorMismatch { event_id: String }, - ReceiptCounterpartyMismatch { event_id: String }, - ReceiptBuyerMismatch { event_id: String }, - ReceiptSellerMismatch { event_id: String }, - ReceiptListingAddressInvalid { event_id: String }, - ReceiptListingMismatch { event_id: String }, - ReceiptRootMismatch { event_id: String }, - ReceiptPreviousMismatch { event_id: String }, - PaymentWithoutAcceptedAgreement { event_id: String }, - PaymentPayloadInvalid { event_id: String }, - PaymentOrderIdMismatch { event_id: String }, - PaymentAuthorMismatch { event_id: String }, - PaymentCounterpartyMismatch { event_id: String }, - PaymentBuyerMismatch { event_id: String }, - PaymentSellerMismatch { event_id: String }, - PaymentListingAddressInvalid { event_id: String }, - PaymentListingMismatch { event_id: String }, - PaymentRootMismatch { event_id: String }, - PaymentPreviousMismatch { event_id: String }, - PaymentAgreementMismatch { event_id: String }, - PaymentQuoteMismatch { event_id: String }, - PaymentQuoteVersionMismatch { event_id: String }, - PaymentEconomicsDigestMismatch { event_id: String }, - PaymentAmountMismatch { event_id: String }, - PaymentCurrencyMismatch { event_id: String }, - PaymentAfterCancellation { event_id: String }, - RevisionAfterPayment { event_id: String }, - DuplicatePayments { event_ids: Vec<String> }, - SettlementWithoutValidPayment { event_id: String }, - SettlementPayloadInvalid { event_id: String }, - SettlementOrderIdMismatch { event_id: String }, - SettlementAuthorMismatch { event_id: String }, - SettlementCounterpartyMismatch { event_id: String }, - SettlementBuyerMismatch { event_id: String }, - SettlementSellerMismatch { event_id: String }, - SettlementListingAddressInvalid { event_id: String }, - SettlementListingMismatch { event_id: String }, - SettlementRootMismatch { event_id: String }, - SettlementPreviousMismatch { event_id: String }, - SettlementPaymentEventMismatch { event_id: String }, - SettlementAgreementMismatch { event_id: String }, - SettlementQuoteMismatch { event_id: String }, - SettlementQuoteVersionMismatch { event_id: String }, - SettlementEconomicsDigestMismatch { event_id: String }, - SettlementAmountMismatch { event_id: String }, - SettlementCurrencyMismatch { event_id: String }, - DuplicateSettlements { event_ids: Vec<String> }, - ForkedLifecycle { event_ids: Vec<String> }, + MultipleRequests { event_ids: Vec<RadrootsEventId> }, + RequestPayloadInvalid { event_id: RadrootsEventId }, + RequestOrderIdMismatch { event_id: RadrootsEventId }, + RequestAuthorMismatch { event_id: RadrootsEventId }, + RequestListingAddressInvalid { event_id: RadrootsEventId }, + RequestSellerListingMismatch { event_id: RadrootsEventId }, + DecisionPayloadInvalid { event_id: RadrootsEventId }, + DecisionOrderIdMismatch { event_id: RadrootsEventId }, + DecisionAuthorMismatch { event_id: RadrootsEventId }, + DecisionCounterpartyMismatch { event_id: RadrootsEventId }, + DecisionBuyerMismatch { event_id: RadrootsEventId }, + DecisionSellerMismatch { event_id: RadrootsEventId }, + DecisionListingAddressInvalid { event_id: RadrootsEventId }, + DecisionListingMismatch { event_id: RadrootsEventId }, + DecisionRootMismatch { event_id: RadrootsEventId }, + DecisionPreviousMismatch { event_id: RadrootsEventId }, + DecisionMissingInventoryCommitments { event_id: RadrootsEventId }, + DecisionInventoryCommitmentMismatch { event_id: RadrootsEventId }, + DecisionMissingReason { event_id: RadrootsEventId }, + ConflictingDecisions { event_ids: Vec<RadrootsEventId> }, + RevisionProposalWithoutAcceptedDecision { event_id: RadrootsEventId }, + RevisionProposalPayloadInvalid { event_id: RadrootsEventId }, + RevisionProposalOrderIdMismatch { event_id: RadrootsEventId }, + RevisionProposalAuthorMismatch { event_id: RadrootsEventId }, + RevisionProposalCounterpartyMismatch { event_id: RadrootsEventId }, + RevisionProposalBuyerMismatch { event_id: RadrootsEventId }, + RevisionProposalSellerMismatch { event_id: RadrootsEventId }, + RevisionProposalListingAddressInvalid { event_id: RadrootsEventId }, + RevisionProposalListingMismatch { event_id: RadrootsEventId }, + RevisionProposalRootMismatch { event_id: RadrootsEventId }, + RevisionProposalPreviousMismatch { event_id: RadrootsEventId }, + RevisionDecisionWithoutProposal { event_id: RadrootsEventId }, + RevisionDecisionPayloadInvalid { event_id: RadrootsEventId }, + RevisionDecisionOrderIdMismatch { event_id: RadrootsEventId }, + RevisionDecisionAuthorMismatch { event_id: RadrootsEventId }, + RevisionDecisionCounterpartyMismatch { event_id: RadrootsEventId }, + RevisionDecisionBuyerMismatch { event_id: RadrootsEventId }, + RevisionDecisionSellerMismatch { event_id: RadrootsEventId }, + RevisionDecisionListingAddressInvalid { event_id: RadrootsEventId }, + RevisionDecisionListingMismatch { event_id: RadrootsEventId }, + RevisionDecisionRootMismatch { event_id: RadrootsEventId }, + RevisionDecisionPreviousMismatch { event_id: RadrootsEventId }, + RevisionDecisionRevisionIdMismatch { event_id: RadrootsEventId }, + FulfillmentWithoutAcceptedDecision { event_id: RadrootsEventId }, + FulfillmentPayloadInvalid { event_id: RadrootsEventId }, + FulfillmentOrderIdMismatch { event_id: RadrootsEventId }, + FulfillmentAuthorMismatch { event_id: RadrootsEventId }, + FulfillmentCounterpartyMismatch { event_id: RadrootsEventId }, + FulfillmentBuyerMismatch { event_id: RadrootsEventId }, + FulfillmentSellerMismatch { event_id: RadrootsEventId }, + FulfillmentListingAddressInvalid { event_id: RadrootsEventId }, + FulfillmentListingMismatch { event_id: RadrootsEventId }, + FulfillmentRootMismatch { event_id: RadrootsEventId }, + FulfillmentPreviousMismatch { event_id: RadrootsEventId }, + FulfillmentStatusNotPublishable { event_id: RadrootsEventId }, + FulfillmentUnsupportedTransition { event_id: RadrootsEventId }, + ForkedFulfillments { event_ids: Vec<RadrootsEventId> }, + CancellationWithoutCancellableOrder { event_id: RadrootsEventId }, + CancellationPayloadInvalid { event_id: RadrootsEventId }, + CancellationOrderIdMismatch { event_id: RadrootsEventId }, + CancellationAuthorMismatch { event_id: RadrootsEventId }, + CancellationCounterpartyMismatch { event_id: RadrootsEventId }, + CancellationBuyerMismatch { event_id: RadrootsEventId }, + CancellationSellerMismatch { event_id: RadrootsEventId }, + CancellationListingAddressInvalid { event_id: RadrootsEventId }, + CancellationListingMismatch { event_id: RadrootsEventId }, + CancellationRootMismatch { event_id: RadrootsEventId }, + CancellationPreviousMismatch { event_id: RadrootsEventId }, + CancellationAfterFulfillment { event_id: RadrootsEventId }, + ReceiptWithoutEligibleFulfillment { event_id: RadrootsEventId }, + ReceiptPayloadInvalid { event_id: RadrootsEventId }, + ReceiptOrderIdMismatch { event_id: RadrootsEventId }, + ReceiptAuthorMismatch { event_id: RadrootsEventId }, + ReceiptCounterpartyMismatch { event_id: RadrootsEventId }, + ReceiptBuyerMismatch { event_id: RadrootsEventId }, + ReceiptSellerMismatch { event_id: RadrootsEventId }, + ReceiptListingAddressInvalid { event_id: RadrootsEventId }, + ReceiptListingMismatch { event_id: RadrootsEventId }, + ReceiptRootMismatch { event_id: RadrootsEventId }, + ReceiptPreviousMismatch { event_id: RadrootsEventId }, + PaymentWithoutAcceptedAgreement { event_id: RadrootsEventId }, + PaymentPayloadInvalid { event_id: RadrootsEventId }, + PaymentOrderIdMismatch { event_id: RadrootsEventId }, + PaymentAuthorMismatch { event_id: RadrootsEventId }, + PaymentCounterpartyMismatch { event_id: RadrootsEventId }, + PaymentBuyerMismatch { event_id: RadrootsEventId }, + PaymentSellerMismatch { event_id: RadrootsEventId }, + PaymentListingAddressInvalid { event_id: RadrootsEventId }, + PaymentListingMismatch { event_id: RadrootsEventId }, + PaymentRootMismatch { event_id: RadrootsEventId }, + PaymentPreviousMismatch { event_id: RadrootsEventId }, + PaymentAgreementMismatch { event_id: RadrootsEventId }, + PaymentQuoteMismatch { event_id: RadrootsEventId }, + PaymentQuoteVersionMismatch { event_id: RadrootsEventId }, + PaymentEconomicsDigestMismatch { event_id: RadrootsEventId }, + PaymentAmountMismatch { event_id: RadrootsEventId }, + PaymentCurrencyMismatch { event_id: RadrootsEventId }, + PaymentAfterCancellation { event_id: RadrootsEventId }, + RevisionAfterPayment { event_id: RadrootsEventId }, + DuplicatePayments { event_ids: Vec<RadrootsEventId> }, + SettlementWithoutValidPayment { event_id: RadrootsEventId }, + SettlementPayloadInvalid { event_id: RadrootsEventId }, + SettlementOrderIdMismatch { event_id: RadrootsEventId }, + SettlementAuthorMismatch { event_id: RadrootsEventId }, + SettlementCounterpartyMismatch { event_id: RadrootsEventId }, + SettlementBuyerMismatch { event_id: RadrootsEventId }, + SettlementSellerMismatch { event_id: RadrootsEventId }, + SettlementListingAddressInvalid { event_id: RadrootsEventId }, + SettlementListingMismatch { event_id: RadrootsEventId }, + SettlementRootMismatch { event_id: RadrootsEventId }, + SettlementPreviousMismatch { event_id: RadrootsEventId }, + SettlementPaymentEventMismatch { event_id: RadrootsEventId }, + SettlementAgreementMismatch { event_id: RadrootsEventId }, + SettlementQuoteMismatch { event_id: RadrootsEventId }, + SettlementQuoteVersionMismatch { event_id: RadrootsEventId }, + SettlementEconomicsDigestMismatch { event_id: RadrootsEventId }, + SettlementAmountMismatch { event_id: RadrootsEventId }, + SettlementCurrencyMismatch { event_id: RadrootsEventId }, + DuplicateSettlements { event_ids: Vec<RadrootsEventId> }, + ForkedLifecycle { event_ids: Vec<RadrootsEventId> }, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderPaymentProjection { pub state: RadrootsOrderPaymentState, pub settlement_state: RadrootsOrderSettlementState, - pub payment_event_id: Option<String>, - pub settlement_event_id: Option<String>, - pub agreement_event_id: Option<String>, - pub quote_id: Option<String>, + pub payment_event_id: Option<RadrootsEventId>, + pub settlement_event_id: Option<RadrootsEventId>, + pub agreement_event_id: Option<RadrootsEventId>, + pub quote_id: Option<RadrootsOrderQuoteId>, pub quote_version: Option<u32>, - pub economics_digest: Option<String>, + pub economics_digest: Option<RadrootsEconomicsDigest>, pub amount: Option<RadrootsCoreDecimal>, pub currency: Option<RadrootsCoreCurrency>, pub method: Option<RadrootsOrderPaymentMethod>, @@ -334,25 +337,25 @@ impl RadrootsOrderPaymentProjection { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsOrderProjection { - pub order_id: String, + pub order_id: RadrootsOrderId, pub status: RadrootsOrderStatus, - pub request_event_id: Option<String>, - pub decision_event_id: Option<String>, - pub fulfillment_event_id: Option<String>, + pub request_event_id: Option<RadrootsEventId>, + pub decision_event_id: Option<RadrootsEventId>, + pub fulfillment_event_id: Option<RadrootsEventId>, pub fulfillment_status: Option<RadrootsOrderFulfillmentState>, - pub cancellation_event_id: Option<String>, - pub receipt_event_id: Option<String>, + pub cancellation_event_id: Option<RadrootsEventId>, + pub receipt_event_id: Option<RadrootsEventId>, pub receipt_received: Option<bool>, pub receipt_issue: Option<String>, pub receipt_received_at: Option<u64>, pub lifecycle_terminal: bool, pub payment: RadrootsOrderPaymentProjection, pub economics: Option<RadrootsOrderEconomics>, - pub agreement_event_id: Option<String>, - pub listing_addr: Option<String>, - pub buyer_pubkey: Option<String>, - pub seller_pubkey: Option<String>, - pub last_event_id: Option<String>, + pub agreement_event_id: Option<RadrootsEventId>, + pub listing_addr: Option<RadrootsListingAddress>, + pub buyer_pubkey: Option<RadrootsPublicKey>, + pub seller_pubkey: Option<RadrootsPublicKey>, + pub last_event_id: Option<RadrootsEventId>, pub issues: Vec<RadrootsOrderIssue>, } @@ -365,20 +368,20 @@ pub enum RadrootsOrderEconomicsDigestError { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsListingInventoryBinAvailability { - pub bin_id: String, + pub bin_id: RadrootsInventoryBinId, pub available_count: u64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsListingInventoryOrderReservation { - pub order_id: String, - pub decision_event_id: String, + pub order_id: RadrootsOrderId, + pub decision_event_id: RadrootsEventId, pub bin_count: u64, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsListingInventoryBinAccounting { - pub bin_id: String, + pub bin_id: RadrootsInventoryBinId, pub available_count: u64, pub accepted_reserved_count: u64, pub remaining_count: u64, @@ -389,39 +392,39 @@ pub struct RadrootsListingInventoryBinAccounting { #[derive(Clone, Debug, PartialEq, Eq)] pub enum RadrootsListingInventoryAccountingIssue { InvalidOrder { - order_id: String, - event_ids: Vec<String>, + order_id: RadrootsOrderId, + event_ids: Vec<RadrootsEventId>, }, ArithmeticOverflow { - bin_id: String, - event_ids: Vec<String>, + bin_id: RadrootsInventoryBinId, + event_ids: Vec<RadrootsEventId>, }, UnknownInventoryBin { - bin_id: String, - event_ids: Vec<String>, + bin_id: RadrootsInventoryBinId, + event_ids: Vec<RadrootsEventId>, }, OverReserved { - bin_id: String, + bin_id: RadrootsInventoryBinId, available_count: u64, reserved_count: u64, - event_ids: Vec<String>, + event_ids: Vec<RadrootsEventId>, }, } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RadrootsListingInventoryAccountingProjection { - pub listing_addr: String, - pub listing_event_id: String, + pub listing_addr: RadrootsListingAddress, + pub listing_event_id: RadrootsEventId, pub bins: Vec<RadrootsListingInventoryBinAccounting>, - pub declined_order_ids: Vec<String>, - pub cancelled_order_ids: Vec<String>, - pub invalid_event_ids: Vec<String>, + pub declined_order_ids: Vec<RadrootsOrderId>, + pub cancelled_order_ids: Vec<RadrootsOrderId>, + pub invalid_event_ids: Vec<RadrootsEventId>, pub issues: Vec<RadrootsListingInventoryAccountingIssue>, } #[cfg_attr(coverage_nightly, coverage(off))] pub fn reduce_order_events<I, J, K, L, M, N, O, P, Q>( - order_id: &str, + order_id: &RadrootsOrderId, requests: I, decisions: J, revision_proposals: K, @@ -458,7 +461,7 @@ where } fn reduce_order_event_records( - order_id: &str, + order_id: &RadrootsOrderId, requests: Vec<RadrootsOrderRequestRecord>, decisions: Vec<RadrootsOrderDecisionRecord>, revision_proposals: Vec<RadrootsOrderRevisionProposalRecord>, @@ -489,7 +492,7 @@ fn reduce_order_event_records( && settlements.is_empty() { return RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status: RadrootsOrderStatus::Missing, request_event_id: None, decision_event_id: None, @@ -604,7 +607,7 @@ fn reduce_order_event_records( .iter() .map(|cancellation| cancellation.event_id.clone()), ); - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); return invalid_projection( order_id, Some(request), @@ -669,8 +672,8 @@ fn reduce_order_event_records( #[cfg_attr(coverage_nightly, coverage(off))] pub fn reduce_listing_inventory_accounting<I, J, K, L, M, N, O, P>( - listing_addr: &str, - listing_event_id: &str, + listing_addr: &RadrootsListingAddress, + listing_event_id: &RadrootsEventId, bins: I, requests: J, decisions: K, @@ -705,8 +708,8 @@ where } fn reduce_listing_inventory_accounting_records( - listing_addr: &str, - listing_event_id: &str, + listing_addr: &RadrootsListingAddress, + listing_event_id: &RadrootsEventId, bins: Vec<RadrootsListingInventoryBinAvailability>, requests: Vec<RadrootsOrderRequestRecord>, decisions: Vec<RadrootsOrderDecisionRecord>, @@ -719,31 +722,31 @@ fn reduce_listing_inventory_accounting_records( let (mut bins, mut issues) = normalized_listing_inventory_bins(bins); let requests = unique_request_records(requests) .into_iter() - .filter(|request| request.payload.listing_addr.trim() == listing_addr) + .filter(|request| request.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let decisions = unique_decision_records(decisions) .into_iter() - .filter(|decision| decision.payload.listing_addr.trim() == listing_addr) + .filter(|decision| decision.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let revision_proposals = unique_revision_proposal_records(revision_proposals) .into_iter() - .filter(|proposal| proposal.payload.listing_addr.trim() == listing_addr) + .filter(|proposal| proposal.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let revision_decisions = unique_revision_decision_records(revision_decisions) .into_iter() - .filter(|decision| decision.payload.listing_addr.trim() == listing_addr) + .filter(|decision| decision.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let fulfillments = unique_fulfillment_records(fulfillments) .into_iter() - .filter(|fulfillment| fulfillment.payload.listing_addr.trim() == listing_addr) + .filter(|fulfillment| fulfillment.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let cancellations = unique_cancellation_records(cancellations) .into_iter() - .filter(|cancellation| cancellation.payload.listing_addr.trim() == listing_addr) + .filter(|cancellation| cancellation.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let receipts = unique_receipt_records(receipts) .into_iter() - .filter(|receipt| receipt.payload.listing_addr.trim() == listing_addr) + .filter(|receipt| receipt.payload.listing_addr.as_str() == listing_addr.as_str()) .collect::<Vec<_>>(); let mut order_ids = listing_order_ids( &requests, @@ -815,7 +818,7 @@ fn reduce_listing_inventory_accounting_records( { continue; } - if let Some(agreement_event_id) = projection.agreement_event_id.as_deref() + if let Some(agreement_event_id) = projection.agreement_event_id.as_ref() && let Some(economics) = projection.economics.as_ref() { add_accepted_inventory_reservations_from_economics( @@ -867,7 +870,7 @@ fn reduce_listing_inventory_accounting_records( .iter() .map(|receipt| receipt.event_id.clone()), ); - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); } invalid_event_ids.extend(event_ids.iter().cloned()); issues.push(RadrootsListingInventoryAccountingIssue::InvalidOrder { @@ -879,14 +882,14 @@ fn reduce_listing_inventory_accounting_records( } } - sort_and_dedup_strings(&mut declined_order_ids); - sort_and_dedup_strings(&mut cancelled_order_ids); - sort_and_dedup_strings(&mut invalid_event_ids); + sort_and_dedup_values(&mut declined_order_ids); + sort_and_dedup_values(&mut cancelled_order_ids); + sort_and_dedup_values(&mut invalid_event_ids); finish_inventory_accounting_bins(&mut bins, &mut issues); issues.sort_by(inventory_issue_sort_key); RadrootsListingInventoryAccountingProjection { - listing_addr: listing_addr.to_string(), - listing_event_id: listing_event_id.to_string(), + listing_addr: listing_addr.clone(), + listing_event_id: listing_event_id.clone(), bins, declined_order_ids, cancelled_order_ids, @@ -1139,10 +1142,7 @@ where let mut normalized: Vec<RadrootsListingInventoryBinAccounting> = Vec::new(); let mut issues = Vec::new(); for bin in bins { - let bin_id = bin.bin_id.trim(); - if bin_id.is_empty() { - continue; - } + let bin_id = bin.bin_id; if let Some(existing) = normalized .iter_mut() .find(|existing| existing.bin_id == bin_id) @@ -1162,7 +1162,7 @@ where } } else { normalized.push(RadrootsListingInventoryBinAccounting { - bin_id: bin_id.to_string(), + bin_id, available_count: bin.available_count, accepted_reserved_count: 0, remaining_count: bin.available_count, @@ -1183,51 +1183,51 @@ fn listing_order_ids( fulfillments: &[RadrootsOrderFulfillmentRecord], cancellations: &[RadrootsOrderCancellationRecord], receipts: &[RadrootsOrderReceiptRecord], -) -> Vec<String> { +) -> Vec<RadrootsOrderId> { let mut order_ids = Vec::new(); order_ids.extend( requests .iter() - .map(|request| request.payload.order_id.to_string()), + .map(|request| request.payload.order_id.clone()), ); order_ids.extend( decisions .iter() - .map(|decision| decision.payload.order_id.to_string()), + .map(|decision| decision.payload.order_id.clone()), ); order_ids.extend( revision_proposals .iter() - .map(|proposal| proposal.payload.order_id.to_string()), + .map(|proposal| proposal.payload.order_id.clone()), ); order_ids.extend( revision_decisions .iter() - .map(|decision| decision.payload.order_id.to_string()), + .map(|decision| decision.payload.order_id.clone()), ); order_ids.extend( fulfillments .iter() - .map(|fulfillment| fulfillment.payload.order_id.to_string()), + .map(|fulfillment| fulfillment.payload.order_id.clone()), ); order_ids.extend( cancellations .iter() - .map(|cancellation| cancellation.payload.order_id.to_string()), + .map(|cancellation| cancellation.payload.order_id.clone()), ); order_ids.extend( receipts .iter() - .map(|receipt| receipt.payload.order_id.to_string()), + .map(|receipt| receipt.payload.order_id.clone()), ); - sort_and_dedup_strings(&mut order_ids); + sort_and_dedup_values(&mut order_ids); order_ids } fn add_accepted_inventory_reservations_from_economics( bins: &mut [RadrootsListingInventoryBinAccounting], - order_id: &str, - agreement_event_id: &str, + order_id: &RadrootsOrderId, + agreement_event_id: &RadrootsEventId, economics: &RadrootsOrderEconomics, issues: &mut Vec<RadrootsListingInventoryAccountingIssue>, ) { @@ -1243,8 +1243,8 @@ fn add_accepted_inventory_reservations_from_economics( } else { issues.push( RadrootsListingInventoryAccountingIssue::UnknownInventoryBin { - bin_id: item.bin_id.to_string(), - event_ids: vec![agreement_event_id.to_string()], + bin_id: item.bin_id.clone(), + event_ids: vec![agreement_event_id.clone()], }, ); } @@ -1254,7 +1254,7 @@ fn add_accepted_inventory_reservations_from_economics( #[cfg(test)] fn add_inventory_reservation( bin: &mut RadrootsListingInventoryBinAccounting, - order_id: &str, + order_id: &RadrootsOrderId, decision: &RadrootsOrderDecisionRecord, bin_count: u64, issues: &mut Vec<RadrootsListingInventoryAccountingIssue>, @@ -1264,8 +1264,8 @@ fn add_inventory_reservation( fn add_inventory_reservation_event( bin: &mut RadrootsListingInventoryBinAccounting, - order_id: &str, - event_id: &str, + order_id: &RadrootsOrderId, + event_id: &RadrootsEventId, bin_count: u64, issues: &mut Vec<RadrootsListingInventoryAccountingIssue>, ) { @@ -1273,15 +1273,15 @@ fn add_inventory_reservation_event( bin.accepted_reserved_count = next_count; bin.accepted_orders .push(RadrootsListingInventoryOrderReservation { - order_id: order_id.to_string(), - decision_event_id: event_id.to_string(), + order_id: order_id.clone(), + decision_event_id: event_id.clone(), bin_count, }); } else { issues.push( RadrootsListingInventoryAccountingIssue::ArithmeticOverflow { bin_id: bin.bin_id.clone(), - event_ids: vec![event_id.to_string()], + event_ids: vec![event_id.clone()], }, ); } @@ -1307,7 +1307,7 @@ fn finish_inventory_accounting_bins( .iter() .map(|reservation| reservation.decision_event_id.clone()) .collect::<Vec<_>>(); - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); issues.push(RadrootsListingInventoryAccountingIssue::OverReserved { bin_id: bin.bin_id.clone(), available_count: bin.available_count, @@ -1319,7 +1319,7 @@ fn finish_inventory_accounting_bins( bins.sort_by(|left, right| left.bin_id.cmp(&right.bin_id)); } -fn projection_issue_event_ids(issues: &[RadrootsOrderIssue]) -> Vec<String> { +fn projection_issue_event_ids(issues: &[RadrootsOrderIssue]) -> Vec<RadrootsEventId> { let mut event_ids = Vec::new(); for issue in issues { match issue { @@ -1452,11 +1452,11 @@ fn projection_issue_event_ids(issues: &[RadrootsOrderIssue]) -> Vec<String> { } } } - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); event_ids } -fn sort_and_dedup_strings(values: &mut Vec<String>) { +fn sort_and_dedup_values<T: Ord>(values: &mut Vec<T>) { values.sort(); values.dedup(); } @@ -1489,7 +1489,9 @@ fn inventory_issue_id(issue: &RadrootsListingInventoryAccountingIssue) -> &str { } } -fn inventory_issue_event_ids(issue: &RadrootsListingInventoryAccountingIssue) -> &[String] { +fn inventory_issue_event_ids( + issue: &RadrootsListingInventoryAccountingIssue, +) -> &[RadrootsEventId] { match issue { RadrootsListingInventoryAccountingIssue::InvalidOrder { event_ids, .. } | RadrootsListingInventoryAccountingIssue::ArithmeticOverflow { event_ids, .. } @@ -1499,7 +1501,7 @@ fn inventory_issue_event_ids(issue: &RadrootsListingInventoryAccountingIssue) -> } fn validate_order_request_record( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, issues: &mut Vec<RadrootsOrderIssue>, ) -> bool { @@ -1510,7 +1512,7 @@ fn validate_order_request_record( }); valid = false; } - if request.payload.order_id != order_id { + if request.payload.order_id.as_str() != order_id.as_str() { issues.push(RadrootsOrderIssue::RequestOrderIdMismatch { event_id: request.event_id.clone(), }); @@ -2027,7 +2029,7 @@ fn validate_order_receipt_record( fn reduce_order_payment_settlement_records( request: &RadrootsOrderRequestRecord, - agreement_event_id: &str, + agreement_event_id: &RadrootsEventId, economics: &RadrootsOrderEconomics, payments: Vec<RadrootsOrderPaymentEventRecord>, settlements: Vec<RadrootsOrderSettlementRecord>, @@ -2057,7 +2059,7 @@ fn reduce_order_payment_settlement_records( }; } - let mut previous_payment_parent = agreement_event_id.to_string(); + let mut previous_payment_parent = agreement_event_id.clone(); let mut used_payment_event_ids = Vec::new(); let mut used_settlement_event_ids = Vec::new(); let mut rejected_projection = None; @@ -2207,10 +2209,10 @@ fn payment_projection_from_record( settlement_state, payment_event_id: Some(payment.event_id.clone()), settlement_event_id: settlement.map(|settlement| settlement.event_id.clone()), - agreement_event_id: Some(payment.payload.agreement_event_id.to_string()), - quote_id: Some(payment.payload.quote_id.to_string()), + agreement_event_id: Some(payment.payload.agreement_event_id.clone()), + quote_id: Some(payment.payload.quote_id.clone()), quote_version: Some(payment.payload.quote_version), - economics_digest: Some(payment.payload.economics_digest.to_string()), + economics_digest: Some(payment.payload.economics_digest.clone()), amount: Some(payment.payload.amount), currency: Some(payment.payload.currency), method: Some(payment.payload.method), @@ -2302,11 +2304,11 @@ fn validate_order_payment_record( fn validate_order_payment_agreement_record( payment: &RadrootsOrderPaymentEventRecord, - agreement_event_id: &str, + agreement_event_id: &RadrootsEventId, economics: &RadrootsOrderEconomics, issues: &mut Vec<RadrootsOrderIssue>, ) { - if payment.payload.agreement_event_id != agreement_event_id { + if payment.payload.agreement_event_id.as_str() != agreement_event_id.as_str() { issues.push(RadrootsOrderIssue::PaymentAgreementMismatch { event_id: payment.event_id.clone(), }); @@ -2473,7 +2475,7 @@ fn validate_order_settlement_payment_record( fn decision_payload_issue( decision: &RadrootsOrderDecisionOutcome, - event_id: &str, + event_id: &RadrootsEventId, issues: &mut Vec<RadrootsOrderIssue>, ) -> bool { match decision { @@ -2482,7 +2484,7 @@ fn decision_payload_issue( } => { if inventory_commitments.is_empty() { issues.push(RadrootsOrderIssue::DecisionMissingInventoryCommitments { - event_id: event_id.to_string(), + event_id: event_id.clone(), }); true } else { @@ -2492,7 +2494,7 @@ fn decision_payload_issue( RadrootsOrderDecisionOutcome::Declined { reason } => { if reason.trim().is_empty() { issues.push(RadrootsOrderIssue::DecisionMissingReason { - event_id: event_id.to_string(), + event_id: event_id.clone(), }); true } else { @@ -2594,7 +2596,7 @@ fn record_payment_after_cancellation( fn single_lifecycle_child<T>( records: &[T], - event_id: impl Fn(&T) -> &String, + event_id: impl Fn(&T) -> &RadrootsEventId, ) -> Result<Option<T>, RadrootsOrderIssue> where T: Clone, @@ -2604,7 +2606,7 @@ where [record] => Ok(Some(record.clone())), _ => { let mut event_ids = records.iter().map(event_id).cloned().collect::<Vec<_>>(); - event_ids.sort(); + sort_and_dedup_values(&mut event_ids); Err(RadrootsOrderIssue::ForkedLifecycle { event_ids }) } } @@ -2625,10 +2627,10 @@ fn validated_fulfillment_records( } struct RadrootsOrderRevisionState { - agreement_event_id: String, - lifecycle_parent_event_id: String, + agreement_event_id: RadrootsEventId, + lifecycle_parent_event_id: RadrootsEventId, economics: RadrootsOrderEconomics, - pending_revision_event_id: Option<String>, + pending_revision_event_id: Option<RadrootsEventId>, } fn order_revision_state( @@ -2739,7 +2741,7 @@ fn order_revision_state( } fn latest_fulfillment_record( - parent_event_id: &str, + parent_event_id: &RadrootsEventId, valid_fulfillments: &[RadrootsOrderFulfillmentRecord], issues: &mut Vec<RadrootsOrderIssue>, ) -> Option<RadrootsOrderFulfillmentRecord> { @@ -2747,7 +2749,7 @@ fn latest_fulfillment_record( return None; } let mut used_event_ids = Vec::new(); - let mut previous_event_id = parent_event_id.to_string(); + let mut previous_event_id = parent_event_id.clone(); let mut previous_status = RadrootsOrderFulfillmentState::AcceptedNotFulfilled; let mut latest = None; @@ -2800,11 +2802,11 @@ fn latest_fulfillment_record( } fn requested_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, ) -> RadrootsOrderProjection { RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status: RadrootsOrderStatus::Requested, request_event_id: Some(request.event_id.clone()), decision_event_id: None, @@ -2819,16 +2821,16 @@ fn requested_projection( payment: RadrootsOrderPaymentProjection::not_recorded(), economics: Some(request.payload.economics.clone()), agreement_event_id: None, - listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), - seller_pubkey: Some(request.payload.seller_pubkey.to_string()), + listing_addr: Some(request.payload.listing_addr.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), + seller_pubkey: Some(request.payload.seller_pubkey.clone()), last_event_id: Some(request.event_id.clone()), issues: Vec::new(), } } fn requested_cancellation_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, cancellations: Vec<RadrootsOrderCancellationRecord>, ) -> RadrootsOrderProjection { @@ -2863,7 +2865,7 @@ fn requested_cancellation_projection( } fn decided_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, decision: &RadrootsOrderDecisionRecord, revision_proposals: Vec<RadrootsOrderRevisionProposalRecord>, @@ -2923,7 +2925,7 @@ fn decided_projection( .iter() .map(|settlement| settlement.event_id.clone()), ); - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); return invalid_projection( order_id, Some(request), @@ -2978,7 +2980,7 @@ fn decided_projection( .map(|cancellation| cancellation.event_id.clone()) .collect::<Vec<_>>(); event_ids.push(first_fulfillment.event_id.clone()); - sort_and_dedup_strings(&mut event_ids); + sort_and_dedup_values(&mut event_ids); return invalid_projection( order_id, Some(request), @@ -3113,7 +3115,7 @@ fn decided_projection( ), }; RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status, request_event_id: Some(request.event_id.clone()), decision_event_id: Some(decision.event_id.clone()), @@ -3128,19 +3130,19 @@ fn decided_projection( payment, economics, agreement_event_id, - listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), - seller_pubkey: Some(request.payload.seller_pubkey.to_string()), + listing_addr: Some(request.payload.listing_addr.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), + seller_pubkey: Some(request.payload.seller_pubkey.clone()), last_event_id, issues: Vec::new(), } } fn receipt_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, decision: &RadrootsOrderDecisionRecord, - agreement_event_id: &str, + agreement_event_id: &RadrootsEventId, economics: &RadrootsOrderEconomics, latest_fulfillment: Option<&RadrootsOrderFulfillmentRecord>, fulfillments: &[RadrootsOrderFulfillmentRecord], @@ -3187,7 +3189,7 @@ fn receipt_projection( } } if !fork_event_ids.is_empty() { - sort_and_dedup_strings(&mut fork_event_ids); + sort_and_dedup_values(&mut fork_event_ids); issues.push(RadrootsOrderIssue::ForkedLifecycle { event_ids: fork_event_ids, }); @@ -3224,15 +3226,15 @@ fn receipt_projection( } fn cancelled_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, - decision_event_id: Option<String>, - agreement_event_id: Option<String>, + decision_event_id: Option<RadrootsEventId>, + agreement_event_id: Option<RadrootsEventId>, economics: RadrootsOrderEconomics, cancellation: RadrootsOrderCancellationRecord, ) -> RadrootsOrderProjection { RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status: RadrootsOrderStatus::Cancelled, request_event_id: Some(request.event_id.clone()), decision_event_id, @@ -3247,19 +3249,19 @@ fn cancelled_projection( payment: RadrootsOrderPaymentProjection::not_recorded(), economics: Some(economics), agreement_event_id, - listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), - seller_pubkey: Some(request.payload.seller_pubkey.to_string()), + listing_addr: Some(request.payload.listing_addr.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), + seller_pubkey: Some(request.payload.seller_pubkey.clone()), last_event_id: Some(cancellation.event_id), issues: Vec::new(), } } fn receipt_terminal_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: &RadrootsOrderRequestRecord, decision: &RadrootsOrderDecisionRecord, - agreement_event_id: &str, + agreement_event_id: &RadrootsEventId, economics: &RadrootsOrderEconomics, fulfillment: &RadrootsOrderFulfillmentRecord, receipt: RadrootsOrderReceiptRecord, @@ -3270,7 +3272,7 @@ fn receipt_terminal_projection( RadrootsOrderStatus::Disputed }; RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status, request_event_id: Some(request.event_id.clone()), decision_event_id: Some(decision.event_id.clone()), @@ -3284,17 +3286,17 @@ fn receipt_terminal_projection( lifecycle_terminal: true, payment: RadrootsOrderPaymentProjection::not_recorded(), economics: Some(economics.clone()), - agreement_event_id: Some(agreement_event_id.to_string()), - listing_addr: Some(request.payload.listing_addr.to_string()), - buyer_pubkey: Some(request.payload.buyer_pubkey.to_string()), - seller_pubkey: Some(request.payload.seller_pubkey.to_string()), + agreement_event_id: Some(agreement_event_id.clone()), + listing_addr: Some(request.payload.listing_addr.clone()), + buyer_pubkey: Some(request.payload.buyer_pubkey.clone()), + seller_pubkey: Some(request.payload.seller_pubkey.clone()), last_event_id: Some(receipt.event_id), issues: Vec::new(), } } fn invalid_projection( - order_id: &str, + order_id: &RadrootsOrderId, request: Option<&RadrootsOrderRequestRecord>, issues: Vec<RadrootsOrderIssue>, ) -> RadrootsOrderProjection { @@ -3307,7 +3309,7 @@ fn invalid_projection( } fn invalid_projection_with_payment( - order_id: &str, + order_id: &RadrootsOrderId, request: Option<&RadrootsOrderRequestRecord>, issues: Vec<RadrootsOrderIssue>, payment: RadrootsOrderPaymentProjection, @@ -3319,7 +3321,7 @@ fn invalid_projection_with_payment( _ => None, }; RadrootsOrderProjection { - order_id: order_id.to_string(), + order_id: order_id.clone(), status: RadrootsOrderStatus::Invalid, request_event_id: request.map(|request| request.event_id.clone()), decision_event_id: None, @@ -3334,9 +3336,9 @@ fn invalid_projection_with_payment( payment, economics, agreement_event_id: None, - listing_addr: request.map(|request| request.payload.listing_addr.to_string()), - buyer_pubkey: request.map(|request| request.payload.buyer_pubkey.to_string()), - seller_pubkey: request.map(|request| request.payload.seller_pubkey.to_string()), + listing_addr: request.map(|request| request.payload.listing_addr.clone()), + buyer_pubkey: request.map(|request| request.payload.buyer_pubkey.clone()), + seller_pubkey: request.map(|request| request.payload.seller_pubkey.clone()), last_event_id: request.map(|request| request.event_id.clone()), issues, } @@ -3417,7 +3419,7 @@ fn canonicalize_inventory_commitments( #[derive(Debug, PartialEq, Eq)] struct NormalizedInventoryCount { - bin_id: String, + bin_id: RadrootsInventoryBinId, bin_count: u64, } @@ -3453,18 +3455,20 @@ fn normalized_inventory_commitment_counts( fn push_normalized_inventory_count( counts: &mut Vec<NormalizedInventoryCount>, - bin_id: &str, + bin_id: &RadrootsInventoryBinId, bin_count: u32, ) -> Option<()> { - let bin_id = bin_id.trim(); - if bin_id.is_empty() || bin_count == 0 { + if bin_count == 0 { return None; } - if let Some(existing) = counts.iter_mut().find(|count| count.bin_id == bin_id) { + if let Some(existing) = counts + .iter_mut() + .find(|count| count.bin_id.as_str() == bin_id.as_str()) + { existing.bin_count = existing.bin_count.checked_add(u64::from(bin_count))?; } else { counts.push(NormalizedInventoryCount { - bin_id: bin_id.to_string(), + bin_id: bin_id.clone(), bin_count: u64::from(bin_count), }); } @@ -3484,12 +3488,13 @@ fn normalized_required_string( #[cfg(test)] mod tests { + use core::fmt::Write as _; use radroots_core::{ RadrootsCoreCurrency, RadrootsCoreDecimal, RadrootsCoreMoney, RadrootsCoreUnit, }; use radroots_events::ids::{ - RadrootsEconomicsDigest, RadrootsInventoryBinId, RadrootsListingAddress, RadrootsOrderId, - RadrootsOrderQuoteId, RadrootsOrderRevisionId, + RadrootsEconomicsDigest, RadrootsEventId, RadrootsInventoryBinId, RadrootsListingAddress, + RadrootsOrderId, RadrootsOrderQuoteId, RadrootsOrderRevisionId, RadrootsPublicKey, }; use radroots_events::kinds::KIND_LISTING; use radroots_events::order::{ @@ -3528,6 +3533,35 @@ mod tests { RadrootsOrderId::parse(raw).expect("order id") } + fn pubkey(raw: &str) -> RadrootsPublicKey { + RadrootsPublicKey::parse(raw).expect("public key") + } + + fn pubkey_or(default: &str, raw: &str) -> RadrootsPublicKey { + if raw.is_empty() { + pubkey(default) + } else { + pubkey(raw) + } + } + + fn test_event_id(raw: &str) -> RadrootsEventId { + let mut bytes = [0u8; 32]; + for (index, byte) in raw.bytes().enumerate() { + let primary = index % bytes.len(); + let secondary = (index * 7 + 13) % bytes.len(); + bytes[primary] = bytes[primary] + .wrapping_add(byte) + .wrapping_add((index as u8).wrapping_mul(31)); + bytes[secondary] ^= byte.rotate_left((index % 8) as u32); + } + let mut hex = String::with_capacity(64); + for byte in bytes { + write!(&mut hex, "{byte:02x}").unwrap(); + } + RadrootsEventId::parse(hex).expect("event id") + } + fn order_revision_id(raw: &str) -> RadrootsOrderRevisionId { RadrootsOrderRevisionId::parse(raw).expect("revision id") } @@ -3552,8 +3586,8 @@ mod tests { RadrootsOrderRequest { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: buyer_pubkey.to_string(), - seller_pubkey: seller_pubkey.to_string(), + buyer_pubkey: pubkey_or(BUYER, buyer_pubkey), + seller_pubkey: pubkey_or(SELLER, seller_pubkey), items: vec![RadrootsOrderItem { bin_id: bin_id("bin-1"), bin_count: 2, @@ -3602,8 +3636,8 @@ mod tests { RadrootsOrderDecision { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: format!(" {BUYER} "), - seller_pubkey: seller_pubkey.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey_or(SELLER, seller_pubkey), decision: RadrootsOrderDecisionOutcome::Accepted { inventory_commitments: vec![RadrootsOrderInventoryCommitment { bin_id: bin_id("bin-1"), @@ -3621,8 +3655,8 @@ mod tests { RadrootsOrderRequest { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), items: vec![RadrootsOrderItem { bin_id: bin_id("bin-1"), bin_count: 2, @@ -3633,8 +3667,8 @@ mod tests { fn request_record_with_event_id(event_id: &str) -> RadrootsOrderRequestRecord { RadrootsOrderRequestRecord { - event_id: event_id.to_string(), - author_pubkey: BUYER.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(BUYER), payload: clean_request_payload(), } } @@ -3661,19 +3695,19 @@ mod tests { RadrootsOrderDecision { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), decision, } } fn accepted_decision_record(event_id: &str) -> RadrootsOrderDecisionRecord { RadrootsOrderDecisionRecord { - event_id: event_id.to_string(), - author_pubkey: SELLER.to_string(), - counterparty_pubkey: BUYER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: "request-1".to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(SELLER), + counterparty_pubkey: pubkey(BUYER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id("request-1"), payload: decision_payload(RadrootsOrderDecisionOutcome::Accepted { inventory_commitments: vec![RadrootsOrderInventoryCommitment { bin_id: bin_id("bin-1"), @@ -3685,11 +3719,11 @@ mod tests { fn declined_decision_record(event_id: &str) -> RadrootsOrderDecisionRecord { RadrootsOrderDecisionRecord { - event_id: event_id.to_string(), - author_pubkey: SELLER.to_string(), - counterparty_pubkey: BUYER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: "request-1".to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(SELLER), + counterparty_pubkey: pubkey(BUYER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id("request-1"), payload: decision_payload(RadrootsOrderDecisionOutcome::Declined { reason: "out_of_stock".to_string(), }), @@ -3702,16 +3736,16 @@ mod tests { status: RadrootsOrderFulfillmentState, ) -> RadrootsOrderFulfillmentRecord { RadrootsOrderFulfillmentRecord { - event_id: event_id.to_string(), - author_pubkey: SELLER.to_string(), - counterparty_pubkey: BUYER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(SELLER), + counterparty_pubkey: pubkey(BUYER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderFulfillmentUpdate { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), status, }, } @@ -3719,16 +3753,16 @@ mod tests { fn cancellation_record(event_id: &str, prev_event_id: &str) -> RadrootsOrderCancellationRecord { RadrootsOrderCancellationRecord { - event_id: event_id.to_string(), - author_pubkey: BUYER.to_string(), - counterparty_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(BUYER), + counterparty_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderCancellation { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), reason: "changed plans".to_string(), }, } @@ -3740,16 +3774,16 @@ mod tests { received: bool, ) -> RadrootsOrderReceiptRecord { RadrootsOrderReceiptRecord { - event_id: event_id.to_string(), - author_pubkey: BUYER.to_string(), - counterparty_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(BUYER), + counterparty_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderReceipt { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), received, issue: (!received).then(|| "damaged items".to_string()), received_at: 1_777_665_600, @@ -3760,19 +3794,19 @@ mod tests { fn payment_record(event_id: &str, prev_event_id: &str) -> RadrootsOrderPaymentEventRecord { let economics = request_economics("bin-1", 2, "10"); RadrootsOrderPaymentEventRecord { - event_id: event_id.to_string(), - author_pubkey: BUYER.to_string(), - counterparty_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(BUYER), + counterparty_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderPaymentPayload { order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - previous_event_id: prev_event_id.to_string(), - agreement_event_id: "decision-1".to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + previous_event_id: test_event_id(prev_event_id), + agreement_event_id: test_event_id("decision-1"), quote_id: economics.quote_id.clone(), quote_version: economics.quote_version, economics_digest: economics_digest( @@ -3794,20 +3828,20 @@ mod tests { ) -> RadrootsOrderSettlementRecord { let payment = payment_record(payment_event_id, "decision-1"); RadrootsOrderSettlementRecord { - event_id: event_id.to_string(), - author_pubkey: SELLER.to_string(), - counterparty_pubkey: BUYER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: payment_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(SELLER), + counterparty_pubkey: pubkey(BUYER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(payment_event_id), payload: RadrootsOrderSettlementDecision { order_id: payment.payload.order_id, listing_addr: payment.payload.listing_addr, seller_pubkey: payment.payload.seller_pubkey, buyer_pubkey: payment.payload.buyer_pubkey, - root_event_id: payment.payload.root_event_id, - previous_event_id: payment_event_id.to_string(), + root_event_id: payment.payload.root_event_id.clone(), + previous_event_id: test_event_id(payment_event_id), agreement_event_id: payment.payload.agreement_event_id, - payment_event_id: payment_event_id.to_string(), + payment_event_id: test_event_id(payment_event_id), quote_id: payment.payload.quote_id, quote_version: payment.payload.quote_version, economics_digest: payment.payload.economics_digest, @@ -3827,8 +3861,8 @@ mod tests { bin_count: u32, ) -> RadrootsOrderDecisionRecord { let mut decision = accepted_decision_record(event_id); - decision.root_event_id = request_event_id.to_string(); - decision.prev_event_id = request_event_id.to_string(); + decision.root_event_id = test_event_id(request_event_id); + decision.prev_event_id = test_event_id(request_event_id); decision.payload.order_id = order_id(raw_order_id); let RadrootsOrderDecisionOutcome::Accepted { inventory_commitments, @@ -3842,7 +3876,7 @@ mod tests { fn inventory_bin(available_count: u64) -> RadrootsListingInventoryBinAvailability { RadrootsListingInventoryBinAvailability { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count, } } @@ -3856,19 +3890,19 @@ mod tests { let subtotal = (RadrootsCoreDecimal::from(5u32) * RadrootsCoreDecimal::from(bin_count)).to_string(); RadrootsOrderRevisionProposalRecord { - event_id: event_id.to_string(), - author_pubkey: SELLER.to_string(), - counterparty_pubkey: BUYER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(SELLER), + counterparty_pubkey: pubkey(BUYER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderRevisionProposal { revision_id: order_revision_id(revision_id), order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), items: vec![RadrootsOrderItem { bin_id: bin_id("bin-1"), bin_count, @@ -3886,19 +3920,19 @@ mod tests { decision: RadrootsOrderRevisionOutcome, ) -> RadrootsOrderRevisionDecisionRecord { RadrootsOrderRevisionDecisionRecord { - event_id: event_id.to_string(), - author_pubkey: BUYER.to_string(), - counterparty_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + event_id: test_event_id(event_id), + author_pubkey: pubkey(BUYER), + counterparty_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), payload: RadrootsOrderRevisionDecision { revision_id: order_revision_id(revision_id), order_id: order_id("order-1"), listing_addr: listing_address(), - buyer_pubkey: BUYER.to_string(), - seller_pubkey: SELLER.to_string(), - root_event_id: "request-1".to_string(), - prev_event_id: prev_event_id.to_string(), + buyer_pubkey: pubkey(BUYER), + seller_pubkey: pubkey(SELLER), + root_event_id: test_event_id("request-1"), + prev_event_id: test_event_id(prev_event_id), decision, }, } @@ -3919,8 +3953,9 @@ mod tests { L: IntoIterator<Item = RadrootsOrderCancellationRecord>, M: IntoIterator<Item = RadrootsOrderReceiptRecord>, { + let order_id = RadrootsOrderId::parse(order_id).expect("order id"); reduce_order_events_with_revisions( - order_id, + &order_id, requests, decisions, Vec::<RadrootsOrderRevisionProposalRecord>::new(), @@ -3951,9 +3986,11 @@ mod tests { M: IntoIterator<Item = RadrootsOrderCancellationRecord>, N: IntoIterator<Item = RadrootsOrderReceiptRecord>, { + let listing_addr = RadrootsListingAddress::parse(listing_addr).expect("listing address"); + let listing_event_id = test_event_id(listing_event_id); reduce_listing_inventory_accounting_with_revisions( - listing_addr, - listing_event_id, + &listing_addr, + &listing_event_id, bins, requests, decisions, @@ -4095,8 +4132,14 @@ mod tests { let projection = reduce_order_events("order-1", [request_record()], [], [], [], []); assert_eq!(projection.status, RadrootsOrderStatus::Requested); - assert_eq!(projection.request_event_id.as_deref(), Some("request-1")); - assert_eq!(projection.last_event_id.as_deref(), Some("request-1")); + assert_eq!( + projection.request_event_id.as_ref(), + Some(&test_event_id("request-1")) + ); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("request-1")) + ); assert_eq!( projection.economics, Some(request_economics("bin-1", 2, "10")) @@ -4115,13 +4158,19 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Accepted); - assert_eq!(projection.decision_event_id.as_deref(), Some("decision-1")); + assert_eq!( + projection.decision_event_id.as_ref(), + Some(&test_event_id("decision-1")) + ); assert_eq!( projection.fulfillment_status, Some(RadrootsOrderFulfillmentState::AcceptedNotFulfilled) ); assert_eq!(projection.fulfillment_event_id, None); - assert_eq!(projection.last_event_id.as_deref(), Some("decision-1")); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("decision-1")) + ); assert_eq!( projection.economics, Some(request_economics("bin-1", 2, "10")) @@ -4131,7 +4180,7 @@ mod tests { #[test] fn reduce_order_events_reports_recorded_payment_state() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], Vec::<RadrootsOrderRevisionProposalRecord>::new(), @@ -4153,12 +4202,12 @@ mod tests { RadrootsOrderSettlementState::Pending ); assert_eq!( - projection.payment.payment_event_id.as_deref(), - Some("payment-1") + projection.payment.payment_event_id.as_ref(), + Some(&test_event_id("payment-1")) ); assert_eq!( - projection.payment.agreement_event_id.as_deref(), - Some("decision-1") + projection.payment.agreement_event_id.as_ref(), + Some(&test_event_id("decision-1")) ); assert_eq!(projection.payment.amount, Some(decimal("10"))); assert_eq!(projection.payment.currency, Some(RadrootsCoreCurrency::USD)); @@ -4168,7 +4217,7 @@ mod tests { #[test] fn reduce_order_events_reports_accepted_settlement_state() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], Vec::<RadrootsOrderRevisionProposalRecord>::new(), @@ -4191,8 +4240,8 @@ mod tests { RadrootsOrderSettlementState::Accepted ); assert_eq!( - projection.payment.settlement_event_id.as_deref(), - Some("settlement-1") + projection.payment.settlement_event_id.as_ref(), + Some(&test_event_id("settlement-1")) ); assert_eq!(projection.payment.reason, None); assert!(projection.issues.is_empty()); @@ -4204,7 +4253,7 @@ mod tests { payment.payload.amount = decimal("9"); let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], Vec::<RadrootsOrderRevisionProposalRecord>::new(), @@ -4221,14 +4270,14 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::PaymentAmountMismatch { event_id } - if event_id == "payment-1" + if event_id == &test_event_id("payment-1") ))); } #[test] fn reduce_order_events_keeps_payment_separate_from_receipt() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], Vec::<RadrootsOrderRevisionProposalRecord>::new(), @@ -4260,7 +4309,7 @@ mod tests { #[test] fn reduce_order_events_applies_accepted_revision_agreement() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], [revision_proposal_record( @@ -4284,12 +4333,12 @@ mod tests { assert_eq!(projection.status, RadrootsOrderStatus::Accepted); assert_eq!( - projection.agreement_event_id.as_deref(), - Some("revision-decision-1") + projection.agreement_event_id.as_ref(), + Some(&test_event_id("revision-decision-1")) ); assert_eq!( - projection.last_event_id.as_deref(), - Some("revision-decision-1") + projection.last_event_id.as_ref(), + Some(&test_event_id("revision-decision-1")) ); assert_eq!( projection.economics, @@ -4301,7 +4350,7 @@ mod tests { #[test] fn reduce_order_events_preserves_agreement_after_declined_revision() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], [revision_proposal_record( @@ -4326,10 +4375,13 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Accepted); - assert_eq!(projection.agreement_event_id.as_deref(), Some("decision-1")); assert_eq!( - projection.last_event_id.as_deref(), - Some("revision-decision-1") + projection.agreement_event_id.as_ref(), + Some(&test_event_id("decision-1")) + ); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("revision-decision-1")) ); assert_eq!( projection.economics, @@ -4346,10 +4398,10 @@ mod tests { "revision-1", RadrootsOrderRevisionOutcome::Accepted, ); - decision.author_pubkey = SELLER.to_string(); + decision.author_pubkey = pubkey(SELLER); let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], [revision_proposal_record( @@ -4370,14 +4422,14 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::RevisionDecisionAuthorMismatch { event_id } - if event_id == "revision-decision-1" + if event_id == &test_event_id("revision-decision-1") ))); } #[test] fn reduce_order_events_rejects_stale_revision_decision() { let projection = reduce_order_events_with_revisions( - "order-1", + &order_id("order-1"), [request_record()], [accepted_decision_record("decision-1")], [revision_proposal_record( @@ -4403,7 +4455,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::RevisionDecisionWithoutProposal { event_id } - if event_id == "revision-decision-1" + if event_id == &test_event_id("revision-decision-1") ))); } @@ -4419,7 +4471,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::RequestPayloadInvalid { - event_id: "request-1".to_string() + event_id: test_event_id("request-1") }] ); } @@ -4452,10 +4504,13 @@ mod tests { Some(RadrootsOrderFulfillmentState::ReadyForPickup) ); assert_eq!( - projection.fulfillment_event_id.as_deref(), - Some("fulfillment-2") + projection.fulfillment_event_id.as_ref(), + Some(&test_event_id("fulfillment-2")) + ); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("fulfillment-2")) ); - assert_eq!(projection.last_event_id.as_deref(), Some("fulfillment-2")); } #[test] @@ -4493,12 +4548,18 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Cancelled); - assert_eq!(projection.request_event_id.as_deref(), Some("request-1")); assert_eq!( - projection.cancellation_event_id.as_deref(), - Some("cancel-1") + projection.request_event_id.as_ref(), + Some(&test_event_id("request-1")) + ); + assert_eq!( + projection.cancellation_event_id.as_ref(), + Some(&test_event_id("cancel-1")) + ); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("cancel-1")) ); - assert_eq!(projection.last_event_id.as_deref(), Some("cancel-1")); assert!(projection.lifecycle_terminal); assert_eq!( projection.payment, @@ -4522,7 +4583,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ForkedLifecycle { - event_ids: vec!["cancel-1".to_string(), "decision-1".to_string()] + event_ids: vec![test_event_id("cancel-1"), test_event_id("decision-1")] }] ); } @@ -4539,12 +4600,18 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Cancelled); - assert_eq!(projection.decision_event_id.as_deref(), Some("decision-1")); assert_eq!( - projection.cancellation_event_id.as_deref(), - Some("cancel-1") + projection.decision_event_id.as_ref(), + Some(&test_event_id("decision-1")) + ); + assert_eq!( + projection.cancellation_event_id.as_ref(), + Some(&test_event_id("cancel-1")) + ); + assert_eq!( + projection.last_event_id.as_ref(), + Some(&test_event_id("cancel-1")) ); - assert_eq!(projection.last_event_id.as_deref(), Some("cancel-1")); assert!(projection.lifecycle_terminal); } @@ -4567,7 +4634,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ForkedLifecycle { - event_ids: vec!["cancel-1".to_string(), "fulfillment-1".to_string()] + event_ids: vec![test_event_id("cancel-1"), test_event_id("fulfillment-1")] }] ); } @@ -4589,10 +4656,13 @@ mod tests { assert_eq!(projection.status, RadrootsOrderStatus::Completed); assert_eq!( - projection.fulfillment_event_id.as_deref(), - Some("fulfillment-1") + projection.fulfillment_event_id.as_ref(), + Some(&test_event_id("fulfillment-1")) + ); + assert_eq!( + projection.receipt_event_id.as_ref(), + Some(&test_event_id("receipt-1")) ); - assert_eq!(projection.receipt_event_id.as_deref(), Some("receipt-1")); assert_eq!(projection.receipt_received, Some(true)); assert_eq!(projection.receipt_issue, None); assert_eq!(projection.receipt_received_at, Some(1_777_665_600)); @@ -4629,7 +4699,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ForkedLifecycle { - event_ids: vec!["fulfillment-2".to_string(), "receipt-1".to_string()] + event_ids: vec![test_event_id("fulfillment-2"), test_event_id("receipt-1")] }] ); } @@ -4650,7 +4720,10 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Disputed); - assert_eq!(projection.receipt_event_id.as_deref(), Some("receipt-1")); + assert_eq!( + projection.receipt_event_id.as_ref(), + Some(&test_event_id("receipt-1")) + ); assert_eq!(projection.receipt_received, Some(false)); assert_eq!(projection.receipt_issue.as_deref(), Some("damaged items")); assert!(projection.lifecycle_terminal); @@ -4679,7 +4752,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ReceiptWithoutEligibleFulfillment { - event_id: "receipt-1".to_string() + event_id: test_event_id("receipt-1") }] ); } @@ -4703,7 +4776,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::FulfillmentWithoutAcceptedDecision { - event_id: "fulfillment-1".to_string() + event_id: test_event_id("fulfillment-1") }] ); } @@ -4727,7 +4800,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::FulfillmentWithoutAcceptedDecision { - event_id: "fulfillment-1".to_string() + event_id: test_event_id("fulfillment-1") }] ); } @@ -4739,7 +4812,7 @@ mod tests { "decision-1", RadrootsOrderFulfillmentState::Preparing, ); - fulfillment.author_pubkey = BUYER.to_string(); + fulfillment.author_pubkey = pubkey(BUYER); let projection = reduce_order_events( "order-1", @@ -4754,7 +4827,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::FulfillmentAuthorMismatch { event_id } - if event_id == "fulfillment-1" + if event_id == &test_event_id("fulfillment-1") ))); } @@ -4784,7 +4857,10 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ForkedFulfillments { - event_ids: vec!["fulfillment-1".to_string(), "fulfillment-2".to_string()] + event_ids: vec![ + test_event_id("fulfillment-1"), + test_event_id("fulfillment-2") + ] }] ); } @@ -4815,7 +4891,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::FulfillmentUnsupportedTransition { - event_id: "fulfillment-2".to_string() + event_id: test_event_id("fulfillment-2") }] ); } @@ -4832,7 +4908,10 @@ mod tests { ); assert_eq!(projection.status, RadrootsOrderStatus::Declined); - assert_eq!(projection.decision_event_id.as_deref(), Some("decision-1")); + assert_eq!( + projection.decision_event_id.as_ref(), + Some(&test_event_id("decision-1")) + ); } #[test] @@ -4848,22 +4927,28 @@ mod tests { [], ); - assert_eq!(projection.listing_event_id, "listing-event-1"); - assert_eq!(projection.declined_order_ids, Vec::<String>::new()); - assert_eq!(projection.cancelled_order_ids, Vec::<String>::new()); - assert_eq!(projection.invalid_event_ids, Vec::<String>::new()); + assert_eq!( + projection.listing_event_id, + test_event_id("listing-event-1") + ); + assert_eq!(projection.declined_order_ids, Vec::<RadrootsOrderId>::new()); + assert_eq!( + projection.cancelled_order_ids, + Vec::<RadrootsOrderId>::new() + ); + assert_eq!(projection.invalid_event_ids, Vec::<RadrootsEventId>::new()); assert!(projection.issues.is_empty()); assert_eq!( projection.bins, vec![RadrootsListingInventoryBinAccounting { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count: 5, accepted_reserved_count: 2, remaining_count: 3, over_reserved: false, accepted_orders: vec![RadrootsListingInventoryOrderReservation { - order_id: "order-1".to_string(), - decision_event_id: "decision-1".to_string(), + order_id: order_id("order-1"), + decision_event_id: test_event_id("decision-1"), bin_count: 2, }], }] @@ -4873,8 +4958,8 @@ mod tests { #[test] fn reduce_listing_inventory_accounting_reserves_accepted_revision_inventory() { let projection = reduce_listing_inventory_accounting_with_revisions( - &listing_addr(), - "listing-event-1", + &listing_address(), + &test_event_id("listing-event-1"), [inventory_bin(5)], [request_record()], [accepted_decision_record("decision-1")], @@ -4901,8 +4986,8 @@ mod tests { assert_eq!( projection.bins[0].accepted_orders, vec![RadrootsListingInventoryOrderReservation { - order_id: "order-1".to_string(), - decision_event_id: "revision-decision-1".to_string(), + order_id: order_id("order-1"), + decision_event_id: test_event_id("revision-decision-1"), bin_count: 1, }] ); @@ -4926,7 +5011,7 @@ mod tests { ); assert!(projection.issues.is_empty()); - assert_eq!(projection.invalid_event_ids, Vec::<String>::new()); + assert_eq!(projection.invalid_event_ids, Vec::<RadrootsEventId>::new()); assert_eq!(projection.bins[0].accepted_reserved_count, 0); assert_eq!(projection.bins[0].remaining_count, 5); assert!(projection.bins[0].accepted_orders.is_empty()); @@ -4946,8 +5031,8 @@ mod tests { ); assert!(projection.issues.is_empty()); - assert_eq!(projection.cancelled_order_ids, vec!["order-1".to_string()]); - assert_eq!(projection.invalid_event_ids, Vec::<String>::new()); + assert_eq!(projection.cancelled_order_ids, vec![order_id("order-1")]); + assert_eq!(projection.invalid_event_ids, Vec::<RadrootsEventId>::new()); assert_eq!(projection.bins[0].accepted_reserved_count, 0); assert_eq!(projection.bins[0].remaining_count, 5); assert!(projection.bins[0].accepted_orders.is_empty()); @@ -5003,13 +5088,19 @@ mod tests { assert_eq!(projection.bins[0].accepted_reserved_count, 0); assert_eq!( projection.invalid_event_ids, - vec!["fulfillment-1".to_string(), "fulfillment-2".to_string()] + vec![ + test_event_id("fulfillment-1"), + test_event_id("fulfillment-2") + ] ); assert_eq!( projection.issues, vec![RadrootsListingInventoryAccountingIssue::InvalidOrder { - order_id: "order-1".to_string(), - event_ids: vec!["fulfillment-1".to_string(), "fulfillment-2".to_string()], + order_id: order_id("order-1"), + event_ids: vec![ + test_event_id("fulfillment-1"), + test_event_id("fulfillment-2") + ], }] ); } @@ -5027,7 +5118,7 @@ mod tests { [], ); - assert_eq!(projection.declined_order_ids, vec!["order-1".to_string()]); + assert_eq!(projection.declined_order_ids, vec![order_id("order-1")]); assert!(projection.cancelled_order_ids.is_empty()); assert!(projection.invalid_event_ids.is_empty()); assert!(projection.issues.is_empty()); @@ -5060,12 +5151,15 @@ mod tests { ); assert_eq!(projection.bins[0].accepted_reserved_count, 0); - assert_eq!(projection.invalid_event_ids, vec!["decision-1".to_string()]); + assert_eq!( + projection.invalid_event_ids, + vec![test_event_id("decision-1")] + ); assert_eq!( projection.issues, vec![RadrootsListingInventoryAccountingIssue::InvalidOrder { - order_id: "order-1".to_string(), - event_ids: vec!["decision-1".to_string()], + order_id: order_id("order-1"), + event_ids: vec![test_event_id("decision-1")], }] ); } @@ -5096,10 +5190,10 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsListingInventoryAccountingIssue::OverReserved { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count: 3, reserved_count: 4, - event_ids: vec!["decision-1".to_string(), "decision-2".to_string()], + event_ids: vec![test_event_id("decision-1"), test_event_id("decision-2")], }] ); } @@ -5111,7 +5205,7 @@ mod tests { "listing-event-1", [ RadrootsListingInventoryBinAvailability { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count: u64::MAX, }, inventory_bin(1), @@ -5130,7 +5224,7 @@ mod tests { projection.issues, vec![ RadrootsListingInventoryAccountingIssue::ArithmeticOverflow { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), event_ids: Vec::new(), } ] @@ -5140,7 +5234,7 @@ mod tests { #[test] fn add_inventory_reservation_reports_reservation_overflow() { let mut bin = RadrootsListingInventoryBinAccounting { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count: u64::MAX, accepted_reserved_count: u64::MAX, remaining_count: 0, @@ -5150,7 +5244,13 @@ mod tests { let decision = accepted_decision_record("decision-overflow"); let mut issues = Vec::new(); - add_inventory_reservation(&mut bin, "order-overflow", &decision, 1, &mut issues); + add_inventory_reservation( + &mut bin, + &order_id("order-overflow"), + &decision, + 1, + &mut issues, + ); assert_eq!(bin.accepted_reserved_count, u64::MAX); assert!(bin.accepted_orders.is_empty()); @@ -5158,8 +5258,8 @@ mod tests { issues, vec![ RadrootsListingInventoryAccountingIssue::ArithmeticOverflow { - bin_id: "bin-1".to_string(), - event_ids: vec!["decision-overflow".to_string()], + bin_id: bin_id("bin-1"), + event_ids: vec![test_event_id("decision-overflow")], } ] ); @@ -5168,22 +5268,22 @@ mod tests { #[test] fn inventory_accounting_issues_sort_by_rank_id_and_event_ids() { let invalid = RadrootsListingInventoryAccountingIssue::InvalidOrder { - order_id: "order-1".to_string(), - event_ids: vec!["event-c".to_string()], + order_id: order_id("order-1"), + event_ids: vec![test_event_id("event-c")], }; let overflow = RadrootsListingInventoryAccountingIssue::ArithmeticOverflow { - bin_id: "bin-1".to_string(), - event_ids: vec!["event-b".to_string()], + bin_id: bin_id("bin-1"), + event_ids: vec![test_event_id("event-b")], }; let unknown = RadrootsListingInventoryAccountingIssue::UnknownInventoryBin { - bin_id: "bin-1".to_string(), - event_ids: vec!["event-a".to_string()], + bin_id: bin_id("bin-1"), + event_ids: vec![test_event_id("event-a")], }; let over_reserved = RadrootsListingInventoryAccountingIssue::OverReserved { - bin_id: "bin-1".to_string(), + bin_id: bin_id("bin-1"), available_count: 1, reserved_count: 2, - event_ids: vec!["event-d".to_string()], + event_ids: vec![test_event_id("event-d")], }; assert_eq!(inventory_issue_rank(&invalid), 0); @@ -5194,10 +5294,22 @@ mod tests { assert_eq!(inventory_issue_id(&overflow), "bin-1"); assert_eq!(inventory_issue_id(&unknown), "bin-1"); assert_eq!(inventory_issue_id(&over_reserved), "bin-1"); - assert_eq!(inventory_issue_event_ids(&invalid), ["event-c"]); - assert_eq!(inventory_issue_event_ids(&overflow), ["event-b"]); - assert_eq!(inventory_issue_event_ids(&unknown), ["event-a"]); - assert_eq!(inventory_issue_event_ids(&over_reserved), ["event-d"]); + assert_eq!( + inventory_issue_event_ids(&invalid), + [test_event_id("event-c")] + ); + assert_eq!( + inventory_issue_event_ids(&overflow), + [test_event_id("event-b")] + ); + assert_eq!( + inventory_issue_event_ids(&unknown), + [test_event_id("event-a")] + ); + assert_eq!( + inventory_issue_event_ids(&over_reserved), + [test_event_id("event-d")] + ); let mut issues = vec![ over_reserved.clone(), @@ -5213,7 +5325,7 @@ mod tests { #[test] fn reduce_order_events_rejects_invalid_decision_actor() { let mut decision = accepted_decision_record("decision-1"); - decision.author_pubkey = BUYER.to_string(); + decision.author_pubkey = pubkey(BUYER); let projection = reduce_order_events("order-1", [request_record()], [decision], [], [], []); @@ -5221,14 +5333,14 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionAuthorMismatch { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } #[test] fn reduce_order_events_rejects_invalid_decision_counterparty() { let mut decision = accepted_decision_record("decision-1"); - decision.counterparty_pubkey = SELLER.to_string(); + decision.counterparty_pubkey = pubkey(SELLER); let projection = reduce_order_events("order-1", [request_record()], [decision], [], [], []); @@ -5236,14 +5348,14 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionCounterpartyMismatch { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } #[test] fn reduce_listing_inventory_accounting_ignores_wrong_counterparty_decision() { let mut decision = accepted_decision_record("decision-1"); - decision.counterparty_pubkey = SELLER.to_string(); + decision.counterparty_pubkey = pubkey(SELLER); let projection = reduce_listing_inventory_accounting( &listing_addr(), @@ -5257,12 +5369,15 @@ mod tests { ); assert_eq!(projection.bins[0].accepted_reserved_count, 0); - assert_eq!(projection.invalid_event_ids, vec!["decision-1".to_string()]); + assert_eq!( + projection.invalid_event_ids, + vec![test_event_id("decision-1")] + ); assert_eq!( projection.issues, vec![RadrootsListingInventoryAccountingIssue::InvalidOrder { - order_id: "order-1".to_string(), - event_ids: vec!["decision-1".to_string()], + order_id: order_id("order-1"), + event_ids: vec![test_event_id("decision-1")], }] ); } @@ -5270,7 +5385,7 @@ mod tests { #[test] fn reduce_order_events_rejects_invalid_decision_chain() { let mut decision = accepted_decision_record("decision-1"); - decision.prev_event_id = "request-2".to_string(); + decision.prev_event_id = test_event_id("request-2"); let projection = reduce_order_events("order-1", [request_record()], [decision], [], [], []); @@ -5278,7 +5393,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionPreviousMismatch { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } @@ -5297,7 +5412,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionMissingInventoryCommitments { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } @@ -5319,7 +5434,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionInventoryCommitmentMismatch { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } @@ -5341,7 +5456,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::DecisionInventoryCommitmentMismatch { - event_id: "decision-1".to_string() + event_id: test_event_id("decision-1") }] ); } @@ -5390,7 +5505,7 @@ mod tests { assert!(projection.issues.iter().any(|issue| matches!( issue, RadrootsOrderIssue::DecisionMissingReason { event_id } - if event_id == "decision-1" + if event_id == &test_event_id("decision-1") ))); } @@ -5412,7 +5527,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ConflictingDecisions { - event_ids: vec!["decision-1".to_string(), "decision-2".to_string()] + event_ids: vec![test_event_id("decision-1"), test_event_id("decision-2")] }] ); } @@ -5444,11 +5559,14 @@ mod tests { assert_eq!(projection, reversed); assert_eq!(projection.status, RadrootsOrderStatus::Invalid); - assert_eq!(projection.request_event_id.as_deref(), Some("request-1")); + assert_eq!( + projection.request_event_id.as_ref(), + Some(&test_event_id("request-2")) + ); assert_eq!( projection.issues, vec![RadrootsOrderIssue::MultipleRequests { - event_ids: vec!["request-1".to_string(), "request-2".to_string()] + event_ids: vec![test_event_id("request-2"), test_event_id("request-1")] }] ); } @@ -5483,7 +5601,7 @@ mod tests { assert_eq!( projection.issues, vec![RadrootsOrderIssue::ConflictingDecisions { - event_ids: vec!["decision-1".to_string(), "decision-2".to_string()] + event_ids: vec![test_event_id("decision-1"), test_event_id("decision-2")] }] ); } @@ -5493,7 +5611,7 @@ mod tests { macro_rules! issue { ($variant:ident, $id:expr) => { RadrootsOrderIssue::$variant { - event_id: $id.to_string(), + event_id: test_event_id($id), } }; } @@ -5501,7 +5619,7 @@ mod tests { let issues = vec![ RadrootsOrderIssue::MissingRequest, RadrootsOrderIssue::MultipleRequests { - event_ids: vec!["multi-b".to_string(), "multi-a".to_string()], + event_ids: vec![test_event_id("multi-b"), test_event_id("multi-a")], }, issue!(RequestPayloadInvalid, "request-payload"), issue!(RequestOrderIdMismatch, "request-order"), @@ -5528,7 +5646,7 @@ mod tests { ), issue!(DecisionMissingReason, "decision-missing-reason"), RadrootsOrderIssue::ConflictingDecisions { - event_ids: vec!["conflict-b".to_string(), "conflict-a".to_string()], + event_ids: vec![test_event_id("conflict-b"), test_event_id("conflict-a")], }, issue!( RevisionProposalWithoutAcceptedDecision, @@ -5595,8 +5713,8 @@ mod tests { ), RadrootsOrderIssue::ForkedFulfillments { event_ids: vec![ - "fulfillment-fork-b".to_string(), - "fulfillment-fork-a".to_string(), + test_event_id("fulfillment-fork-b"), + test_event_id("fulfillment-fork-a"), ], }, issue!( @@ -5658,8 +5776,8 @@ mod tests { issue!(RevisionAfterPayment, "revision-after-payment"), RadrootsOrderIssue::DuplicatePayments { event_ids: vec![ - "payment-duplicate-b".to_string(), - "payment-duplicate-a".to_string(), + test_event_id("payment-duplicate-b"), + test_event_id("payment-duplicate-a"), ], }, issue!(SettlementWithoutValidPayment, "settlement-without-payment"), @@ -5685,29 +5803,22 @@ mod tests { issue!(SettlementCurrencyMismatch, "settlement-currency"), RadrootsOrderIssue::DuplicateSettlements { event_ids: vec![ - "settlement-duplicate-b".to_string(), - "settlement-duplicate-a".to_string(), + test_event_id("settlement-duplicate-b"), + test_event_id("settlement-duplicate-a"), ], }, RadrootsOrderIssue::ForkedLifecycle { - event_ids: vec!["lifecycle-b".to_string(), "lifecycle-a".to_string()], + event_ids: vec![test_event_id("lifecycle-b"), test_event_id("lifecycle-a")], }, ]; let event_ids = projection_issue_event_ids(&issues); - assert_eq!( - event_ids.first().map(String::as_str), - Some("cancellation-after-fulfillment") - ); - assert_eq!( - event_ids.last().map(String::as_str), - Some("settlement-without-payment") - ); - assert_eq!(event_ids.contains(&"payment-digest".to_string()), true); - assert_eq!(event_ids.contains(&"multi-a".to_string()), true); - assert_eq!(event_ids.contains(&"multi-b".to_string()), true); - assert_eq!(event_ids.contains(&"missing-request".to_string()), false); + assert!(event_ids.windows(2).all(|pair| pair[0] <= pair[1])); + assert_eq!(event_ids.contains(&test_event_id("payment-digest")), true); + assert_eq!(event_ids.contains(&test_event_id("multi-a")), true); + assert_eq!(event_ids.contains(&test_event_id("multi-b")), true); + assert_eq!(event_ids.contains(&test_event_id("missing-request")), false); assert_eq!(event_ids.len(), 126); } }