use std::time::{Duration, Instant};
use microsandbox::{MicrosandboxError, Sandbox};
use test_utils::msb_test;
const IMAGE: &str = "mirror.gcr.io/library/alpine";
async fn cleanup(name: &str) {
if let Ok(mut h) = Sandbox::get(name).await {
let _ = h.kill().await;
let _ = h.remove().await;
}
}
#[msb_test]
async fn replace_with_live_handle_does_not_hang() {
let name = "replace-live-handle";
cleanup(name).await;
let sb1 = Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.replace()
.create()
.await
.expect("first create");
let started = Instant::now();
let result = tokio::time::timeout(
Duration::from_secs(120),
Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.replace()
.create(),
)
.await;
let elapsed = started.elapsed();
let sb2 = match result {
Err(_) => {
drop(sb1);
cleanup(name).await;
panic!(
"second .replace().create() did not return within 120s — bug regressed (was the 30s SIGTERM-poll path)"
);
}
Ok(Err(err)) => {
drop(sb1);
cleanup(name).await;
panic!("second .replace().create() failed: {err}");
}
Ok(Ok(sb2)) => sb2,
};
assert!(
elapsed < Duration::from_secs(60),
"second create took {elapsed:?}; expected sub-30s with SIGKILL escalation"
);
drop(sb2);
drop(sb1);
cleanup(name).await;
}
#[msb_test]
async fn create_without_replace_returns_typed_already_exists() {
let name = "replace-typed-error";
cleanup(name).await;
let sb1 = Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.replace()
.create()
.await
.expect("first create");
let result = Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.create()
.await;
let err = match result {
Ok(_) => {
drop(sb1);
cleanup(name).await;
panic!("second create without .replace() should error, got Ok");
}
Err(e) => e,
};
assert!(
matches!(err, MicrosandboxError::SandboxAlreadyExists(_)),
"expected SandboxAlreadyExists, got {err:?}"
);
drop(sb1);
cleanup(name).await;
}
#[msb_test]
async fn replace_with_grace_zero_succeeds() {
let name = "replace-with-grace-zero";
cleanup(name).await;
let sb1 = Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.replace()
.create()
.await
.expect("first create");
let result = tokio::time::timeout(
Duration::from_secs(120),
Sandbox::builder(name)
.image(IMAGE)
.cpus(1)
.memory(256)
.replace_with_grace(Duration::from_secs(0))
.create(),
)
.await
.expect("did not hang")
.expect("create");
drop(result);
drop(sb1);
cleanup(name).await;
}