mod common;
use std::time::Duration;
use common::{MockBackend, Scratch, request, within};
use modelpipe::{
CloseReason, ConnectOptions, NetworkMetrics, PipeStatus, ServeOptions, Ticket, TokenPolicy,
};
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
const OK_BODY: &str = r#"{"object":"list","data":[]}"#;
async fn paired(
backend: &MockBackend,
auth: TokenPolicy,
) -> (modelpipe::ServeHandle, modelpipe::ConnectHandle, String) {
let mut serve_opts = ServeOptions::default();
serve_opts.auth = auth;
let serving = within(
"serve must bind",
Box::pin(modelpipe::serve(&backend.url, serve_opts)),
)
.await
.expect("serve");
let ticket = serving.ticket();
let connected = within(
"connect must bind its local port",
Box::pin(modelpipe::connect(&ticket, ConnectOptions::default())),
)
.await
.expect("connect");
within("the pairing must form", carrying(&connected)).await;
let url = connected.base_url();
(serving, connected, url)
}
async fn until(mut ready: impl FnMut() -> bool) {
while !ready() {
tokio::time::sleep(Duration::from_millis(10)).await;
}
}
async fn carrying(handle: &modelpipe::ConnectHandle) {
until(|| handle.status() != PipeStatus::Idle).await;
}
#[tokio::test]
async fn a_pairing_still_forms_with_discovery_and_port_mapping_off() {
let backend = MockBackend::json(200, OK_BODY).await;
let mut serve_opts = ServeOptions::default();
serve_opts.auth = TokenPolicy::Generate;
serve_opts.port_mapping = false;
serve_opts.discovery = false;
let serving = within(
"serve must bind without discovery",
Box::pin(modelpipe::serve(&backend.url, serve_opts)),
)
.await
.expect("serve");
let mut connect_opts = ConnectOptions::default();
connect_opts.port_mapping = false;
connect_opts.discovery = false;
let connected = within(
"connect must bind on the ticket's own paths",
Box::pin(modelpipe::connect(&serving.ticket(), connect_opts)),
)
.await
.expect("connect");
within("the pairing must form on those paths", carrying(&connected)).await;
let response = within(
"a request must cross the pipe",
request(&connected.base_url(), "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(response.starts_with("HTTP/1.1 200 OK"), "got: {response}");
connected.shutdown().await;
serving.shutdown().await;
}
async fn settles_on(handle: &modelpipe::ConnectHandle, wanted: PipeStatus) {
until(|| handle.status() == wanted).await;
}
fn bearer(handle: &modelpipe::ServeHandle) -> String {
format!("Bearer {}", handle.token().expect("a token is enforced"))
}
#[tokio::test]
async fn connect_binds_its_port_without_waiting_for_a_peer_that_is_not_there() {
let backend = MockBackend::json(200, OK_BODY).await;
let serving = within(
"serve",
Box::pin(modelpipe::serve(&backend.url, ServeOptions::default())),
)
.await
.expect("serve");
let ticket = serving.ticket();
serving.shutdown().await;
drop(serving);
let connected = tokio::time::timeout(
Duration::from_secs(5),
Box::pin(modelpipe::connect(&ticket, ConnectOptions::default())),
)
.await
.expect("connect must not wait on a dial that will not land")
.expect("binding the local port is all it has to do");
assert_eq!(
connected.status(),
PipeStatus::Idle,
"nobody has been reached, and the handle is what says so"
);
let authority = connected.local_addr().to_string();
within("the advertised port must accept", async {
tokio::net::TcpStream::connect(&authority)
.await
.expect("the local listener is up");
})
.await;
within(
"shutdown must not wait on the dial either",
connected.shutdown(),
)
.await;
}
#[tokio::test]
async fn a_request_crosses_the_pipe_and_the_response_comes_back() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let response = within(
"a request must cross the pipe",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(response.starts_with("HTTP/1.1 200 OK"), "got: {response}");
assert!(
response.contains(OK_BODY),
"the body must arrive: {response}"
);
assert_eq!(
backend.accepts(),
1,
"and the backend served it exactly once"
);
let sent = backend.received().await;
assert!(sent.contains("GET /v1/models"), "the path survives: {sent}");
assert!(
sent.contains(&format!(
"Host: {}",
backend.url.trim_start_matches("http://")
)),
"the Host names the backend: {sent}"
);
assert!(
sent.contains("Via: 1.1 modelpipe"),
"the backend is told the request came through the tunnel: {sent}"
);
let peer = sent
.lines()
.find_map(|line| line.strip_prefix("X-Modelpipe-Peer: "))
.expect("the backend is told which peer");
assert_eq!(peer.len(), 12, "a twelve-hex-character fingerprint: {peer}");
assert!(peer.chars().all(|c| c.is_ascii_hexdigit()), "{peer}");
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn the_base_url_is_something_a_client_can_actually_use() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
assert!(
url.starts_with("http://127.0.0.1:"),
"loopback by default: {url}"
);
assert!(url.ends_with("/v1"), "and the OpenAI base path: {url}");
assert_eq!(
connected.local_addr().to_string(),
url.trim_start_matches("http://").trim_end_matches("/v1"),
"the URL names the port actually bound"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn an_unauthorized_request_never_reaches_the_backend() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
for auth in [None, Some("Bearer wrong"), Some("Basic whatever")] {
let response = within("a refusal must arrive", request(&url, "/v1/models", auth))
.await
.expect("request");
assert!(
response.starts_with("HTTP/1.1 401"),
"{auth:?} must be refused: {response}"
);
}
assert_eq!(
backend.accepts(),
0,
"after three refused requests the backend was never contacted"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn serving_open_forwards_without_a_credential() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::InsecureNoAuth).await;
assert_eq!(serving.token(), None, "there is no token to report");
let response = within("must forward", request(&url, "/v1/models", None))
.await
.expect("request");
assert!(response.starts_with("HTTP/1.1 200"), "got: {response}");
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn rotating_the_token_leaves_the_ticket_and_the_live_pairing_intact() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let ticket_before = serving.ticket().to_string();
let old = bearer(&serving);
assert!(
within("first request", request(&url, "/v1/models", Some(&old)))
.await
.expect("request")
.starts_with("HTTP/1.1 200")
);
let fresh = serving.rotate_token();
assert_eq!(
serving.ticket().to_string(),
ticket_before,
"rotating a token must not disturb the ticket"
);
let refused = within("old credential", request(&url, "/v1/models", Some(&old)))
.await
.expect("request");
assert!(
refused.starts_with("HTTP/1.1 401"),
"the old token dies immediately: {refused}"
);
let accepted = within(
"new credential",
request(&url, "/v1/models", Some(&format!("Bearer {fresh}"))),
)
.await
.expect("request");
assert!(
accepted.starts_with("HTTP/1.1 200"),
"and the same pairing carries the new one: {accepted}"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_supplied_credential_can_be_replaced_in_place() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) =
paired(&backend, TokenPolicy::Supplied("first-key".to_owned())).await;
assert_eq!(serving.token().as_deref(), Some("first-key"));
serving
.set_token("second-key".to_owned())
.expect("a usable token is installed");
assert!(
within("old", request(&url, "/v1/models", Some("Bearer first-key")))
.await
.expect("request")
.starts_with("HTTP/1.1 401")
);
assert!(
within(
"new",
request(&url, "/v1/models", Some("Bearer second-key"))
)
.await
.expect("request")
.starts_with("HTTP/1.1 200")
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_grant_admits_one_request_through_a_live_pipe_and_then_none() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) =
paired(&backend, TokenPolicy::Supplied("the-real-key".to_owned())).await;
serving
.grant_once("483920".to_owned(), Duration::from_mins(2))
.expect("a presentable code is granted");
let first = within(
"the code admits once",
request(&url, "/v1/models", Some("Bearer 483920")),
)
.await
.expect("request");
assert!(first.starts_with("HTTP/1.1 200"), "got: {first}");
let second = within(
"the spent code is a wrong token",
request(&url, "/v1/models", Some("Bearer 483920")),
)
.await
.expect("request");
assert!(second.starts_with("HTTP/1.1 401"), "got: {second}");
let token = within(
"the real key still works",
request(&url, "/v1/models", Some("Bearer the-real-key")),
)
.await
.expect("request");
assert!(token.starts_with("HTTP/1.1 200"), "got: {token}");
assert_eq!(serving.token().as_deref(), Some("the-real-key"));
assert_eq!(
backend.accepts(),
2,
"the refusal never reached the backend"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn restarting_the_listener_mints_a_ticket_the_old_one_cannot_impersonate() {
let backend = MockBackend::json(200, OK_BODY).await;
let first = within(
"serve",
Box::pin(modelpipe::serve(&backend.url, ServeOptions::default())),
)
.await
.expect("serve");
let old_ticket = first.ticket().to_string();
first.shutdown().await;
drop(first);
let second = within(
"serve again",
Box::pin(modelpipe::serve(&backend.url, ServeOptions::default())),
)
.await
.expect("serve");
let new_ticket = second.ticket().to_string();
assert_ne!(
old_ticket, new_ticket,
"a restart must mint a different ticket"
);
let old: Ticket = old_ticket.parse().expect("the old ticket still parses");
let new: Ticket = new_ticket.parse().expect("parses");
assert_ne!(
old.fingerprint(),
new.fingerprint(),
"and a different identity, not merely different addresses"
);
second.shutdown().await;
}
#[tokio::test]
async fn a_streaming_response_arrives_as_it_is_produced() {
let backend =
MockBackend::streaming(&["data: one\n\n", "data: two\n\n", "data: [DONE]\n\n"]).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let started = std::time::Instant::now();
let response = within(
"the stream must complete",
request(&url, "/v1/chat/completions", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(response.contains("data: one"), "got: {response}");
assert!(response.contains("data: [DONE]"), "got: {response}");
assert!(
started.elapsed() >= Duration::from_millis(60),
"the backend paused between frames, so a response that arrived \
instantly would mean the frames were produced before being sent"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_shutdown_pipe_reports_closed_and_never_blocks_a_watcher() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
serving.shutdown().await;
assert_eq!(serving.status(), PipeStatus::Closed);
assert_eq!(
within(
"a closed pipe must not block a watcher",
serving.status_changed()
)
.await,
PipeStatus::Closed
);
connected.shutdown().await;
assert_eq!(connected.status(), PipeStatus::Closed);
}
#[tokio::test]
async fn a_connect_side_says_whether_it_is_still_trying_and_why_it_stopped() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
assert_eq!(
connected.close_reason(),
None,
"a live pipe has not closed, so there is nothing to explain"
);
serving.shutdown().await;
settles_on(&connected, PipeStatus::Idle).await;
assert_eq!(
connected.close_reason(),
None,
"a peer that went away has not closed this side, and it is still trying"
);
connected.shutdown().await;
assert_eq!(connected.status(), PipeStatus::Closed);
assert_eq!(
connected.close_reason(),
Some(CloseReason::Shutdown),
"and a close this caller asked for is named as theirs"
);
assert_eq!(
connected.close_reason().map(CloseReason::as_str),
Some("shutdown")
);
}
#[tokio::test]
async fn a_completed_shutdown_releases_the_local_port() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
let port = connected.local_addr();
connected.shutdown().await;
drop(connected);
tokio::net::TcpListener::bind(port)
.await
.expect("the port must be free the moment shutdown returns");
serving.shutdown().await;
}
#[tokio::test]
async fn shutting_down_twice_is_harmless() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
serving.shutdown().await;
within("the second call must not hang", serving.shutdown()).await;
connected.shutdown().await;
within("nor on the connect side", connected.shutdown()).await;
}
#[tokio::test]
async fn a_serve_shutdown_lets_an_admitted_request_finish() {
let backend = MockBackend::streaming(&[
"data: one\n\n",
"data: two\n\n",
"data: three\n\n",
"data: [DONE]\n\n",
])
.await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let auth = bearer(&serving);
let authority = url
.trim_start_matches("http://")
.trim_end_matches("/v1")
.to_owned();
let reading = tokio::spawn(async move {
use tokio::io::{AsyncReadExt, AsyncWriteExt};
let mut socket = tokio::net::TcpStream::connect(&authority)
.await
.expect("connect");
socket
.write_all(
format!(
"GET /v1/chat/completions HTTP/1.1\r\nHost: x\r\n\
Authorization: {auth}\r\n\r\n"
)
.as_bytes(),
)
.await
.expect("write");
let mut seen = Vec::new();
socket.read_to_end(&mut seen).await.expect("read");
String::from_utf8_lossy(&seen).into_owned()
});
tokio::time::sleep(Duration::from_millis(40)).await;
within("the drain must not hang", serving.shutdown()).await;
let body = within("the admitted request must complete", reading)
.await
.expect("reader");
assert!(
body.contains("data: [DONE]"),
"shutdown promises the drain, so an admitted request runs to \
completion; the client got: {body}"
);
connected.shutdown().await;
}
#[tokio::test]
async fn an_idle_local_connection_does_not_wedge_the_connect_side_drain() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let authority = url
.trim_start_matches("http://")
.trim_end_matches("/v1")
.to_owned();
let _idle = tokio::net::TcpStream::connect(&authority)
.await
.expect("an SDK preconnect");
tokio::time::sleep(Duration::from_millis(50)).await;
within(
"one silent connection must not hold the drain open",
connected.shutdown(),
)
.await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_connect_shutdown_timeout_releases_the_port_and_leaves_the_latch_honest() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
let port = connected.local_addr();
let drained = within(
"nothing is in flight, so the drain must succeed",
connected.shutdown_timeout(Duration::from_secs(5)),
)
.await;
assert!(drained, "there was nothing to wait for");
tokio::net::TcpListener::bind(port)
.await
.expect("the port must be free the moment shutdown_timeout returns");
within("a second call must not hang", connected.shutdown()).await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_connect_shutdown_is_announced_rather_than_left_to_the_idle_timeout() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
within(
"a request must cross the pipe",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert_eq!(serving.peers().len(), 1, "the peer is registered");
connected.shutdown().await;
let noticed = tokio::time::timeout(Duration::from_secs(2), async {
while !serving.peers().is_empty() {
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await;
assert!(
noticed.is_ok(),
"the serve side was never told, and still lists {:?}",
serving.peers()
);
serving.shutdown().await;
}
#[tokio::test]
async fn a_live_pairing_reports_a_transport_path_on_both_sides() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
within(
"a request must cross the pipe",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
for (side, status) in [("serve", serving.status()), ("connect", connected.status())] {
assert!(
matches!(status, PipeStatus::Direct | PipeStatus::Relayed),
"the {side} side is carrying traffic and reports {status:?}"
);
}
let peers = serving.peers();
assert_eq!(peers.len(), 1, "one connect side is paired: {peers:?}");
assert_eq!(
peers[0].path,
serving.status(),
"one peer: the aggregate is it"
);
assert_eq!(peers[0].fingerprint.len(), 12);
assert!(peers[0].fingerprint.chars().all(|c| c.is_ascii_hexdigit()));
connected.shutdown().await;
within("the peer leaves the set", async {
while !serving.peers().is_empty() {
tokio::time::sleep(Duration::from_millis(10)).await;
}
})
.await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_client_that_disconnects_mid_stream_stops_the_backend() {
let (backend, frames_written) = MockBackend::endless_stream().await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let authority = url
.trim_start_matches("http://")
.trim_end_matches("/v1")
.to_owned();
let auth = bearer(&serving);
{
let mut socket = tokio::net::TcpStream::connect(&authority)
.await
.expect("connect");
let request = format!(
"GET /v1/chat/completions HTTP/1.1\r\nHost: {authority}\r\n\
Authorization: {auth}\r\n\r\n"
);
tokio::io::AsyncWriteExt::write_all(&mut socket, request.as_bytes())
.await
.expect("write");
let mut seen = vec![0u8; 64];
within(
"the stream must start",
tokio::io::AsyncReadExt::read(&mut socket, &mut seen),
)
.await
.expect("read");
}
tokio::time::sleep(Duration::from_millis(300)).await;
let after_disconnect = frames_written.load(std::sync::atomic::Ordering::SeqCst);
tokio::time::sleep(Duration::from_millis(300)).await;
let later = frames_written.load(std::sync::atomic::Ordering::SeqCst);
assert_eq!(
later,
after_disconnect,
"the backend produced {} more frames after the client left; a \
cancelled request must not leave a generation running",
later - after_disconnect
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_response_tells_the_client_not_to_reuse_the_connection() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let response = within(
"a request must cross the pipe",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(
response.to_ascii_lowercase().contains("connection: close"),
"a pooling client will otherwise send its next request down a \
stream nobody is reading: {response}"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_listener_restarted_with_a_stored_identity_keeps_its_ticket() {
let backend = MockBackend::json(200, OK_BODY).await;
let scratch = Scratch::new("identity");
let key = scratch.join("key");
let mut first = ServeOptions::default();
first.identity = Some(key.clone());
let before = within(
"serve must bind",
Box::pin(modelpipe::serve(&backend.url, first)),
)
.await
.expect("serve");
let ticket_before = before.ticket();
before.shutdown().await;
let mut second = ServeOptions::default();
second.identity = Some(key.clone());
let after = within(
"the restarted listener must bind",
Box::pin(modelpipe::serve(&backend.url, second)),
)
.await
.expect("serve");
let ticket_after = after.ticket();
assert_eq!(
ticket_before.fingerprint(),
ticket_after.fingerprint(),
"a stored identity is what makes a ticket outlive the process"
);
after.shutdown().await;
}
#[tokio::test]
async fn a_listener_restarted_without_one_is_a_different_peer_as_before() {
let backend = MockBackend::json(200, OK_BODY).await;
let before = within(
"serve must bind",
Box::pin(modelpipe::serve(&backend.url, ServeOptions::default())),
)
.await
.expect("serve");
let ticket_before = before.ticket();
before.shutdown().await;
let after = within(
"serve must bind again",
Box::pin(modelpipe::serve(&backend.url, ServeOptions::default())),
)
.await
.expect("serve");
assert_ne!(
ticket_before.fingerprint(),
after.ticket().fingerprint(),
"the default stays ephemeral, which is the revocation the README sells"
);
after.shutdown().await;
}
#[tokio::test]
async fn an_unusable_identity_refuses_to_serve_at_all() {
let backend = MockBackend::json(200, OK_BODY).await;
let scratch = Scratch::new("bad-identity");
let key = scratch.join("key");
std::fs::write(&key, "not a key\n").expect("write");
let mut opts = ServeOptions::default();
opts.identity = Some(key);
let refused = within(
"serve must refuse rather than hang",
Box::pin(modelpipe::serve(&backend.url, opts)),
)
.await;
let Err(refused) = refused else {
panic!("an unusable identity must not start a listener");
};
assert!(!refused.is_retryable(), "the operator named this path");
assert_eq!(backend.accepts(), 0, "and nothing was served");
}
#[tokio::test]
async fn a_connect_side_whose_peer_goes_away_reports_idle_rather_than_pretending() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
assert!(
matches!(connected.status(), PipeStatus::Direct | PipeStatus::Relayed),
"a live pairing reports the path it is using: {:?}",
connected.status()
);
serving.shutdown().await;
within(
"the connect side must notice its peer has gone",
settles_on(&connected, PipeStatus::Idle),
)
.await;
assert_ne!(
connected.status(),
PipeStatus::Closed,
"and this side is still up and still looking, not gone"
);
let refused = within(
"a request with no peer must still be answered",
request(&url, "/v1/models", Some("Bearer whatever")),
)
.await
.expect("request");
assert!(refused.starts_with("HTTP/1.1 502"), "got: {refused}");
assert!(
refused.contains(r#""code":"tunnel_unavailable""#),
"the connect side must say the tunnel is down, not blame a backend: {refused}"
);
assert!(
!refused.contains("backend"),
"there is no backend in this failure: {refused}"
);
connected.shutdown().await;
}
#[tokio::test]
async fn a_refused_rotation_reports_it_and_leaves_the_previous_credential_in_force() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) =
paired(&backend, TokenPolicy::Supplied("the-only-key".to_owned())).await;
for blank in ["", " ", "\t\n"] {
assert!(
serving.set_token(blank.to_owned()).is_err(),
"{blank:?} is a credential no conforming client could ever send"
);
}
assert_eq!(
serving.token().as_deref(),
Some("the-only-key"),
"the handle still reports what it is actually enforcing"
);
assert!(
within(
"the key the operator may now believe is dead",
request(&url, "/v1/models", Some("Bearer the-only-key")),
)
.await
.expect("request")
.starts_with("HTTP/1.1 200"),
"and it is still the key that works"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn an_aborted_upload_does_not_wedge_the_serve_side_drain() {
let backend = MockBackend::reads_whole_body(
"HTTP/1.1 200 OK\r\nContent-Length: 2\r\nConnection: close\r\n\r\n{}",
)
.await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let authority = url
.trim_start_matches("http://")
.trim_end_matches("/v1")
.to_owned();
let mut socket = tokio::net::TcpStream::connect(&authority)
.await
.expect("a client");
let request = format!(
"POST /v1/chat/completions HTTP/1.1\r\nHost: {authority}\r\n\
Authorization: {}\r\nContent-Length: 1000\r\n\r\n{{\"model\":\"",
bearer(&serving)
);
socket
.write_all(request.as_bytes())
.await
.expect("the head and a tenth of the body");
socket.flush().await.expect("flush");
socket.shutdown().await.expect("half-close");
let reader = tokio::spawn(async move {
let mut seen = Vec::new();
let _ = socket.read_to_end(&mut seen).await;
String::from_utf8_lossy(&seen).into_owned()
});
tokio::time::sleep(Duration::from_millis(50)).await;
within(
"an aborted upload must not hold the serve-side drain open",
serving.shutdown(),
)
.await;
let seen = reader.await.expect("the reader task");
assert!(
seen.starts_with("HTTP/1.1 400"),
"and the client is told, rather than left with an empty stream: {seen}"
);
connected.shutdown().await;
}
#[tokio::test]
async fn a_transition_that_lands_before_the_next_wait_is_reported_rather_than_lost() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
let rendered = connected.status();
assert!(
matches!(rendered, PipeStatus::Direct | PipeStatus::Relayed),
"a live pairing reports the path it is using: {rendered:?}"
);
serving.shutdown().await;
within(
"the connect side must notice its peer has gone",
settles_on(&connected, PipeStatus::Idle),
)
.await;
assert!(
tokio::time::timeout(Duration::from_millis(200), connected.status_changed())
.await
.is_err(),
"status_changed snapshots at the call, so the transition is already behind it"
);
let seen = within(
"a caller holding its own snapshot must be told what it missed",
connected.status_changed_since(rendered),
)
.await;
assert_eq!(
seen,
Some(PipeStatus::Idle),
"the transition happened, and this is the form that reports it"
);
connected.shutdown().await;
}
#[tokio::test]
async fn the_serve_side_reports_a_transition_that_lands_before_its_next_wait_too() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
within(
"the serve side must see the peer it is carrying",
until(|| serving.status() != PipeStatus::Idle),
)
.await;
let rendered = serving.status();
assert!(
matches!(rendered, PipeStatus::Direct | PipeStatus::Relayed),
"a live pairing reports the path it is carrying: {rendered:?}"
);
connected.shutdown().await;
within(
"the serve side must notice its last peer has gone",
until(|| serving.status() == PipeStatus::Idle),
)
.await;
assert!(
tokio::time::timeout(Duration::from_millis(200), serving.status_changed())
.await
.is_err(),
"status_changed snapshots at the call, so the transition is already behind it"
);
let seen = within(
"a caller holding its own snapshot must be told what it missed",
serving.status_changed_since(rendered),
)
.await;
assert_eq!(
seen,
Some(PipeStatus::Idle),
"the peer left while nobody waited, and this is the form that reports it"
);
serving.shutdown().await;
}
#[tokio::test]
async fn a_watcher_carrying_its_last_value_forward_ends_when_the_pipe_does() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, _url) = paired(&backend, TokenPolicy::Generate).await;
let serving = std::sync::Arc::new(serving);
let connected = std::sync::Arc::new(connected);
macro_rules! watch {
($handle:expr) => {{
let handle = $handle.clone();
tokio::spawn(async move {
let mut turns = 0;
let mut held = handle.status();
let mut last = held;
while let Some(next) = handle.status_changed_since(held).await {
last = next;
held = next;
turns += 1;
assert!(turns <= 10, "the sequence has to end rather than repeat");
}
last
})
}};
}
let serve_watch = watch!(serving);
let connect_watch = watch!(connected);
tokio::time::sleep(Duration::from_millis(50)).await;
connected.shutdown().await;
serving.shutdown().await;
assert_eq!(
within(
"the connect side's watcher must end with its pipe",
connect_watch
)
.await
.expect("the watcher must not panic"),
PipeStatus::Closed,
"the last value a watcher sees is the terminal one"
);
assert_eq!(
within(
"the serve side's watcher must end with its pipe",
serve_watch
)
.await
.expect("the watcher must not panic"),
PipeStatus::Closed
);
}
#[tokio::test]
async fn telling_both_sides_the_network_moved_leaves_the_pipe_carrying() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
within(
"the serve side must accept a network-change notice",
serving.notify_network_change(),
)
.await;
within(
"and so must the connect side",
connected.notify_network_change(),
)
.await;
let response = within(
"a request must still cross the pipe afterwards",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(response.starts_with("HTTP/1.1 200 OK"), "got: {response}");
assert!(
!matches!(connected.status(), PipeStatus::Closed),
"and the pipe is still up, not closed under the notice"
);
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn both_sides_report_their_own_transport_counters() {
let backend = MockBackend::json(200, OK_BODY).await;
let (serving, connected, url) = paired(&backend, TokenPolicy::Generate).await;
let response = within(
"a request must cross the pipe first, so the counters describe a used pipe",
request(&url, "/v1/models", Some(&bearer(&serving))),
)
.await
.expect("request");
assert!(response.starts_with("HTTP/1.1 200 OK"), "got: {response}");
for (side, metrics) in [
("serve", serving.network_metrics()),
("connect", connected.network_metrics()),
] {
assert_eq!(
metrics.relay_connections_ratelimited, 0,
"nothing throttled the {side} side, and it must not say otherwise: {metrics:?}"
);
let held = metrics;
assert_eq!(held, metrics);
}
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn each_side_counts_the_relay_connection_its_own_endpoint_could_not_make() {
let backend = MockBackend::json(200, OK_BODY).await;
let fake_relay = MockBackend::json(200, OK_BODY).await;
let mut serve_opts = ServeOptions::default();
serve_opts.auth = TokenPolicy::Generate;
serve_opts.relay = Some(fake_relay.url.clone());
serve_opts.port_mapping = false;
serve_opts.discovery = false;
let serving = within(
"serve must bind against a relay that is not one",
Box::pin(modelpipe::serve(&backend.url, serve_opts)),
)
.await
.expect("a relay that does not behave like one is not a startup error");
let mut connect_opts = ConnectOptions::default();
connect_opts.relay = Some(fake_relay.url.clone());
connect_opts.port_mapping = false;
connect_opts.discovery = false;
let connected = within(
"connect must bind against the same one",
Box::pin(modelpipe::connect(&serving.ticket(), connect_opts)),
)
.await
.expect("connect");
within(
"the pairing must still form, on the ticket's direct addresses",
carrying(&connected),
)
.await;
within(
"the serve side must count the relay connection its endpoint could not make",
until(|| serving.network_metrics().relay_connections_failed > 0),
)
.await;
within(
"and the connect side must count its own",
until(|| connected.network_metrics().relay_connections_failed > 0),
)
.await;
for (side, metrics) in [
("serve", serving.network_metrics()),
("connect", connected.network_metrics()),
] {
assert_eq!(
metrics.relay_connections, 0,
"the {side} side reached no relay, and must not count one: {metrics:?}"
);
assert_eq!(
metrics.relay_connections_ratelimited, 0,
"a relay that never completed a handshake cannot have throttled \
the {side} side: {metrics:?}"
);
}
let mut nowhere = ServeOptions::default();
nowhere.relay = Some("https://127.0.0.1:1/".to_owned());
nowhere.port_mapping = false;
nowhere.discovery = false;
let elsewhere = within(
"a listener must bind against a relay that is not there",
Box::pin(modelpipe::serve(&backend.url, nowhere)),
)
.await
.expect("a relay that does not answer is not a startup error");
assert_eq!(
elsewhere.network_metrics(),
NetworkMetrics::default(),
"an endpoint that dialled nothing has nothing to report, whatever the \
pipe beside it has been counting: {:?}",
elsewhere.network_metrics()
);
elsewhere.shutdown().await;
connected.shutdown().await;
serving.shutdown().await;
}
#[tokio::test]
async fn a_live_ticket_says_which_paths_it_carries() {
let backend = MockBackend::json(200, OK_BODY).await;
let mut serve_opts = ServeOptions::default();
serve_opts.auth = TokenPolicy::Generate;
serve_opts.wait_online = Some(Duration::from_secs(20));
let serving = within(
"serve must bind",
Box::pin(modelpipe::serve(&backend.url, serve_opts)),
)
.await
.expect("serve");
let ticket = serving.ticket();
assert!(
!ticket.relay_urls().is_empty(),
"a listener that waited to come online carries the relay it reached: {ticket:?}"
);
for url in ticket.relay_urls() {
assert!(
url.starts_with("http"),
"a relay body is a URL, handed back as written: {url}"
);
}
assert!(
!ticket.direct_addrs().is_empty(),
"and the local paths beside it: {ticket:?}"
);
let printed: Ticket = ticket.to_string().parse().expect("its own string parses");
assert_eq!(printed.relay_urls(), ticket.relay_urls());
assert_eq!(printed.direct_addrs(), ticket.direct_addrs());
serving.shutdown().await;
}