commit ac4f31fdddeefbe04447fa9867b6083d2213a81f
parent 043739cceb8045877ba05fab24b7282723d6bcd7
Author: triesap <tyson@radroots.org>
Date: Tue, 7 Jul 2026 09:01:06 +0000
outbox: expose preview outcome target completion
Diffstat:
1 file changed, 145 insertions(+), 0 deletions(-)
diff --git a/crates/outbox/src/store.rs b/crates/outbox/src/store.rs
@@ -745,6 +745,44 @@ impl RadrootsOutbox {
.await
}
+ pub async fn mark_delivery_target_deferred_until_implemented(
+ &self,
+ outbox_event_id: i64,
+ claim_token: &str,
+ delivery_target_id: i64,
+ message: &str,
+ attempted_at_ms: i64,
+ ) -> Result<(), RadrootsOutboxError> {
+ self.mark_delivery_target_status(
+ outbox_event_id,
+ claim_token,
+ delivery_target_id,
+ RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented,
+ Some(message),
+ attempted_at_ms,
+ )
+ .await
+ }
+
+ pub async fn mark_delivery_target_preview_unavailable(
+ &self,
+ outbox_event_id: i64,
+ claim_token: &str,
+ delivery_target_id: i64,
+ message: &str,
+ attempted_at_ms: i64,
+ ) -> Result<(), RadrootsOutboxError> {
+ self.mark_delivery_target_status(
+ outbox_event_id,
+ claim_token,
+ delivery_target_id,
+ RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable,
+ Some(message),
+ attempted_at_ms,
+ )
+ .await
+ }
+
pub async fn mark_delivery_target_skipped_policy_denied(
&self,
outbox_event_id: i64,
@@ -2482,6 +2520,113 @@ mod tests {
}
#[tokio::test]
+ async fn claimed_delivery_targets_can_complete_as_preview_outcomes() {
+ for (content, mark, expected_status, expected_plan_status) in [
+ (
+ "proxy deferred",
+ "deferred",
+ RadrootsOutboxDeliveryTargetStatus::DeferredUntilImplemented,
+ RadrootsOutboxDeliveryPlanStatus::DeferredUntilImplemented,
+ ),
+ (
+ "proxy preview unavailable",
+ "preview",
+ RadrootsOutboxDeliveryTargetStatus::PreviewUnavailable,
+ RadrootsOutboxDeliveryPlanStatus::PreviewUnavailable,
+ ),
+ ] {
+ let outbox = RadrootsOutbox::open_memory().await.expect("open");
+ let draft = post_draft(FIXTURE_ALICE_PUBLIC_KEY_HEX, content);
+ let signed_event =
+ radroots_nostr_sign_frozen_draft(&fixture_keys(), &draft).expect("signed event");
+ let receipt = outbox
+ .enqueue_signed_operation(RadrootsOutboxSignedOperationInput::new(
+ "publish_post",
+ draft,
+ signed_event,
+ RadrootsOutboxDeliveryPlanInput::new(
+ "transport.nostr.local",
+ 1,
+ RadrootsTransportSatisfactionPolicy::all_accepted(),
+ vec![nostr_target(NOSTR_PRIMARY_WSS)],
+ ),
+ true,
+ 1_007,
+ 1_000,
+ ))
+ .await
+ .expect("enqueue");
+ let claimed = outbox
+ .claim_next_ready_signed_event("publisher", "claim-a", 2_000, 1_000)
+ .await
+ .expect("claim")
+ .expect("claimed");
+ let target_id = claimed.delivery_targets[0].delivery_target_id;
+
+ match mark {
+ "deferred" => {
+ outbox
+ .mark_delivery_target_deferred_until_implemented(
+ receipt.outbox_event_id,
+ "claim-a",
+ target_id,
+ "transport deferred",
+ 1_100,
+ )
+ .await
+ .expect("deferred");
+ }
+ "preview" => {
+ outbox
+ .mark_delivery_target_preview_unavailable(
+ receipt.outbox_event_id,
+ "claim-a",
+ target_id,
+ "transport preview unavailable",
+ 1_100,
+ )
+ .await
+ .expect("preview unavailable");
+ }
+ _ => unreachable!(),
+ }
+
+ let state = outbox
+ .complete_publish_attempt(
+ receipt.outbox_event_id,
+ "claim-a",
+ "retryable",
+ "terminal",
+ 2_500,
+ 1_200,
+ )
+ .await
+ .expect("complete");
+ assert_eq!(state, RadrootsOutboxEventState::Signed);
+ let targets = outbox
+ .delivery_targets(receipt.outbox_event_id)
+ .await
+ .expect("targets");
+ assert_eq!(targets[0].status, expected_status);
+ assert_eq!(targets[0].completed_at_ms, Some(1_100));
+ assert_eq!(targets[0].attempt_count, 1);
+ let plans = outbox
+ .delivery_plans(receipt.outbox_event_id)
+ .await
+ .expect("plans");
+ assert_eq!(plans[0].status, expected_plan_status);
+ assert_eq!(
+ outbox
+ .status_summary(1_200)
+ .await
+ .expect("summary")
+ .ready_signed_events,
+ 0
+ );
+ }
+ }
+
+ #[tokio::test]
async fn local_signed_event_ingest_remains_idempotent_without_transport_observation() {
let outbox = RadrootsOutbox::open_memory().await.expect("open");
let event_store = RadrootsEventStore::open_memory()