use std::path::PathBuf;
use std::process::Command;
fn tritium_bin() -> PathBuf {
PathBuf::from(env!("CARGO_BIN_EXE_tritium"))
}
fn tiny_fixture() -> PathBuf {
PathBuf::from(concat!(
env!("CARGO_MANIFEST_DIR"),
"/../tritium-format/tests/fixtures/bitnet_tiny.gguf"
))
}
fn write_tokens(name: &str, json: &str) -> PathBuf {
let mut p = std::env::temp_dir();
p.push(format!(
"tritium-cli-gen-{}-{}.json",
name,
std::process::id()
));
std::fs::write(&p, json).expect("write token file");
p
}
#[test]
fn generate_on_tiny_fixture_errors_cleanly() {
let tokens = write_tokens("tiny", "[1, 2, 3]");
let output = Command::new(tritium_bin())
.arg("generate")
.arg("--model")
.arg(tiny_fixture())
.arg("--tokens")
.arg(&tokens)
.arg("--max-new")
.arg("4")
.output()
.expect("spawn tritium");
let _ = std::fs::remove_file(&tokens);
assert!(
!output.status.success(),
"generate unexpectedly succeeded on the partial fixture"
);
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("failed to load model") || stderr.contains("missing tensor"),
"expected a clean load error, got: {stderr}"
);
assert!(
!stderr.contains("panicked"),
"must not panic, got: {stderr}"
);
}
#[test]
fn generate_rejects_missing_token_file() {
let output = Command::new(tritium_bin())
.arg("generate")
.arg("--model")
.arg(tiny_fixture())
.arg("--tokens")
.arg("/nonexistent/tokens.json")
.output()
.expect("spawn tritium");
assert!(!output.status.success());
let stderr = String::from_utf8_lossy(&output.stderr);
assert!(
stderr.contains("failed to read token file"),
"expected a token-file read error, got: {stderr}"
);
}
#[test]
fn generate_help_lists_flags() {
let output = Command::new(tritium_bin())
.arg("generate")
.arg("--help")
.output()
.expect("spawn tritium");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
for flag in ["--model", "--tokens", "--max-new", "--greedy", "--eos"] {
assert!(stdout.contains(flag), "help missing {flag}: {stdout}");
}
}
#[test]
fn generate_on_real_model_runs() {
if std::env::var_os("TRITIUM_RUN_SLOW").is_none() {
eprintln!("skipping real-model generate; set TRITIUM_RUN_SLOW=1 to enable");
return;
}
let Some(home) = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")) else {
eprintln!("skipping: no HOME/USERPROFILE in the environment");
return;
};
let model =
PathBuf::from(home).join(".cache/tritium-models/bitnet-2b4t-gguf/ggml-model-i2_s.gguf");
if !model.exists() {
eprintln!("skipping: real model not present at {}", model.display());
return;
}
let tokens = write_tokens("real", "[128000, 9906, 1917]");
let out = Command::new(tritium_bin())
.arg("generate")
.arg("--model")
.arg(&model)
.arg("--tokens")
.arg(&tokens)
.arg("--max-new")
.arg("4")
.output()
.expect("spawn tritium");
let _ = std::fs::remove_file(&tokens);
assert!(
out.status.success(),
"generate failed: {}",
String::from_utf8_lossy(&out.stderr)
);
let stdout = String::from_utf8_lossy(&out.stdout);
let first_line = stdout.lines().next().unwrap_or_default();
assert!(
first_line.starts_with('[') && first_line.ends_with(']'),
"first line should be a JSON array, got: {first_line}"
);
}