#[path = "support/api.rs"]
mod api_support;
use api_support::*;
use http_body_util::BodyExt;
const INSPECT_WEB: &str = r#"{
"Id": "abc123",
"Name": "/web",
"Config": {"Labels": {}},
"NetworkSettings": {"IPAddress": "172.17.0.2", "Networks": {}}
}"#;
fn lifecycle_routes() -> Vec<MockRoute> {
vec![
MockRoute {
method: "GET",
path: "/containers/json",
status: 200,
body: "[]",
},
MockRoute {
method: "GET",
path: "/containers/abc123/json",
status: 200,
body: INSPECT_WEB,
},
MockRoute {
method: "POST",
path: "/containers/abc123/start",
status: 204,
body: "",
},
MockRoute {
method: "POST",
path: "/containers/abc123/stop",
status: 204,
body: "",
},
MockRoute {
method: "POST",
path: "/containers/abc123/kill",
status: 204,
body: "",
},
MockRoute {
method: "DELETE",
path: "/containers/web",
status: 204,
body: "",
},
MockRoute {
method: "GET",
path: "/containers/nope/json",
status: 404,
body: r#"{"message":"No such container: nope"}"#,
},
]
}
async fn send(router: &axum::Router, method: &str, uri: &str) -> (StatusCode, bytes::Bytes) {
let request = Request::builder()
.method(method)
.uri(uri)
.body(Body::empty())
.unwrap();
let request = arcbox_docker::api::strip_api_version_prefix(request);
let response = router.clone().oneshot(request).await.unwrap();
let status = response.status();
let body = response.into_body().collect().await.unwrap().to_bytes();
(status, body)
}
#[tokio::test]
async fn ping_and_list_proxy_through() {
let (router, _runtime, _guest, _tmp) = router_with_mock_guest(lifecycle_routes()).await;
let (status, _) = send(&router, "GET", "/_ping").await;
assert_eq!(status, StatusCode::OK);
let (status, body) = send(&router, "GET", "/containers/json").await;
assert_eq!(status, StatusCode::OK);
let json: serde_json::Value = serde_json::from_slice(&body).unwrap();
assert_eq!(json.as_array().map(Vec::len), Some(0));
let (status, _) = send(&router, "GET", "/v1.47/containers/json").await;
assert_eq!(status, StatusCode::OK);
}
#[tokio::test]
async fn guest_error_status_passes_through() {
let (router, _runtime, _guest, _tmp) = router_with_mock_guest(lifecycle_routes()).await;
let (status, body) = send(&router, "GET", "/containers/nope/json").await;
assert_eq!(status, StatusCode::NOT_FOUND);
assert!(String::from_utf8_lossy(&body).contains("No such container"));
}
#[tokio::test]
async fn start_stop_cycle_manages_host_networking() {
let (router, runtime, _guest, _tmp) = router_with_mock_guest(lifecycle_routes()).await;
let (status, _) = send(&router, "POST", "/containers/abc123/start").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(runtime.registered_container_ids().await.contains("abc123"));
assert_eq!(
runtime.resolve_registered_container("web").await.as_deref(),
Some("abc123"),
"name alias must resolve after start"
);
let (status, _) = send(&router, "POST", "/containers/abc123/stop").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(
runtime.registered_container_ids().await.is_empty(),
"stop must tear down host networking"
);
assert_eq!(runtime.resolve_registered_container("web").await, None);
}
#[tokio::test]
async fn versioned_remove_by_name_runs_local_teardown() {
let (router, runtime, _guest, _tmp) = router_with_mock_guest(lifecycle_routes()).await;
let (status, _) = send(&router, "POST", "/containers/abc123/start").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(runtime.registered_container_ids().await.contains("abc123"));
let (status, _) = send(&router, "DELETE", "/v1.47/containers/web").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(
runtime.registered_container_ids().await.is_empty(),
"remove must run local host teardown, resolved from the name alias"
);
}
#[tokio::test]
async fn non_fatal_kill_keeps_host_networking() {
let (router, runtime, _guest, _tmp) = router_with_mock_guest(lifecycle_routes()).await;
let (status, _) = send(&router, "POST", "/containers/abc123/start").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(runtime.registered_container_ids().await.contains("abc123"));
let (status, _) = send(&router, "POST", "/containers/abc123/kill?signal=SIGHUP").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(
runtime.registered_container_ids().await.contains("abc123"),
"a non-fatal kill signal must not tear down host networking"
);
let (status, _) = send(&router, "POST", "/containers/abc123/kill").await;
assert_eq!(status, StatusCode::NO_CONTENT);
assert!(runtime.registered_container_ids().await.is_empty());
}