lib

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

commit c0ba705ff19c5951ab5ed84dc0e2d4cf5af7c577
parent e86ac0f3532d0513a8f61ec05287bec2ac0fd5cc
Author: triesap <tyson@radroots.org>
Date:   Thu,  9 Jul 2026 01:20:04 +0000

tests: cover Hybrid product publish satisfaction

- add listing Hybrid publish coverage for default AllTargets
- add farm Hybrid publish coverage for default AllTargets
- assert Reticulum preview targets remain stored and unattempted
- verify focused listing and farm runtime regressions

Diffstat:
Mcrates/sdk/tests/farms_runtime.rs | 99+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mcrates/sdk/tests/listings_runtime.rs | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 192 insertions(+), 9 deletions(-)

diff --git a/crates/sdk/tests/farms_runtime.rs b/crates/sdk/tests/farms_runtime.rs @@ -11,17 +11,21 @@ use radroots_events::{ ids::RadrootsAddressableCoordinate, kinds::{KIND_FARM, KIND_PROFILE}, }; -use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState}; +use radroots_outbox::{ + RadrootsOutbox, RadrootsOutboxDeliveryPlanStatus, RadrootsOutboxDeliveryTargetStatus, + RadrootsOutboxEventState, +}; use radroots_sdk::{ FARM_PUBLISH_OPERATION_KIND, FarmEnqueuePublishRequest, FarmPreparePublishRequest, FarmPrivateLocationClearRequest, FarmPrivateLocationInput, FarmPrivateLocationLookupCandidate, FarmPrivateLocationLookupReceipt, FarmPrivateLocationReceipt, FarmPrivateLocationSetRequest, FarmPrivateLocationSetResult, FarmPrivateLocationUpsertRequest, Geocoder, - GeocoderLocalityQuery, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, + GeocoderLocalityQuery, HybridProfile, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, PushOutboxRequest, PushOutboxTargetOutcomeKind, RadrootsClient, RadrootsSdkError, RadrootsSdkErrorClass, RadrootsSdkGeoNamesErrorKind, RadrootsSdkRecoveryAction, - RadrootsSdkTimestamp, SdkExactLocation, SdkIdempotencyKey, SdkMutationState, SdkPublicLocality, - StorageStatusRequest, TargetPolicy, TargetSet, TransportProfile, + RadrootsSdkTimestamp, ReticulumPreviewProfile, SdkExactLocation, SdkIdempotencyKey, + SdkMutationState, SdkPublicLocality, StorageStatusRequest, TargetPolicy, TargetSet, + TransportProfile, }; use radroots_transport_nostr::RadrootsMockRelayPublishAdapter; use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; @@ -148,6 +152,21 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro (tempdir, sdk) } +async fn hybrid_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { + let tempdir = tempfile::tempdir().expect("tempdir"); + let sdk = RadrootsClient::builder() + .directory_storage(tempdir.path().join("sdk")) + .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) + .transport_profile(TransportProfile::hybrid(HybridProfile::new( + NostrProfile::new([RELAY], NostrRelayUrlPolicy::Public).expect("Nostr profile"), + ReticulumPreviewProfile::preview_unavailable(), + ))) + .build() + .await + .expect("sdk"); + (tempdir, sdk) +} + async fn fixture_geocoder(tempdir: &tempfile::TempDir) -> Geocoder { let path = tempdir.path().join("geonames-fixture.db"); let options = SqliteConnectOptions::new() @@ -827,6 +846,78 @@ async fn farm_enqueue_publish_pushes_queued_event_with_mock_relay_sync() { } #[tokio::test] +async fn farm_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum_preview() { + let (_tempdir, sdk) = hybrid_directory_sdk().await; + let enqueue_receipt = sdk + .farms() + .enqueue_publish_with_explicit_signer( + FarmEnqueuePublishRequest::new( + farmer_actor(), + farm(FARM_E_D_TAG, "Hybrid Farm"), + TargetPolicy::use_transport_profile(), + ), + &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); + let event = &push_receipt.events[0]; + assert_eq!(event.outbox_event_id, enqueue_receipt.outbox_event_id); + assert_eq!(event.final_state, PushOutboxEventState::Published); + assert_eq!(event.quorum, 1); + assert!(event.quorum_met); + assert_eq!(event.targets.len(), 1); + assert_eq!(event.targets[0].endpoint_uri, RELAY); + assert_eq!( + event.targets[0].outcome_kind, + PushOutboxTargetOutcomeKind::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); + let plans = outbox + .delivery_plans(enqueue_receipt.outbox_event_id) + .await + .expect("delivery plans"); + assert_eq!(plans.len(), 1); + assert_eq!(plans[0].required_success_count, 1); + assert_eq!(plans[0].status, RadrootsOutboxDeliveryPlanStatus::Complete); + let targets = outbox + .delivery_targets(enqueue_receipt.outbox_event_id) + .await + .expect("delivery targets"); + assert_eq!(targets.len(), 2); + assert!(targets.iter().any(|target| { + target.endpoint_uri.to_string() == RELAY + && target.status == RadrootsOutboxDeliveryTargetStatus::Accepted + })); + assert!(targets.iter().any(|target| { + target.endpoint_uri.to_string() == "reticulum:preview-unavailable" + && target.status == RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable + && target.attempt_count == 0 + })); +} + +#[tokio::test] async fn farm_enqueue_publish_reports_preflight_idempotency_conflict_without_mutation() { let (_tempdir, sdk) = directory_sdk().await; let first = FarmEnqueuePublishRequest::new( diff --git a/crates/sdk/tests/listings_runtime.rs b/crates/sdk/tests/listings_runtime.rs @@ -16,14 +16,19 @@ use radroots_events::{ kinds::KIND_LISTING, listing::{RadrootsListing, RadrootsListingBin, RadrootsListingProduct}, }; -use radroots_outbox::{RadrootsOutbox, RadrootsOutboxEventState}; +use radroots_outbox::{ + RadrootsOutbox, RadrootsOutboxDeliveryPlanStatus, RadrootsOutboxDeliveryTargetStatus, + RadrootsOutboxEventState, +}; use radroots_sdk::{ - LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, ListingPreparePublishRequest, - NostrProfile, NostrRelayUrlPolicy, RadrootsClient, RadrootsSdkError, RadrootsSdkRecoveryAction, - RadrootsSdkTimestamp, SdkIdempotencyKey, SdkMutationState, TargetPolicy, TargetSet, - TransportProfile, + HybridProfile, LISTING_PUBLISH_OPERATION_KIND, ListingEnqueuePublishRequest, + ListingPreparePublishRequest, NostrProfile, NostrRelayUrlPolicy, PushOutboxEventState, + PushOutboxRequest, PushOutboxTargetOutcomeKind, RadrootsClient, RadrootsSdkError, + RadrootsSdkRecoveryAction, RadrootsSdkTimestamp, ReticulumPreviewProfile, SdkIdempotencyKey, + SdkMutationState, TargetPolicy, TargetSet, TransportProfile, }; use radroots_trade::listing::RadrootsListingDraftDocumentV1; +use radroots_transport_nostr::RadrootsMockRelayPublishAdapter; #[path = "support/serializer_failure.rs"] mod serializer_failure; @@ -181,6 +186,21 @@ async fn directory_sdk_with_relays(relays: &[&str]) -> (tempfile::TempDir, Radro (tempdir, sdk) } +async fn hybrid_directory_sdk() -> (tempfile::TempDir, RadrootsClient) { + let tempdir = tempfile::tempdir().expect("tempdir"); + let sdk = RadrootsClient::builder() + .directory_storage(tempdir.path().join("sdk")) + .fixed_clock(RadrootsSdkTimestamp::from_unix_seconds(1_700_000_000)) + .transport_profile(TransportProfile::hybrid(HybridProfile::new( + NostrProfile::new([RELAY], NostrRelayUrlPolicy::Public).expect("Nostr profile"), + ReticulumPreviewProfile::preview_unavailable(), + ))) + .build() + .await + .expect("sdk"); + (tempdir, sdk) +} + #[tokio::test] async fn prepare_publish_is_side_effect_free() { let (_tempdir, sdk) = directory_sdk().await; @@ -788,3 +808,75 @@ async fn enqueue_publish_derives_order_independent_idempotency_key() { .collect::<Vec<_>>(); assert_eq!(relay_urls, vec![RELAY_B.to_owned(), RELAY.to_owned()]); } + +#[tokio::test] +async fn listing_hybrid_profile_publishes_after_nostr_success_and_retains_reticulum_preview() { + let (_tempdir, sdk) = hybrid_directory_sdk().await; + let enqueue_receipt = sdk + .listings() + .enqueue_publish_with_explicit_signer( + ListingEnqueuePublishRequest::new( + actor(), + listing(LISTING_G_D_TAG, "Hybrid Coffee"), + TargetPolicy::use_transport_profile(), + ), + &FixtureSigner::new(SELLER), + ) + .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); + let event = &push_receipt.events[0]; + assert_eq!(event.outbox_event_id, enqueue_receipt.outbox_event_id); + assert_eq!(event.final_state, PushOutboxEventState::Published); + assert_eq!(event.quorum, 1); + assert!(event.quorum_met); + assert_eq!(event.targets.len(), 1); + assert_eq!(event.targets[0].endpoint_uri, RELAY); + assert_eq!( + event.targets[0].outcome_kind, + PushOutboxTargetOutcomeKind::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); + let plans = outbox + .delivery_plans(enqueue_receipt.outbox_event_id) + .await + .expect("delivery plans"); + assert_eq!(plans.len(), 1); + assert_eq!(plans[0].required_success_count, 1); + assert_eq!(plans[0].status, RadrootsOutboxDeliveryPlanStatus::Complete); + let targets = outbox + .delivery_targets(enqueue_receipt.outbox_event_id) + .await + .expect("delivery targets"); + assert_eq!(targets.len(), 2); + assert!(targets.iter().any(|target| { + target.endpoint_uri.to_string() == RELAY + && target.status == RadrootsOutboxDeliveryTargetStatus::Accepted + })); + assert!(targets.iter().any(|target| { + target.endpoint_uri.to_string() == "reticulum:preview-unavailable" + && target.status == RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable + && target.attempt_count == 0 + })); +}