Some checks failed
CI / fmt (push) Successful in 1m10s
CI / clippy (push) Successful in 8m17s
CI / test (1.85) (push) Failing after 9m49s
CI / test (stable) (push) Failing after 17m18s
CI / deny (push) Failing after 1m51s
CI / sim-bench (stable) (push) Successful in 24m52s
CI / image-build (4 images) (push) Has been skipped
CI / smoke (all-in-one TLS sim call) (push) Has been skipped
CI / smoke (T2 compose four-service) (push) Has been skipped
CI / smoke (caddy reload during live call, zero drops) (push) Has been skipped
CI / twilio-live (manual only) (push) Has been skipped
deploy-epoch: deployment topology — one binary, three blessed shapes The full deploy-epoch integration: deployment topology spec + 4 implementation slices (69 files, +14771/-36). Adds Docker image build pipeline (4 named --target stages), container smoke CI (all-in-one TLS + compose + caddy reload-during-call), rustls Phase 1 in-process TLS, /metrics endpoint, ValkeyEventSink, engine hygiene fixes (TCP_NODELAY + TCP_QUICKACK-suppression + WS pings + trunk config), and the slice-F tag-push image publish workflow. ADR-0011 (deployment topology) Proposed. Pre-tag verification (PM-executed on integration tipc772485): - Seam gate intact (loop_driver + rtc_session hashes unchanged) - cargo fmt/clippy/test/doc all exit 0 - cargo deny bans+licenses FAILED pre-existing at main6340e63(CI authoritative via cargo-deny-action@v2 with cargo-deny 0.19.x) PRs merged into this commit: #23 (dev-d/G), #24 (dev-a/A), #25 (dev-c/C+D+E), #26 (dev-b/B+F) Integration PR: #27 Signed-off-by: Aaron D. Lee <himself@adlee.work> Co-authored-by: Aaron D. Lee <himself@adlee.work> Co-committed-by: Aaron D. Lee <himself@adlee.work>
70 lines
2.8 KiB
Rust
70 lines
2.8 KiB
Rust
//! App-level WS ping emission on the trunk media-stream WS (deploy
|
|
//! slice A §5.2).
|
|
//!
|
|
//! Spins the real router on an ephemeral listener, performs the Twilio
|
|
//! connected/start handshake as a tokio-tungstenite client, then asserts
|
|
//! an engine-originated Ping frame arrives well within CI slack of the
|
|
//! configured interval. Twilio documents no keepalive of its own — this
|
|
//! test is the contract that WE originate the pings.
|
|
|
|
use futures_util::{SinkExt, StreamExt};
|
|
use rutster_trunk::twilio_media_streams::{RegisterTrunkInboundChannel, TwilioMediaStreamsServer};
|
|
use tokio::sync::mpsc;
|
|
use tokio_tungstenite::tungstenite::Message;
|
|
|
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
|
async fn engine_originates_ws_ping_at_configured_interval() {
|
|
let (register_tx, mut register_rx) = mpsc::channel::<RegisterTrunkInboundChannel>(4);
|
|
let app = TwilioMediaStreamsServer::router(register_tx, std::time::Duration::from_millis(100));
|
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
let addr = listener.local_addr().unwrap();
|
|
tokio::spawn(async move {
|
|
axum::serve(listener, app).await.unwrap();
|
|
});
|
|
|
|
// Stub media thread: ack the registration and hold the channel ends
|
|
// alive so the pump loop stays up for the whole test.
|
|
let registered = tokio::spawn(async move {
|
|
let register = register_rx.recv().await.expect("pump registers");
|
|
let RegisterTrunkInboundChannel {
|
|
inbound_from_twilio_rx,
|
|
outbound_to_twilio_tx,
|
|
reply,
|
|
..
|
|
} = register;
|
|
let _ = reply.send(rutster_call_model::ChannelId::new());
|
|
(inbound_from_twilio_rx, outbound_to_twilio_tx)
|
|
});
|
|
|
|
let (mut ws, _resp) =
|
|
tokio_tungstenite::connect_async(format!("ws://{addr}/twilio/media-stream"))
|
|
.await
|
|
.expect("WS connect");
|
|
ws.send(Message::Text(
|
|
r#"{"event":"connected","protocol":"twilio-media-stream","version":"1.0.0"}"#.into(),
|
|
))
|
|
.await
|
|
.unwrap();
|
|
ws.send(Message::Text(
|
|
r#"{"event":"start","start":{"streamSid":"MZping","callSid":"CAping"}}"#.into(),
|
|
))
|
|
.await
|
|
.unwrap();
|
|
let _register = registered.await.unwrap();
|
|
|
|
// 100 ms interval → a Ping must land within 2 s (20 intervals of
|
|
// slack for a loaded CI runner; the point is emission, not cadence
|
|
// precision).
|
|
let got_ping = tokio::time::timeout(std::time::Duration::from_secs(2), async {
|
|
while let Some(msg) = ws.next().await {
|
|
if matches!(msg.expect("WS frame"), Message::Ping(_)) {
|
|
return true;
|
|
}
|
|
}
|
|
false
|
|
})
|
|
.await
|
|
.expect("a Ping frame must arrive before the deadline");
|
|
assert!(got_ping, "expected an engine-originated Ping frame");
|
|
}
|