sdk

Radroots SDK and bindings
git clone https://radroots.dev/git/sdk.git
Log | Files | Refs | README

commit c2ee5ccab8eb19f8d996184a584997407f9e5bea
parent 4a4e7690529e855eeb5492e9cfa8757ec6827d2c
Author: triesap <tyson@radroots.org>
Date:   Thu, 18 Jun 2026 20:42:11 -0700

sdk: harden farm order runtime guards

Diffstat:
Mcrates/sdk/tests/farms_runtime.rs | 61++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcrates/sdk/tests/orders_runtime.rs | 62+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mcrates/sdk/tests/source_boundary.rs | 24+++++++++++++++++++++---
3 files changed, 142 insertions(+), 5 deletions(-)

diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs @@ -11,8 +11,10 @@ use radroots_events::{ kinds::{KIND_FARM, KIND_PROFILE}, }; use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState}; +use radroots_relay_transport::RadrootsMockRelayPublishAdapter; use radroots_sdk::{ - FARM_PUBLISH_OPERATION_KIND, FarmEnqueuePublishRequest, FarmPreparePublishRequest, RadrootsSdk, + FARM_PUBLISH_OPERATION_KIND, FarmEnqueuePublishRequest, FarmPreparePublishRequest, + PushOutboxEventState, PushOutboxRelayOutcomeKind, PushOutboxRequest, RadrootsSdk, RadrootsSdkError, RadrootsSdkPartialLocalMutationFailure, RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, SdkMutationState, SdkRelayTargetPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy, @@ -349,6 +351,63 @@ async fn farm_enqueue_publish_derives_order_independent_idempotency_key() { } #[tokio::test] +async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() { + let (_tempdir, sdk) = directory_sdk().await; + let enqueue_request = FarmEnqueuePublishRequest::new( + farmer_actor(), + farm(FARM_D_D_TAG, "Sync Farm"), + SdkRelayTargetPolicy::UseConfiguredRelays, + ) + .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public) + .expect("target relays"); + let enqueue_receipt = sdk + .farms() + .enqueue_publish(enqueue_request, &FixtureSigner::new(FARMER)) + .await + .expect("enqueue"); + let adapter = RadrootsMockRelayPublishAdapter::new(); + + let push_receipt = sdk + .sync() + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(1)) + .await + .expect("push"); + + assert_eq!(push_receipt.attempted_events, 1); + assert_eq!(push_receipt.published_events, 1); + assert_eq!(push_receipt.retryable_events, 0); + assert_eq!(push_receipt.terminal_events, 0); + assert_eq!(push_receipt.events.len(), 1); + let event = &push_receipt.events[0]; + assert_eq!(event.event_id, enqueue_receipt.signed_event_id); + assert_eq!(event.outbox_event_id, enqueue_receipt.outbox_event_id); + assert_eq!(event.final_state, PushOutboxEventState::Published); + assert_eq!(event.attempted_count, 1); + assert_eq!(event.accepted_count, 1); + assert_eq!(event.retryable_count, 0); + assert_eq!(event.terminal_count, 0); + assert_eq!(event.quorum, 1); + assert!(event.quorum_met); + assert_eq!(event.relays.len(), 1); + assert_eq!(event.relays[0].relay_url, RELAY); + assert_eq!( + event.relays[0].outcome_kind, + PushOutboxRelayOutcomeKind::Accepted + ); + assert_eq!(adapter.captured_raw_events().len(), 1); + + let outbox = RadrootsOutbox::open_file(&sdk.storage_paths().expect("paths").outbox_path) + .await + .expect("outbox"); + let stored = outbox + .get_event(enqueue_receipt.outbox_event_id) + .await + .expect("stored") + .expect("stored"); + assert_eq!(stored.state, RadrootsOutboxEventState::Published); +} + +#[tokio::test] async fn farm_enqueue_publish_reports_partial_local_mutation_after_outbox_conflict() { let (_tempdir, sdk) = directory_sdk().await; let first = FarmEnqueuePublishRequest::new( diff --git a/crates/sdk/tests/orders_runtime.rs b/crates/sdk/tests/orders_runtime.rs @@ -19,6 +19,7 @@ use radroots_nostr::prelude::{ radroots_nostr_build_event, radroots_nostr_sign_frozen_draft, }; use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState}; +use radroots_relay_transport::RadrootsMockRelayPublishAdapter; use radroots_sdk::protocol::events::RadrootsNostrEventPtr; use radroots_sdk::protocol::order::{ RadrootsListingAddress, RadrootsOrderDecision, RadrootsOrderDecisionOutcome, @@ -30,7 +31,8 @@ use radroots_sdk::protocol::wire::WireEventParts; use radroots_sdk::{ ORDER_STATUS_DEFAULT_LIMIT, ORDER_STATUS_MAX_LIMIT, ORDER_SUBMIT_OPERATION_KIND, OrderPaymentStateKind, OrderSettlementStateKind, OrderStatusKind, OrderStatusRequest, - OrderSubmitEnqueueRequest, OrderSubmitPrepareRequest, RadrootsSdk, RadrootsSdkError, + OrderSubmitEnqueueRequest, OrderSubmitPrepareRequest, PushOutboxEventState, + PushOutboxRelayOutcomeKind, PushOutboxRequest, RadrootsSdk, RadrootsSdkError, RadrootsSdkPartialLocalMutationFailure, RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, SdkMutationState, SdkOrderStatusIssue, SdkOrderStatusIssueKind, SdkOrderStatusSource, SdkRelayTargetPolicy, SdkRelayTargetSet, SdkRelayUrlPolicy, @@ -543,6 +545,64 @@ async fn order_submit_enqueue_derives_order_independent_idempotency_key() { } #[tokio::test] +async fn order_submit_enqueue_pushes_queued_event_with_mock_relay_sync() { + let (_tempdir, sdk, _store) = directory_sdk_and_store().await; + let enqueue_request = OrderSubmitEnqueueRequest::new( + buyer_actor(), + listing_event_ptr(), + order_request("order-submit-sync"), + SdkRelayTargetPolicy::UseConfiguredRelays, + ) + .try_with_target_relays([RELAY], SdkRelayUrlPolicy::Public) + .expect("target relays"); + let enqueue_receipt = sdk + .orders() + .enqueue_submit(enqueue_request, &FixtureSigner::new(BUYER_SECRET_KEY_HEX)) + .await + .expect("enqueue"); + let adapter = RadrootsMockRelayPublishAdapter::new(); + + let push_receipt = sdk + .sync() + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(1)) + .await + .expect("push"); + + assert_eq!(push_receipt.attempted_events, 1); + assert_eq!(push_receipt.published_events, 1); + assert_eq!(push_receipt.retryable_events, 0); + assert_eq!(push_receipt.terminal_events, 0); + assert_eq!(push_receipt.events.len(), 1); + let event = &push_receipt.events[0]; + assert_eq!(event.event_id, enqueue_receipt.signed_event_id); + assert_eq!(event.outbox_event_id, enqueue_receipt.outbox_event_id); + assert_eq!(event.final_state, PushOutboxEventState::Published); + assert_eq!(event.attempted_count, 1); + assert_eq!(event.accepted_count, 1); + assert_eq!(event.retryable_count, 0); + assert_eq!(event.terminal_count, 0); + assert_eq!(event.quorum, 1); + assert!(event.quorum_met); + assert_eq!(event.relays.len(), 1); + assert_eq!(event.relays[0].relay_url, RELAY); + assert_eq!( + event.relays[0].outcome_kind, + PushOutboxRelayOutcomeKind::Accepted + ); + assert_eq!(adapter.captured_raw_events().len(), 1); + + let outbox = RadrootsOutbox::open_file(&sdk.storage_paths().expect("paths").outbox_path) + .await + .expect("outbox"); + let stored = outbox + .get_event(enqueue_receipt.outbox_event_id) + .await + .expect("stored") + .expect("stored"); + assert_eq!(stored.state, RadrootsOutboxEventState::Published); +} + +#[tokio::test] async fn order_submit_enqueue_reports_partial_local_mutation_after_outbox_conflict() { let (_tempdir, sdk, _store) = directory_sdk_and_store().await; let first = OrderSubmitEnqueueRequest::new( diff --git a/crates/sdk/tests/source_boundary.rs b/crates/sdk/tests/source_boundary.rs @@ -70,10 +70,25 @@ fn sdk_manifest_does_not_depend_on_app_or_cli_crates() { #[test] fn farm_runtime_stays_on_product_runtime_boundary() { + product_runtime_file_stays_on_boundary("src/farms_runtime.rs"); +} + +#[test] +fn order_runtime_stays_on_product_runtime_boundary() { + product_runtime_file_stays_on_boundary("src/orders_runtime.rs"); +} + +#[test] +fn migrated_runtime_tests_stay_on_product_runtime_boundary() { + for file in ["tests/farms_runtime.rs", "tests/orders_runtime.rs"] { + product_runtime_file_stays_on_boundary(file); + } +} + +fn product_runtime_file_stays_on_boundary(relative_path: &str) { let source = read_source( Path::new(env!("CARGO_MANIFEST_DIR")) - .join("src") - .join("farms_runtime.rs") + .join(relative_path) .as_path(), ); @@ -84,10 +99,13 @@ fn farm_runtime_stays_on_product_runtime_boundary() { "radrootsd", "publish_with_identity", "publish_parts_via_relay", + "publish_listing_via_radrootsd", + "publish_order_request_via_radrootsd", + "publish_farm_via_radrootsd", ] { assert!( !source.contains(forbidden), - "farms_runtime.rs must not use legacy SDK client or transport concept `{forbidden}`" + "{relative_path} must not use legacy SDK client or transport concept `{forbidden}`" ); } }