lib

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

commit ab015cb372c34340665b1e67ad87e6325ed87a16
parent 20731e6024a36485ed511b16934b131087249930
Author: triesap <tyson@radroots.org>
Date:   Sun, 24 May 2026 10:28:46 +0000

local_events: add buyer order work contract

- add shared local-work constants for app-authored buyer order requests
- add deterministic app order request record-id derivation
- validate current no-payment order request payload shape
- cover the order work contract with focused local-events tests

Diffstat:
Mcrates/local_events/src/lib.rs | 7+++++++
Acrates/local_events/src/order_work.rs | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrates/local_events/tests/order_work.rs | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 183 insertions(+), 0 deletions(-)

diff --git a/crates/local_events/src/lib.rs b/crates/local_events/src/lib.rs @@ -3,6 +3,7 @@ mod error; mod migrations; mod models; +mod order_work; mod store; pub use error::LocalEventsError; @@ -11,4 +12,10 @@ pub use models::{ LocalEventRecord, LocalEventRecordInput, LocalEventRecordUpdate, LocalEventsCursor, LocalRecordFamily, LocalRecordStatus, PublishOutboxStatus, SourceRuntime, }; +pub use order_work::{ + BUYER_ORDER_REQUEST_ACTOR_SOURCE_RESOLVED_ACCOUNT, + BUYER_ORDER_REQUEST_ACTOR_SOURCE_UNRESOLVED_APP, BUYER_ORDER_REQUEST_DOCUMENT_KIND, + BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND, buyer_order_request_local_work_record_id, + validate_buyer_order_request_local_work_payload, +}; pub use store::LocalEventsStore; diff --git a/crates/local_events/src/order_work.rs b/crates/local_events/src/order_work.rs @@ -0,0 +1,99 @@ +use serde_json::Value; + +use crate::LocalEventsError; +use crate::models::validate_non_empty; + +pub const BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND: &str = "buyer_order_request_v1"; +pub const BUYER_ORDER_REQUEST_DOCUMENT_KIND: &str = "order_draft_v1"; +pub const BUYER_ORDER_REQUEST_ACTOR_SOURCE_RESOLVED_ACCOUNT: &str = "resolved_account"; +pub const BUYER_ORDER_REQUEST_ACTOR_SOURCE_UNRESOLVED_APP: &str = "app_unresolved"; + +pub fn buyer_order_request_local_work_record_id( + order_id: &str, +) -> Result<String, LocalEventsError> { + let order_id = order_id.trim(); + validate_non_empty("order_id", order_id)?; + Ok(format!("app:local_work:order_request:{order_id}")) +} + +pub fn validate_buyer_order_request_local_work_payload( + payload: &Value, +) -> Result<(), LocalEventsError> { + validate_string_field( + payload, + &["record_kind"], + BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND, + )?; + validate_string_field( + payload, + &["document", "kind"], + BUYER_ORDER_REQUEST_DOCUMENT_KIND, + )?; + validate_required_string(payload, &["document", "order", "order_id"], "order_id")?; + validate_bool_field(payload, &["currentness", "current"], true)?; + validate_bool_field(payload, &["no_payment", "payment_required"], false)?; + validate_bool_field(payload, &["no_payment", "settlement_deferred"], true)?; + Ok(()) +} + +fn validate_string_field( + payload: &Value, + path: &[&str], + expected: &str, +) -> Result<(), LocalEventsError> { + let Some(value) = value_at(payload, path).and_then(Value::as_str) else { + return Err(LocalEventsError::InvalidRecord(format!( + "missing required local order field `{}`", + path.join(".") + ))); + }; + if value != expected { + return Err(LocalEventsError::InvalidRecord(format!( + "local order field `{}` must be `{expected}`", + path.join(".") + ))); + } + Ok(()) +} + +fn validate_required_string( + payload: &Value, + path: &[&str], + field: &str, +) -> Result<(), LocalEventsError> { + let Some(value) = value_at(payload, path).and_then(Value::as_str) else { + return Err(LocalEventsError::InvalidRecord(format!( + "missing required local order field `{}`", + path.join(".") + ))); + }; + validate_non_empty(field, value) +} + +fn validate_bool_field( + payload: &Value, + path: &[&str], + expected: bool, +) -> Result<(), LocalEventsError> { + let Some(value) = value_at(payload, path).and_then(Value::as_bool) else { + return Err(LocalEventsError::InvalidRecord(format!( + "missing required local order field `{}`", + path.join(".") + ))); + }; + if value != expected { + return Err(LocalEventsError::InvalidRecord(format!( + "local order field `{}` must be `{expected}`", + path.join(".") + ))); + } + Ok(()) +} + +fn value_at<'a>(payload: &'a Value, path: &[&str]) -> Option<&'a Value> { + let mut current = payload; + for part in path { + current = current.get(*part)?; + } + Some(current) +} diff --git a/crates/local_events/tests/order_work.rs b/crates/local_events/tests/order_work.rs @@ -0,0 +1,77 @@ +use radroots_local_events::{ + BUYER_ORDER_REQUEST_ACTOR_SOURCE_RESOLVED_ACCOUNT, BUYER_ORDER_REQUEST_DOCUMENT_KIND, + BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND, buyer_order_request_local_work_record_id, + validate_buyer_order_request_local_work_payload, +}; +use serde_json::json; + +#[test] +fn buyer_order_request_record_id_is_deterministic_for_app_orders() { + assert_eq!( + buyer_order_request_local_work_record_id(" order-1 ").expect("record id"), + "app:local_work:order_request:order-1" + ); +} + +#[test] +fn buyer_order_request_payload_requires_current_no_payment_order_document() { + let payload = json!({ + "record_kind": BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND, + "scope": "app", + "currentness": { + "current": true + }, + "no_payment": { + "payment_required": false, + "settlement_deferred": true + }, + "document": { + "version": 1, + "kind": BUYER_ORDER_REQUEST_DOCUMENT_KIND, + "order": { + "order_id": "ord_1", + "listing_addr": "30402:seller:listing", + "buyer_pubkey": "buyer", + "seller_pubkey": "seller", + "items": [ + { + "bin_id": "bin-1", + "bin_count": 1 + } + ] + }, + "buyer_actor": { + "account_id": "buyer-account", + "pubkey": "buyer", + "source": BUYER_ORDER_REQUEST_ACTOR_SOURCE_RESOLVED_ACCOUNT + } + } + }); + + validate_buyer_order_request_local_work_payload(&payload).expect("valid payload"); +} + +#[test] +fn buyer_order_request_payload_rejects_payment_required_documents() { + let payload = json!({ + "record_kind": BUYER_ORDER_REQUEST_LOCAL_WORK_RECORD_KIND, + "currentness": { + "current": true + }, + "no_payment": { + "payment_required": true, + "settlement_deferred": true + }, + "document": { + "kind": BUYER_ORDER_REQUEST_DOCUMENT_KIND, + "order": { + "order_id": "ord_1" + } + } + }); + + let error = + validate_buyer_order_request_local_work_payload(&payload).expect_err("invalid payload"); + + assert!(error.to_string().contains("payment_required")); +}