sdk

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

commit 391dfcc065bdf6b21bb28b9d90b54d7fb7402f2e
parent 78a7efa47971856939798226fe443c2b3769c972
Author: triesap <tyson@radroots.org>
Date:   Mon, 15 Jun 2026 16:57:03 -0700

sdk: add product outbox push API

- rename the adapter-based sync method to push_outbox_with_adapter
- add product push_outbox for relay-runtime builds
- return a structured unsupported-feature error without relay-runtime
- cover configured-relay and adapter sync paths in runtime tests

Diffstat:
Mcrates/sdk/Cargo.toml | 7++++++-
Mcrates/sdk/examples/runtime_local.rs | 2+-
Mcrates/sdk/src/sync_runtime.rs | 31++++++++++++++++++++++++++++++-
Mcrates/sdk/tests/sync_runtime.rs | 55+++++++++++++++++++++++++++++++++++++++++++++++++------
4 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/crates/sdk/Cargo.toml b/crates/sdk/Cargo.toml @@ -60,7 +60,12 @@ runtime = [ "radroots_trade/event_store", ] local-signer = ["runtime", "radroots_authority/local_signer"] -relay-runtime = ["runtime", "radroots_relay_transport/client"] +relay-runtime = [ + "runtime", + "dep:radroots_nostr", + "radroots_nostr/client", + "radroots_relay_transport/client", +] [dependencies] radroots_authority = { workspace = true, optional = true, default-features = false } diff --git a/crates/sdk/examples/runtime_local.rs b/crates/sdk/examples/runtime_local.rs @@ -96,7 +96,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { .await?; let push = sdk .sync() - .push_outbox( + .push_outbox_with_adapter( &RadrootsMockRelayPublishAdapter::new(), PushOutboxRequest::new().with_limit(1), ) diff --git a/crates/sdk/src/sync_runtime.rs b/crates/sdk/src/sync_runtime.rs @@ -1,7 +1,11 @@ #[cfg(feature = "runtime")] use crate::{RadrootsSdkError, SyncClient}; +#[cfg(all(feature = "runtime", feature = "relay-runtime"))] +use radroots_nostr::prelude::RadrootsNostrClient; #[cfg(feature = "runtime")] use radroots_outbox::RadrootsOutboxEventState; +#[cfg(all(feature = "runtime", feature = "relay-runtime"))] +use radroots_relay_transport::RadrootsNostrClientPublishAdapter; #[cfg(feature = "runtime")] use radroots_relay_transport::{ RadrootsOutboxPublishPolicy, RadrootsRelayOutcomeKind, RadrootsRelayPublishAdapter, @@ -181,7 +185,32 @@ impl From<RadrootsRelayOutcomeKind> for PushOutboxRelayOutcomeKind { #[cfg(feature = "runtime")] impl<'sdk> SyncClient<'sdk> { - pub async fn push_outbox<A>( + pub async fn push_outbox( + &self, + request: PushOutboxRequest, + ) -> Result<PushOutboxReceipt, RadrootsSdkError> { + #[cfg(feature = "relay-runtime")] + { + if self.sdk.relay_urls().is_empty() { + return Err(RadrootsSdkError::InvalidRequest { + message: "sync push requires configured relay URLs".to_owned(), + }); + } + let adapter = + RadrootsNostrClientPublishAdapter::new(RadrootsNostrClient::new_signerless()); + self.push_outbox_with_adapter(&adapter, request).await + } + + #[cfg(not(feature = "relay-runtime"))] + { + let _ = request; + Err(RadrootsSdkError::RelayTransport { + message: "sync push requires the relay-runtime feature".to_owned(), + }) + } + } + + pub async fn push_outbox_with_adapter<A>( &self, adapter: &A, request: PushOutboxRequest, diff --git a/crates/sdk/tests/sync_runtime.rs b/crates/sdk/tests/sync_runtime.rs @@ -181,7 +181,7 @@ async fn push_outbox_empty_queue_returns_zero_counts() { let receipt = sdk .sync() - .push_outbox(&adapter, request) + .push_outbox_with_adapter(&adapter, request) .await .expect("push"); @@ -190,6 +190,49 @@ async fn push_outbox_empty_queue_returns_zero_counts() { assert!(adapter.captured_raw_events().is_empty()); } +#[cfg(not(feature = "relay-runtime"))] +#[tokio::test] +async fn product_push_outbox_without_relay_runtime_returns_structured_error() { + let (_tempdir, sdk) = directory_sdk(&[RELAY_A]).await; + + let error = sdk + .sync() + .push_outbox(PushOutboxRequest::new()) + .await + .expect_err("unsupported product push"); + + assert!(matches!(error, RadrootsSdkError::RelayTransport { .. })); +} + +#[cfg(feature = "relay-runtime")] +#[tokio::test] +async fn product_push_outbox_empty_queue_uses_configured_relays() { + let (_tempdir, sdk) = directory_sdk(&[RELAY_A]).await; + + let receipt = sdk + .sync() + .push_outbox(PushOutboxRequest::default()) + .await + .expect("product push"); + + assert_eq!(receipt.attempted_events, 0); + assert!(receipt.events.is_empty()); +} + +#[cfg(feature = "relay-runtime")] +#[tokio::test] +async fn product_push_outbox_requires_configured_relays() { + let (_tempdir, sdk) = directory_sdk(&[]).await; + + let error = sdk + .sync() + .push_outbox(PushOutboxRequest::new()) + .await + .expect_err("missing configured relays"); + + assert!(matches!(error, RadrootsSdkError::InvalidRequest { .. })); +} + #[tokio::test] async fn push_outbox_rejects_invalid_limits_before_claiming() { let (_tempdir, sdk) = directory_sdk(&[RELAY_A]).await; @@ -197,12 +240,12 @@ async fn push_outbox_rejects_invalid_limits_before_claiming() { let zero = sdk .sync() - .push_outbox(&adapter, PushOutboxRequest::new().with_limit(0)) + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(0)) .await .expect_err("zero limit"); let too_large = sdk .sync() - .push_outbox( + .push_outbox_with_adapter( &adapter, PushOutboxRequest::new().with_limit(PUSH_OUTBOX_MAX_LIMIT + 1), ) @@ -222,7 +265,7 @@ async fn push_outbox_publishes_signed_listing_and_marks_outbox_published() { let receipt = sdk .sync() - .push_outbox(&adapter, PushOutboxRequest::new().with_limit(1)) + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(1)) .await .expect("push"); @@ -284,7 +327,7 @@ async fn push_outbox_preserves_retryable_and_terminal_relay_outcomes() { let receipt = sdk .sync() - .push_outbox(&adapter, PushOutboxRequest::new().with_limit(1)) + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(1)) .await .expect("push"); @@ -353,7 +396,7 @@ async fn push_outbox_does_not_claim_unsigned_outbox_work() { let receipt = sdk .sync() - .push_outbox(&adapter, PushOutboxRequest::new().with_limit(1)) + .push_outbox_with_adapter(&adapter, PushOutboxRequest::new().with_limit(1)) .await .expect("push");