feat(client): expose probe send commitment through typed receipts

This commit is contained in:
Nils Andreas Svee committed 2026-10-04 19:14:06 +07:00
1 parent eebe83986a
commit a6ba335302
18 files changed
+322 -122

No files matched your search

@@ -72,14 +72,10 @@ async fn exercise_client_server() {
OpenOutcome::NoTestCompleted(_) => panic!("normal client unexpectedly ran no-test"),
}
assert!(matches!(
client.send_probe().await.unwrap().as_slice(),
[ClientEvent::EchoSent {
seq: 0,
remote,
..
}] if *remote == server_addr
));
let receipt = client.send_probe().await.unwrap();
assert_eq!(receipt.seq, 0);
assert_eq!(receipt.remote, server_addr);
assert!(client.has_pending_probes());
let events = client.recv().await.unwrap();
let [ClientEvent::EchoReply {
seq: 0,
@@ -97,6 +93,7 @@ async fn exercise_client_server() {
panic!("expected a measured reply, got {events:?}");
};
assert_eq!(*remote, server_addr);
assert_eq!(*sent_at, receipt.sent_at);
assert_eq!(rtt.raw, received_at.mono.duration_since(sent_at.mono));
// Verify the measurement endpoint through public reply metadata and real
// OS timestamps. This also covers the userspace fallback on other targets.
@@ -17,7 +17,7 @@ use tokio::runtime::{Builder, Runtime};
mod support;
use irtt_client::{AsyncClient, ClientConfig, ClientError, ClientEvent, OpenOutcome};
use irtt_client::{Client, RunMode};
use irtt_client::{Client, RunMode, SendProbeError};
use irtt_server::ServerConfig;
use std::{
io,
@@ -274,6 +274,11 @@ fn recv_before_open_fails_on_first_poll_without_socket_readiness() {
poll_recv_once(&mut client),
Poll::Ready(Err(ClientError::NotOpen))
));
let mut send = Box::pin(client.send_probe());
assert!(matches!(
poll_once(send.as_mut()),
Poll::Ready(Err(SendProbeError::NotCommitted(ClientError::NotOpen)))
));
});
}
@@ -376,6 +381,13 @@ fn recv_after_local_close_and_no_test_fails_on_first_poll() {
poll_recv_once(&mut client),
Poll::Ready(Err(ClientError::AlreadyClosed))
));
let mut send = Box::pin(client.send_probe());
assert!(matches!(
poll_once(send.as_mut()),
Poll::Ready(Err(SendProbeError::NotCommitted(
ClientError::AlreadyClosed
)))
));
});
assert_eq!(close_server.finish().len(), 2);
@@ -391,6 +403,13 @@ fn recv_after_local_close_and_no_test_fails_on_first_poll() {
poll_recv_once(&mut client),
Poll::Ready(Err(ClientError::AlreadyCompleted))
));
let mut send = Box::pin(client.send_probe());
assert!(matches!(
poll_once(send.as_mut()),
Poll::Ready(Err(SendProbeError::NotCommitted(
ClientError::AlreadyCompleted
)))
));
});
assert_eq!(no_test_server.finish().len(), 1);
}
@@ -556,7 +575,7 @@ fn blocking_and_async_roll_back_a_failed_open_and_accept_a_retry() {
assert!(!blocking.has_pending_probes());
let blocking_second = blocking.open().unwrap();
assert!(!blocking.has_pending_probes());
let blocking_sent = blocking.send_probe().unwrap();
let blocking_sent = [ClientEvent::from(blocking.send_probe().unwrap())];
let blocking_reply = blocking.recv_once().unwrap();
let blocking_close = blocking.close().unwrap();
assert_eq!(blocking_server.finish().len(), 4);
@@ -572,7 +591,7 @@ fn blocking_and_async_roll_back_a_failed_open_and_accept_a_retry() {
assert!(!client.has_pending_probes());
let second = client.open().await.unwrap();
assert!(!client.has_pending_probes());
let sent = client.send_probe().await.unwrap();
let sent = [ClientEvent::from(client.send_probe().await.unwrap())];
let reply = client.recv().await.unwrap();
let closed = client.close().await.unwrap();
(first, second, sent, reply, closed)
@@ -624,7 +643,7 @@ fn blocking_and_async_complete_every_caller_paced_probe() {
let mut blocking_sent = Vec::new();
let mut blocking_replies = Vec::new();
for _ in 0..PROBES {
blocking_sent.extend(blocking.send_probe().unwrap());
blocking_sent.push(ClientEvent::from(blocking.send_probe().unwrap()));
assert!(blocking.has_pending_probes());
assert!(blocking.next_probe_timeout_deadline().is_some());
blocking_replies.extend(blocking.recv_once().unwrap());
@@ -644,7 +663,7 @@ fn blocking_and_async_complete_every_caller_paced_probe() {
let mut sent = Vec::new();
let mut replies = Vec::new();
for _ in 0..PROBES {
sent.extend(client.send_probe().await.unwrap());
sent.push(ClientEvent::from(client.send_probe().await.unwrap()));
assert!(client.has_pending_probes());
assert!(client.next_probe_timeout_deadline().is_some());
replies.extend(client.recv().await.unwrap());
+5 -3
View File
@@ -109,7 +109,9 @@ fn hmac_close_success_sends_authenticated_close_and_closes_session() {
));
assert!(matches!(
client.send_probe(),
Err(ClientError::AlreadyClosed)
Err(irtt_client::SendProbeError::NotCommitted(
ClientError::AlreadyClosed
))
));
let observations = server.observations(2);
@@ -175,7 +177,7 @@ fn bad_hmac_echo_reply_is_rejected_without_echo_reply_event() {
assert_started(outcome, &params);
let sent = client.send_probe().unwrap();
assert_eq!(sent.len(), 1);
assert_eq!(sent.seq, 0);
let events = client.recv_once().unwrap();
assert!(matches!(
events.as_slice(),
@@ -292,7 +294,7 @@ fn backend_hmac_correct_key_succeeds() {
assert!(matches!(outcome, OpenOutcome::Started(_)));
let sent = client.send_probe().unwrap();
assert_eq!(sent.len(), 1);
assert_eq!(sent.seq, 0);
let events = client.recv_once().unwrap();
assert_eq!(events.len(), 1);
@@ -17,7 +17,7 @@ fn backend_basic_open_echo_close() {
assert!(matches!(outcome, irtt_client::OpenOutcome::Started(_)));
let sent = client.send_probe().unwrap();
assert_eq!(sent.len(), 1);
assert_eq!(sent.seq, 0);
let events = client.recv_once().unwrap();
assert_eq!(events.len(), 1);
@@ -243,8 +243,7 @@ where
.unwrap();
let negotiated = assert_started(client.open().unwrap());
let sent_events = client.send_probe().unwrap();
assert_eq!(sent_events.len(), 1);
let receipt = client.send_probe().unwrap();
let reply_events = client.recv_once().unwrap();
assert_eq!(reply_events.len(), 1);
@@ -254,7 +253,7 @@ where
OneProbeRun {
negotiated,
sent: sent_events.into_iter().next().unwrap(),
sent: receipt.into(),
reply: reply_events.into_iter().next().unwrap(),
observations,
}