use super::*;
fn lease() -> Lease {
Lease {
pid: 4242,
port: 40001,
model: "Qwen/Qwen3.8-27B".into(),
recipe_id: "qwen/qwen3.8-27b".into(),
argv_sha256: "a".repeat(64),
binary_sha256: "b".repeat(64),
env_sha256: "e".repeat(64),
owner_pid: 1,
started_at: 0,
}
}
fn reported() -> ServeIdentity {
ServeIdentity {
argv_sha256: "a".repeat(64),
binary_sha256: "b".repeat(64),
env_sha256: "e".repeat(64),
pid: 4242,
}
}
fn want() -> Expected {
Expected {
argv_sha256: "a".repeat(64),
binary_sha256: "b".repeat(64),
env_sha256: "e".repeat(64),
}
}
#[test]
fn a_server_is_reused_only_when_every_digest_matches() {
let want = want();
assert_eq!(
mismatch(&lease(), &reported(), &want, "Qwen/Qwen3.8-27B"),
None
);
let mut r = reported();
r.pid = 4243;
assert!(
mismatch(&lease(), &r, &want, "Qwen/Qwen3.8-27B")
.unwrap()
.contains("pid 4243")
);
let mut r = reported();
r.binary_sha256 = "c".repeat(64);
assert!(
mismatch(&lease(), &r, &want, "Qwen/Qwen3.8-27B")
.unwrap()
.contains("another binary")
);
let hermetic = Expected {
argv_sha256: "d".repeat(64),
..want.clone()
};
assert!(
mismatch(&lease(), &reported(), &hermetic, "Qwen/Qwen3.8-27B")
.unwrap()
.contains("another rendering")
);
assert!(
mismatch(&lease(), &reported(), &want, "Qwen/Qwen3.6-35B")
.unwrap()
.contains("this run needs")
);
}
#[test]
fn a_server_under_another_serve_environment_is_refused() {
let want = want();
let mut r = reported();
r.env_sha256 = "f".repeat(64);
let why = mismatch(&lease(), &r, &want, "Qwen/Qwen3.8-27B").expect("refused");
assert!(why.contains("another METRALE_* serve environment"), "{why}");
assert!(why.contains("qwen/qwen3.8-27b"), "names the recipe: {why}");
assert!(
why.contains("env-only"),
"the message must say argv and binary matched, so a reader is not sent \
looking for a recipe difference that does not exist: {why}"
);
let mut r = reported();
r.env_sha256 = String::new();
let why = mismatch(&lease(), &r, &want, "Qwen/Qwen3.8-27B").expect("refused");
assert!(
why.contains("does not report its METRALE_* serve environment"),
"{why}"
);
assert!(!why.contains("env-only"), "{why}");
let mut stale = lease();
stale.env_sha256 = "0".repeat(64);
assert_eq!(
mismatch(&stale, &reported(), &want, "Qwen/Qwen3.8-27B"),
None
);
}
#[test]
fn the_lease_file_round_trips_and_a_bad_one_is_refused() {
let dir = std::env::temp_dir().join(format!("serve-lease-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let store = ArtifactStore::with_root(dir.clone());
assert_eq!(read(&store).unwrap(), None);
write(&store, &lease()).unwrap();
assert_eq!(read(&store).unwrap(), Some(lease()));
std::fs::write(lease_path(&store), "not json").unwrap();
assert!(read(&store).is_err());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
#[cfg(target_os = "linux")]
fn an_orphaned_lease_is_released_and_a_live_one_kept() {
let dir = std::env::temp_dir().join(format!("serve-lease-orphan-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let store = ArtifactStore::with_root(dir.clone());
let dead_server = Lease {
pid: 4_000_000_000 - 7,
owner_pid: 1,
..lease()
};
write(&store, &dead_server).unwrap();
assert_eq!(release_if_orphaned(&store).unwrap(), None);
assert!(lease_path(&store).exists());
let orphan = Lease {
owner_pid: 4_000_000_000 - 9,
..dead_server
};
write(&store, &orphan).unwrap();
assert_eq!(release_if_orphaned(&store).unwrap(), Some(orphan));
assert!(!lease_path(&store).exists());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn an_older_lease_file_reads_with_an_empty_env_digest() {
let old: Lease = serde_json::from_str(
r#"{"pid":1,"port":2,"model":"m","recipe_id":"r","argv_sha256":"a","binary_sha256":"b","owner_pid":3,"started_at":4}"#,
)
.unwrap();
assert_eq!(old.env_sha256, "");
let back: Lease = serde_json::from_str(&serde_json::to_string(&lease()).unwrap()).unwrap();
assert_eq!(back.env_sha256, "e".repeat(64));
}
#[test]
fn a_serve_that_dies_at_boot_reports_its_own_cause() {
let tail = "native FP8 dense residency: weights 23.42 GB ...\n\
Error: Failed to build model\n\n\
Caused by:\n No memory left for KV cache: total GPU = 121.7 GB, \
--gpu-memory-utilization 70% → budget 85.2 GB, but 69.9 GB already consumed + \
23.7 GB inference reserve = 93.7 GB committed.\n";
let msg = exited_before_serving("exit status: 1", "unsloth/Qwen3.8-27B-NVFP4", tail);
assert!(
msg.starts_with("the leased server exited (exit status: 1) before it began serving"),
"{msg}"
);
assert!(msg.contains("No memory left for KV cache"), "{msg}");
assert!(
msg.contains(" Error: Failed to build model"),
"the block is indented: {msg}"
);
assert!(
!msg.contains("native FP8 dense residency"),
"only the error block: {msg}"
);
let msg = exited_before_serving("signal: 9", "m", "loading shard 3/17\n");
assert!(msg.contains("carries no `Error:` block"), "{msg}");
assert!(!msg.contains("loading shard"), "{msg}");
}