diff --git a/src/nostr/events.rs b/src/nostr/events.rs index 9558b38..7548dac 100644 --- a/src/nostr/events.rs +++ b/src/nostr/events.rs @@ -1764,10 +1764,16 @@ mod tests { let content = NotificationContentBuilder::new(&server_keys) .with_apns_token("deadbeef12345678deadbeef12345678deadbeef12345678deadbeef12345678") .build(); + // One hour beyond the skew tolerance, not +1s: `process()` re-reads + // `Timestamp::now()` (1-second granularity) when it validates freshness, + // so a whole-second wall-clock tick between here and that read would + // collapse a +1s margin onto the exact threshold (`>` becomes `==`) and + // spuriously accept the event under load. A large margin exercises the + // same "created_at exceeds now + skew" rejection path, drift-proof. let future_created_at = Timestamp::from_secs( Timestamp::now() .as_secs() - .saturating_add(DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS + 1), + .saturating_add(DEFAULT_MAX_NOTIFICATION_FUTURE_SKEW_SECS + 3600), ); let event = GiftWrapBuilder::new(server_keys.clone(), sender_keys) .build_with_created_at(&content, future_created_at) diff --git a/src/shutdown.rs b/src/shutdown.rs index 56ddb95..328c190 100644 --- a/src/shutdown.rs +++ b/src/shutdown.rs @@ -315,21 +315,31 @@ mod tests { #[tokio::test] async fn wait_for_signal_or_trigger_returns_on_internal_trigger() { + use std::future::Future; + use std::task::{Context, Waker}; + let handler = ShutdownHandler::new(); let trigger = handler.trigger_handle(); let wait = handler.wait_for_signal_or_trigger(); tokio::pin!(wait); - // No signal and no trigger yet: the wait must still be pending. - let pending = timeout(Duration::from_millis(10), &mut wait).await; - assert!(pending.is_err()); + // No signal and no trigger yet: the wait must still be pending. Poll + // once with a no-op waker instead of racing a wall-clock timeout — the + // first poll subscribes the receiver and registers the signal handlers, + // and with neither fired the future is deterministically `Pending`. + let mut cx = Context::from_waker(Waker::noop()); + assert!( + wait.as_mut().poll(&mut cx).is_pending(), + "wait must be pending before any signal or trigger" + ); trigger.trigger(); - let reason = timeout(Duration::from_secs(1), &mut wait) - .await - .expect("internal trigger must complete the shutdown wait"); + // The trigger set the watch to `true`; awaiting the same future now + // resolves deterministically (a hang here would be a real bug the outer + // test harness surfaces, not a timing flake). + let reason = wait.await; assert_eq!(reason, ShutdownReason::InternalTrigger); } @@ -382,6 +392,13 @@ mod tests { assert_eq!(signal, ShutdownSignal::Sigterm); } + #[test] + fn shutdown_signal_name_labels_each_variant() { + // These strings appear in the operator-facing shutdown logs. + assert_eq!(ShutdownSignal::CtrlC.name(), "Ctrl+C"); + assert_eq!(ShutdownSignal::Sigterm.name(), "SIGTERM"); + } + #[cfg(unix)] #[tokio::test] async fn wait_for_shutdown_signal_receives_sigterm() {