From 6b067a9bf6edb21f069db815acd769cc95c71eba Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sat, 26 Sep 2026 10:27:54 -0700 Subject: [PATCH] test(auth): hold the paused clock while the outage re-checks are on the wire The outage test slept 1500ms on a paused clock while its re-check went through loopback HTTP. Under load the reactor's zero-timeout poll missed the request and auto-advance jumped past the sleep, failing "re-checks happened" about one run in eight. Hold auto-advance with a running blocking task while the re-checks are in flight, and nudge at two seconds: the second POST only goes out once the first 503 lands, so it proves the outage left the lease alone, and the expiry must still fire at the original deadline a second later. Co-Authored-By: Claude Opus 5.5 --- rs/moq-auth/src/client.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/rs/moq-auth/src/client.rs b/rs/moq-auth/src/client.rs index 8b8fc0359f..a89c6a37bb 100644 --- a/rs/moq-auth/src/client.rs +++ b/rs/moq-auth/src/client.rs @@ -347,6 +347,17 @@ mod tests { } } + /// Run `f` with the paused clock held still, so loopback I/O completes before any + /// lease timer can fire: Tokio never auto-advances while a blocking task runs. + async fn frozen(f: impl Future) -> T { + let (release, hold) = std::sync::mpsc::channel::<()>(); + let blocking = tokio::task::spawn_blocking(move || hold.recv().unwrap_err()); + let output = f.await; + drop(release); + blocking.await.unwrap(); + output + } + fn client(server: &MockServer) -> Client { Client::new(server.uri().parse().unwrap(), None).unwrap() } @@ -526,17 +537,29 @@ mod tests { .await; let consumer = client.connect(request()).await.unwrap(); - tokio::time::sleep(Duration::from_millis(1500)).await; - assert!(log.revalidates() >= 1, "re-checks happened"); + frozen(async { + // Re-check one second short of expiry. + tokio::time::advance(Duration::from_secs(2)).await; + consumer.revalidate(); + log.until(|log| log.iter().any(|r| r.event == Event::Revalidate)).await; + // A nudge now posts again only once that 503 lands, so a second re-check + // proves the outage left the lease alone. + consumer.revalidate(); + tokio::select! { + () = log.until(|log| log.iter().filter(|r| r.event == Event::Revalidate).count() >= 2) => {} + reason = consumer.closed() => panic!("the outage ended the lease: {reason:?}"), + } + }) + .await; assert_eq!( consumer.grant().publish, patterns(&["**"]), "the grant stands through the outage" ); - let reason = tokio::time::timeout(Duration::from_secs(5), consumer.closed()) + let reason = tokio::time::timeout(Duration::from_secs(2), consumer.closed()) .await - .expect("expired"); + .expect("the failed re-check left the deadline where it was"); assert_eq!(reason, Reason::Expired); assert!(matches!( log.end().await.event,