use assert_cmd::Command;
fn isolated_command(root: &std::path::Path) -> Command {
let home = root.join("home");
let data = root.join("data");
let cache = root.join("cache");
let work = root.join("work");
std::fs::create_dir_all(&home).unwrap();
std::fs::create_dir_all(&data).unwrap();
std::fs::create_dir_all(&cache).unwrap();
std::fs::create_dir_all(&work).unwrap();
let mut command = Command::cargo_bin("hf2q").unwrap();
command
.current_dir(work)
.env("HOME", home)
.env("XDG_DATA_HOME", data)
.env("XDG_CACHE_HOME", &cache)
.env("HF_HOME", cache.join("huggingface"))
.env("HF_HUB_OFFLINE", "1");
command
}
#[test]
fn serve_and_chat_list_are_identical_read_only_offline_inventory_commands() {
let root = tempfile::tempdir().unwrap();
let state_root = root.path().join("malformed-state");
std::fs::create_dir_all(&state_root).unwrap();
std::fs::write(state_root.join("config.toml"), b"this = [is not toml").unwrap();
let serve = isolated_command(root.path())
.arg("--state-root")
.arg(&state_root)
.args(["serve", "list"])
.output()
.unwrap();
let chat = isolated_command(root.path())
.arg("--state-root")
.arg(&state_root)
.args(["chat", "list"])
.output()
.unwrap();
assert!(
serve.status.success(),
"{}",
String::from_utf8_lossy(&serve.stderr)
);
assert!(
chat.status.success(),
"{}",
String::from_utf8_lossy(&chat.stderr)
);
assert_eq!(serve.stdout, chat.stdout);
assert_eq!(
String::from_utf8(serve.stdout).unwrap(),
"REPOSITORY\tREVISION\tQUANT\tORIGIN\tLAST_USED\tMMPROJ\tPATH\n"
);
assert!(!root.path().join("data/hf2q/models").exists());
}
#[test]
fn released_cli_parses_repository_quant_surfaces_without_network() {
let root = tempfile::tempdir().unwrap();
for arguments in [
vec!["serve", "owner/model:Q4_K_M", "--help"],
vec!["chat", "owner/model:Q4_K_M", "--help"],
vec!["convert", "owner/model:Q4_K_M", "--help"],
] {
isolated_command(root.path())
.args(arguments)
.assert()
.success();
}
}
#[test]
fn json_mode_failure_stderr_is_json_lines_only() {
let root = tempfile::tempdir().unwrap();
let output = isolated_command(root.path())
.env("HF2Q_DEBUG_TQ_RMS", "1")
.args(["--log-format", "json", "info", "--model"])
.arg(root.path().join("missing.gguf"))
.output()
.unwrap();
assert!(!output.status.success());
assert!(output.stdout.is_empty());
let stderr = String::from_utf8(output.stderr).unwrap();
assert!(!stderr.contains("Error:"), "{stderr}");
let lines = stderr.lines().collect::<Vec<_>>();
assert!(!lines.is_empty(), "JSON failure emitted no diagnostic");
for line in lines {
let value: serde_json::Value =
serde_json::from_str(line).unwrap_or_else(|error| panic!("{error}: {line}"));
assert!(value.is_object(), "{line}");
}
}
#[test]
fn json_terminal_failure_survives_rust_log_off_as_one_object() {
let root = tempfile::tempdir().unwrap();
let output = isolated_command(root.path())
.env("RUST_LOG", "off")
.args(["--log-format", "json", "info", "--model"])
.arg(root.path().join("missing.gguf"))
.output()
.unwrap();
assert!(!output.status.success());
assert!(output.stdout.is_empty());
let stderr = String::from_utf8(output.stderr).unwrap();
let lines = stderr.lines().collect::<Vec<_>>();
assert_eq!(lines.len(), 1, "{stderr}");
let value: serde_json::Value = serde_json::from_str(lines[0]).unwrap();
assert_eq!(value["level"], "ERROR");
assert_eq!(value["target"], "hf2q");
assert_eq!(value["fields"]["exit_code"], 3);
}
#[cfg(unix)]
#[test]
fn packed_server_adopts_the_inherited_listener_and_retains_port_authority() {
use std::io::{BufRead, BufReader};
use std::os::fd::AsRawFd;
use std::os::unix::process::CommandExt;
use std::process::Stdio;
use std::time::Duration;
let root = tempfile::tempdir().unwrap();
let listener = std::net::TcpListener::bind((std::net::Ipv4Addr::LOCALHOST, 0)).unwrap();
listener.set_nonblocking(true).unwrap();
let address = listener.local_addr().unwrap();
let listener_fd = listener.as_raw_fd();
let (parent_lifeline, child_lifeline) = std::os::unix::net::UnixStream::pair().unwrap();
parent_lifeline
.set_read_timeout(Some(Duration::from_secs(30)))
.unwrap();
let child_fd = child_lifeline.as_raw_fd();
let (_startup_progress, child_startup_progress) =
std::os::unix::net::UnixDatagram::pair().unwrap();
let startup_progress_fd = child_startup_progress.as_raw_fd();
let binary = assert_cmd::cargo::cargo_bin!("hf2q");
let mut command = std::process::Command::new(binary);
command
.arg("--state-root")
.arg(root.path().join("state"))
.args(["serve", "--host", "127.0.0.1", "--port"])
.arg(address.port().to_string())
.args([
"--quiet",
"--operator-ui",
"plain",
"--auth-token",
"secret",
])
.arg("--chat-parent-lifeline-fd")
.arg(child_fd.to_string())
.arg("--chat-startup-progress-fd")
.arg(startup_progress_fd.to_string())
.arg("--chat-owned-listener-fd")
.arg(listener_fd.to_string())
.env("HOME", root.path().join("home"))
.env("XDG_DATA_HOME", root.path().join("data"))
.env("XDG_CACHE_HOME", root.path().join("cache"))
.env("HF_HUB_OFFLINE", "1")
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::piped())
.process_group(0);
unsafe {
command.pre_exec(move || {
for fd in [child_fd, startup_progress_fd, listener_fd] {
let flags = libc::fcntl(fd, libc::F_GETFD);
if flags < 0 || libc::fcntl(fd, libc::F_SETFD, flags & !libc::FD_CLOEXEC) < 0 {
return Err(std::io::Error::last_os_error());
}
}
Ok(())
});
}
let mut child = command.spawn().unwrap();
drop(child_lifeline);
drop(child_startup_progress);
let mut ready = String::new();
BufReader::new(parent_lifeline.try_clone().unwrap())
.read_line(&mut ready)
.unwrap();
if ready != format!("HF2Q-L1:READY:{}\n", address.port()) {
let _ = child.kill();
let stderr = child
.stderr
.take()
.map(|mut stderr| {
let mut value = String::new();
std::io::Read::read_to_string(&mut stderr, &mut value).unwrap();
value
})
.unwrap_or_default();
panic!("unexpected READY frame {ready:?}; server stderr: {stderr}");
}
let response = reqwest::blocking::Client::builder()
.timeout(Duration::from_secs(5))
.build()
.unwrap()
.get(format!("http://127.0.0.1:{}/health", address.port()))
.bearer_auth("secret")
.send()
.unwrap();
assert!(response.status().is_success(), "{}", response.status());
child.kill().unwrap();
child.wait().unwrap();
let replacement = std::net::TcpListener::bind(address).unwrap_err();
assert_eq!(replacement.kind(), std::io::ErrorKind::AddrInUse);
drop(listener);
std::net::TcpListener::bind(address)
.expect("the endpoint port is released only after retained authority is dropped");
}