Files
ngit-grasp/tests/fixture_lifecycle.rs
T
DanConwayDev 550d2dd5dc test(lifecycle): bound each observable wait including its query
The merged wait helper checked elapsed time only after awaiting its
condition, so a stuck query could defeat the advertised deadline.
Apply Tokio's timeout to the whole polling future while keeping the
existing condition, polling interval and diagnostic message.

Add a paused-time regression with a permanently pending condition and
an outer guard. It fails if the helper lacks its own deadline, without
requiring wall-clock sleeps or hanging the test runner. Production
relay behavior and existing test assertions are unchanged.

Validation: the targeted regression, formatting and all-target Clippy
pass. Three complete workspace runs follow this commit.

Assisted-by: Codex (GPT-6)
2026-09-21 08:31:12 +00:00

74 lines
2.4 KiB
Rust

//! Accepted connections must not outlive the fixture that owns them.
mod common;
use std::time::Duration;
use common::{
censoring_proxy::CensoringProxy, flapping_relay::FlappingRelay,
neg_limiting_proxy::NegLimitingProxy, req_limiting_proxy::ReqLimitingProxy,
setup_drop_relay::SetupDropRelay, MockRelay,
};
use futures_util::StreamExt;
macro_rules! check_shutdown {
($fixture:expr) => {{
tokio::time::timeout(Duration::from_secs(10), async {
let fixture = $fixture;
let (mut connection, _) = tokio_tungstenite::connect_async(fixture.url())
.await
.expect("fixture must finish the WebSocket handshake");
fixture.stop().await;
while let Some(message) = connection.next().await {
if message.is_err() || message.unwrap().is_close() {
break;
}
}
})
.await
.expect("fixture stop must close accepted connections");
}};
}
#[tokio::test]
async fn censoring_proxy_closes_connections_on_stop() {
let backend = MockRelay::start().await;
check_shutdown!(CensoringProxy::start(backend.url()).await);
}
#[tokio::test]
async fn req_limiting_proxy_closes_connections_on_stop() {
let backend = MockRelay::start().await;
check_shutdown!(ReqLimitingProxy::start(backend.url(), 2).await);
}
#[tokio::test]
async fn neg_limiting_proxy_closes_connections_on_stop() {
let backend = MockRelay::start().await;
check_shutdown!(NegLimitingProxy::start(backend.url(), 2).await);
}
#[tokio::test]
async fn flapping_relay_closes_connections_on_stop() {
check_shutdown!(FlappingRelay::start().await);
}
#[tokio::test]
async fn setup_drop_relay_closes_connections_on_stop() {
check_shutdown!(SetupDropRelay::start().await);
}
#[tokio::test(start_paused = true)]
#[should_panic(expected = "stalled query did not hold within")]
async fn observable_wait_deadline_cancels_a_stalled_query() {
// The outer timer makes a missing inner deadline fail deterministically
// rather than hanging this regression. Paused time avoids a wall-clock wait.
tokio::time::timeout(
Duration::from_secs(20),
common::wait_for("stalled query", Duration::from_secs(10), || {
std::future::pending::<bool>()
}),
)
.await
.expect("wait helper failed to enforce its own deadline");
}