use super::*;
use tokio::io::{AsyncReadExt as _, AsyncWriteExt as _};
async fn serve_health(body: &'static str, status_line: &'static str) -> u16 {
let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap();
let port = listener.local_addr().unwrap().port();
tokio::spawn(async move {
while let Ok((mut socket, _)) = listener.accept().await {
let mut request = [0; 1024];
let _ = socket.read(&mut request).await;
let _ = socket
.write_all(
format!(
"{status_line}\r\ncontent-type: application/json\r\ncontent-length: {}\r\nconnection: close\r\n\r\n{body}",
body.len()
)
.as_bytes(),
)
.await;
}
});
tokio::task::yield_now().await;
port
}
#[tokio::test]
async fn forcing_managed_skips_discovery() {
assert!(
discover_local_router(true).await.is_none(),
"--managed must not adopt a running router"
);
}
#[test]
fn a_closed_port_does_not_accept() {
assert!(!port_accepts(1));
}
#[tokio::test]
async fn a_listening_router_is_recognised() {
let port = serve_health(r#"{"status":"ok"}"#, "HTTP/1.1 200 OK").await;
assert!(port_accepts(port), "the test server is not listening");
assert!(
verify_health(&format!("http://127.0.0.1:{port}"))
.await
.is_ok(),
"a router answering /health must be recognised"
);
}
#[tokio::test]
async fn a_non_router_listener_is_rejected() {
let port = serve_health(r#"{"hello":"world"}"#, "HTTP/1.1 200 OK").await;
assert!(port_accepts(port), "the test server is not listening");
assert!(
verify_health(&format!("http://127.0.0.1:{port}"))
.await
.is_err(),
"a listener that is not a router must be rejected"
);
}
#[test]
fn the_default_port_is_always_a_candidate() {
assert!(
local_candidate_ports().contains(&DEFAULT_LOCAL_PORT),
"the documented default port must always be probed"
);
}
#[test]
fn candidate_ports_are_not_repeated() {
let ports = local_candidate_ports();
let mut unique = ports.clone();
unique.sort_unstable();
unique.dedup();
assert_eq!(ports.len(), unique.len(), "duplicate candidates: {ports:?}");
}
#[test]
fn port_zero_is_never_a_candidate() {
assert!(!local_candidate_ports().contains(&0));
}
#[tokio::test]
async fn discovery_stands_down_exactly_when_managed_state_exists() {
let has_managed_state = matches!(load_managed(), Ok(Some(_)));
let discovered = discover_local_router(false).await;
if has_managed_state {
assert!(
discovered.is_none(),
"a machine committed to a managed container must not adopt another listener"
);
}
}
#[test]
fn a_loopback_published_port_is_a_candidate() {
assert_eq!(
parse_published_ports("127.0.0.1:18878->8080/tcp"),
vec![18878]
);
}
#[test]
fn a_wildcard_published_port_is_a_candidate() {
assert_eq!(parse_published_ports("0.0.0.0:9000->8080/tcp"), vec![9000]);
assert_eq!(parse_published_ports("[::]:9100->8080/tcp"), vec![9100]);
}
#[test]
fn an_externally_bound_port_is_not_a_candidate() {
assert!(parse_published_ports("192.168.1.5:9000->8080/tcp").is_empty());
}
#[test]
fn an_unpublished_port_is_not_a_candidate() {
assert!(parse_published_ports("8080/tcp").is_empty());
}
#[test]
fn every_published_mapping_is_considered() {
let ports = parse_published_ports(
"127.0.0.1:18878->8080/tcp, 0.0.0.0:9000->9000/tcp\n127.0.0.1:7000->80/tcp",
);
assert_eq!(ports, vec![18878, 9000, 7000]);
}
#[test]
fn a_repeated_published_port_appears_once() {
assert_eq!(
parse_published_ports("127.0.0.1:8080->8080/tcp, 127.0.0.1:8080->9090/tcp"),
vec![8080]
);
}
#[test]
fn an_unparseable_listing_yields_no_candidates() {
assert!(parse_published_ports("").is_empty());
assert!(parse_published_ports(" ").is_empty());
assert!(parse_published_ports("nonsense").is_empty());
assert!(parse_published_ports("127.0.0.1:notaport->8080/tcp").is_empty());
assert!(parse_published_ports("127.0.0.1:0->8080/tcp").is_empty());
}
#[test]
fn published_ports_are_best_effort() {
let _ports: Vec<u16> = published_container_ports();
}
#[tokio::test]
async fn a_router_on_a_candidate_port_is_adopted_and_named() {
let port = serve_health(r#"{"status":"ok"}"#, "HTTP/1.1 200 OK").await;
let base_url = format!("http://127.0.0.1:{port}");
assert!(port_accepts(port));
assert!(verify_health(&base_url).await.is_ok());
let adopted = ResolvedServer::at(base_url.clone(), None, "already-running local server");
assert_eq!(adopted.source, "already-running local server");
assert_eq!(adopted.base_url, base_url);
}
#[tokio::test]
async fn the_effective_source_describes_what_the_next_command_will_use() {
let reported = effective_source().await.expect("a source is always known");
assert!(!reported.is_empty());
if let Some(url) = discover_local_router(false).await {
assert!(
reported.contains(&url),
"a discovered router must be named: {reported}"
);
assert!(reported.contains("already-running"), "{reported}");
}
}
#[tokio::test]
async fn a_discovered_server_carries_no_managed_lease() {
if let Some(server) = discovered_local_router().await {
assert_eq!(server.source, "already-running local server");
assert!(
server.base_url.starts_with("http://127.0.0.1:"),
"{}",
server.base_url
);
}
}
#[test]
fn a_mapping_without_a_host_address_is_not_a_candidate() {
assert!(parse_published_ports("->8080/tcp").is_empty());
assert!(parse_published_ports("nocolon->8080/tcp").is_empty());
}
#[test]
fn the_configured_router_port_leads_the_candidates() {
let candidates = local_candidate_ports();
if let Some(configured) = std::env::var("ROUTER_PORT")
.ok()
.and_then(|value| value.trim().parse::<u16>().ok())
.filter(|port| *port != 0)
{
assert_eq!(
candidates.first(),
Some(&configured),
"the configured port must be probed first: {candidates:?}"
);
} else {
assert_eq!(
candidates.first(),
Some(&DEFAULT_LOCAL_PORT),
"without ROUTER_PORT the documented default leads: {candidates:?}"
);
}
}