use super::*;
#[test]
fn test_find_apr_binary() {
let result = find_apr_binary();
if result.is_err() && std::env::var("CI").is_ok() {
return; }
assert!(result.is_ok(), "apr binary should be on PATH: {result:?}");
}
#[cfg(unix)]
fn write_marker_bin(path: &std::path::Path, marker: &str) {
use std::io::Write;
let mut f = std::fs::File::create(path).expect("create marker bin");
writeln!(f, "#!/bin/sh").expect("shebang");
writeln!(f, "echo {marker}").expect("body");
f.sync_all().expect("sync");
drop(f);
use std::os::unix::fs::PermissionsExt;
let mut perms = std::fs::metadata(path).expect("stat").permissions();
perms.set_mode(0o755);
std::fs::set_permissions(path, perms).expect("chmod");
}
#[test]
#[cfg(unix)]
fn falsify_2384_self_wins_over_path_lookup() {
let dir =
std::env::temp_dir().join(format!("aprender-orchestrate-2384-self-{}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(dir.join("self")).expect("mkdir self");
std::fs::create_dir_all(dir.join("path")).expect("mkdir path");
let self_apr = dir.join("self").join("apr");
let path_apr = dir.join("path").join("apr");
write_marker_bin(&self_apr, "SELF-0.63.0");
write_marker_bin(&path_apr, "PATH-0.60.0");
let resolved = resolve_apr_binary(None, Some(self_apr.clone()), || Some(path_apr.clone()))
.expect("resolution must succeed");
assert_eq!(
resolved,
self_apr,
"must launch the running executable, got {}",
resolved.display()
);
let out = std::process::Command::new(&resolved).output().expect("spawn resolved backend");
assert_eq!(
String::from_utf8_lossy(&out.stdout).trim(),
"SELF-0.63.0",
"the backend that runs must be the binary the user invoked"
);
}
#[test]
fn falsify_2384_resolution_preserves_the_directory() {
let installed = std::path::PathBuf::from("/opt/release-0.63.0/bin/apr");
let stale = std::path::PathBuf::from("/home/user/.local/bin/apr");
let resolved = resolve_apr_binary(None, Some(installed.clone()), || Some(stale.clone()))
.expect("resolution must succeed");
assert_eq!(resolved, installed);
assert_ne!(resolved, stale);
}
#[test]
fn falsify_2384_non_apr_host_falls_back_to_path() {
let harness = std::path::PathBuf::from("/w/target/debug/deps/batuta-1a2b3c4d");
let on_path = std::path::PathBuf::from("/usr/bin/apr");
assert_eq!(
resolve_apr_binary(None, Some(harness), || Some(on_path.clone())),
Some(on_path.clone())
);
assert_eq!(resolve_apr_binary(None, None, || Some(on_path.clone())), Some(on_path));
assert_eq!(resolve_apr_binary(None, None, || None), None);
}
#[test]
fn falsify_2384_similar_names_are_not_apr() {
let on_path = std::path::PathBuf::from("/usr/bin/apr");
for name in ["apr-cli", "aprender", "batuta", "aprx"] {
let exe = std::path::PathBuf::from("/usr/bin").join(name);
assert_eq!(
resolve_apr_binary(None, Some(exe), || Some(on_path.clone())),
Some(on_path.clone()),
"{name} must not be mistaken for the apr binary"
);
}
}
#[test]
fn falsify_2384_explicit_override_wins() {
let exe = std::path::PathBuf::from("/opt/bin/apr");
let on_path = std::path::PathBuf::from("/usr/bin/apr");
assert_eq!(
resolve_apr_binary(
Some(std::ffi::OsString::from("/mock/apr")),
Some(exe.clone()),
|| Some(on_path.clone())
),
Some(std::path::PathBuf::from("/mock/apr"))
);
assert_eq!(
resolve_apr_binary(Some(std::ffi::OsString::new()), Some(exe.clone()), || Some(on_path)),
Some(exe)
);
assert_eq!(APR_BIN_ENV, "APR_BIN");
}
#[test]
fn test_privacy_tier_is_sovereign() {
assert_eq!(PrivacyTier::Sovereign, PrivacyTier::Sovereign);
}
#[test]
fn falsify_ct_003_strips_closing_think_tag() {
assert_eq!(strip_thinking_blocks("</think>\n\n4"), "4");
}
#[test]
fn falsify_ct_003_strips_full_think_block() {
assert_eq!(strip_thinking_blocks("<think>reasoning here</think>answer"), "answer");
}
#[test]
fn falsify_ct_003_strips_repeated_closing_tags() {
let result = strip_thinking_blocks("</think></think></think>");
assert!(!result.contains("</think>"), "must strip all </think> tags");
}
#[test]
fn falsify_ct_003_preserves_clean_text() {
assert_eq!(strip_thinking_blocks("clean text"), "clean text");
}
#[test]
fn falsify_ct_003_strips_mixed_content() {
let result = strip_thinking_blocks("<think>x</think>y</think>z");
assert!(!result.contains("<think>"), "no <think> tags");
assert!(!result.contains("</think>"), "no </think> tags");
assert!(result.contains('y'), "content between tags preserved");
assert!(result.contains('z'), "trailing content preserved");
}
#[test]
fn falsify_ct_003_strips_multiline_think_block() {
let input = "<think>\nline1\nline2\n</think>\nAnswer: 42";
assert_eq!(strip_thinking_blocks(input), "Answer: 42");
}
#[test]
fn falsify_ct_003_handles_empty_input() {
assert_eq!(strip_thinking_blocks(""), "");
}
#[test]
fn falsify_ct_003_handles_only_think_tags() {
assert_eq!(strip_thinking_blocks("<think></think>"), "");
}
fn build_body_test(
model_name: &str,
request: &crate::agent::driver::CompletionRequest,
) -> serde_json::Value {
use crate::agent::driver::Message;
let mut messages = Vec::new();
if let Some(ref system) = request.system {
let compact_system = system
.find("\n\n## Available Tools")
.map(|i| &system[..i])
.unwrap_or(system)
.to_string();
messages.push(serde_json::json!({"role": "system", "content": compact_system}));
}
for msg in &request.messages {
match msg {
Message::User(text) => {
messages.push(serde_json::json!({"role": "user", "content": text}));
}
Message::Assistant(text) => {
messages.push(serde_json::json!({"role": "assistant", "content": text}));
}
Message::AssistantToolUse(call) => messages.push(serde_json::json!({
"role": "assistant",
"content": format!("<tool_call>\n{}\n</tool_call>",
serde_json::json!({"name": call.name, "input": call.input}))
})),
Message::ToolResult(result) => messages.push(serde_json::json!({
"role": "user",
"content": format!("<tool_result>\n{}\n</tool_result>", result.content)
})),
_ => {}
}
}
let max_tokens = request.max_tokens.min(1024);
serde_json::json!({
"model": model_name,
"messages": messages,
"max_tokens": max_tokens,
"temperature": request.temperature,
"stream": false
})
}
fn make_request(
system: Option<&str>,
messages: Vec<crate::agent::driver::Message>,
max_tokens: u32,
) -> crate::agent::driver::CompletionRequest {
crate::agent::driver::CompletionRequest {
model: "test".into(),
system: system.map(String::from),
messages,
max_tokens,
temperature: 0.0,
tools: vec![],
}
}
#[test]
fn falsify_http_001_body_valid_json() {
use crate::agent::driver::Message;
let req = make_request(Some("You are helpful."), vec![Message::User("Hello".into())], 256);
let body = build_body_test("qwen3-1.7b", &req);
assert!(body.is_object());
assert_eq!(body["model"], "qwen3-1.7b");
assert!(body["messages"].is_array());
assert_eq!(body["stream"], false);
assert!(body["max_tokens"].as_u64().unwrap() <= 1024);
}
#[test]
fn falsify_http_001_system_prompt_first() {
use crate::agent::driver::Message;
let req = make_request(Some("System prompt."), vec![Message::User("Hi".into())], 128);
let body = build_body_test("test", &req);
let msgs = body["messages"].as_array().unwrap();
assert_eq!(msgs[0]["role"], "system");
assert!(msgs[0]["content"].as_str().unwrap().contains("System prompt"));
assert_eq!(msgs[1]["role"], "user");
}
#[test]
fn falsify_http_001_max_tokens_cap() {
use crate::agent::driver::Message;
let req = make_request(None, vec![Message::User("Hi".into())], 4096);
let body = build_body_test("test", &req);
assert_eq!(body["max_tokens"].as_u64().unwrap(), 1024, "PMAT-170: capped at 1024");
}
#[test]
fn falsify_http_001_tool_call_format() {
use crate::agent::driver::{Message, ToolCall, ToolResultMsg};
let req = make_request(
None,
vec![
Message::User("List files".into()),
Message::AssistantToolUse(ToolCall {
id: "1".into(),
name: "glob".into(),
input: serde_json::json!({"pattern": "src/**/*.rs"}),
}),
Message::ToolResult(ToolResultMsg {
tool_use_id: "1".into(),
content: "src/main.rs".into(),
is_error: false,
}),
],
256,
);
let body = build_body_test("test", &req);
let msgs = body["messages"].as_array().unwrap();
assert!(msgs[1]["content"].as_str().unwrap().contains("<tool_call>"));
assert!(msgs[2]["content"].as_str().unwrap().contains("<tool_result>"));
}
#[cfg(target_os = "linux")]
#[test]
fn falsify_1712_pdeathsig_helper_installs_pre_exec() {
let mut cmd = std::process::Command::new("/bin/true");
super::configure_parent_death_signal(&mut cmd);
let mut child = cmd.spawn().expect("spawn /bin/true with pdeathsig");
let status = child.wait().expect("wait /bin/true");
assert!(status.success(), "/bin/true should exit 0 with PR_SET_PDEATHSIG set");
}
#[cfg(target_os = "linux")]
#[test]
fn falsify_1712_pdeathsig_actually_set_in_child() {
if std::process::Command::new("python3").arg("--version").output().is_err() {
eprintln!("skipping: python3 not available");
return;
}
let script = "import ctypes; libc=ctypes.CDLL('libc.so.6'); \
v=ctypes.c_int(0); libc.prctl(2, ctypes.byref(v)); \
print(v.value)";
let mut cmd = std::process::Command::new("python3");
cmd.arg("-c").arg(script).stdout(std::process::Stdio::piped());
super::configure_parent_death_signal(&mut cmd);
let output = cmd.output().expect("spawn python3");
assert!(output.status.success(), "python3 exited nonzero: {:?}", output.status);
let reported: i32 = String::from_utf8_lossy(&output.stdout)
.trim()
.parse()
.expect("python3 should print an integer");
assert_eq!(
reported,
libc::SIGTERM,
"FALSIFY-1712: child PR_GET_PDEATHSIG returned {reported}, expected SIGTERM (15). \
prctl(PR_SET_PDEATHSIG) did not stick across exec — orphan-reaping is broken."
);
}
#[test]
fn falsify_http_001_strips_verbose_keeps_compact() {
use crate::agent::driver::Message;
let sys = "Helpful.\n\n## Tools\n| t | u |\n\n## Available Tools\n{big json}";
let req = make_request(Some(sys), vec![Message::User("Hi".into())], 128);
let body = build_body_test("test", &req);
let c = body["messages"][0]["content"].as_str().unwrap();
assert!(c.contains("## Tools"), "compact table preserved (PMAT-176)");
assert!(!c.contains("## Available Tools"), "verbose section stripped");
}
use super::compute_ready_timeout_secs;
#[test]
fn ready_timeout_env_override_takes_precedence() {
assert_eq!(compute_ready_timeout_secs(Some(50_000_000_000), Some("5")), 5);
assert_eq!(compute_ready_timeout_secs(None, Some("120")), 120);
}
#[test]
fn ready_timeout_env_override_clamped_to_minimum() {
assert_eq!(compute_ready_timeout_secs(None, Some("0")), 1);
}
#[test]
fn ready_timeout_env_override_invalid_falls_through_to_default() {
assert_eq!(compute_ready_timeout_secs(None, Some("abc")), 30);
assert_eq!(compute_ready_timeout_secs(None, Some("")), 30);
}
#[test]
fn ready_timeout_small_model_keeps_baseline() {
assert_eq!(compute_ready_timeout_secs(Some(1 * 1024 * 1024 * 1024), None), 30);
assert_eq!(compute_ready_timeout_secs(Some(2 * 1024 * 1024 * 1024), None), 30);
}
#[test]
fn ready_timeout_scales_with_model_size() {
assert_eq!(compute_ready_timeout_secs(Some(4 * 1024 * 1024 * 1024), None), 34);
let qwen3_size = 18_u64 * 1024 * 1024 * 1024;
assert_eq!(compute_ready_timeout_secs(Some(qwen3_size), None), 62);
assert_eq!(compute_ready_timeout_secs(Some(30 * 1024 * 1024 * 1024), None), 87);
}
#[test]
fn ready_timeout_unknown_size_falls_back_to_baseline() {
assert_eq!(compute_ready_timeout_secs(None, None), 30);
}
#[test]
fn ready_timeout_env_override_works_when_size_unknown() {
assert_eq!(compute_ready_timeout_secs(None, Some("60")), 60);
}
#[test]
fn ready_timeout_qwen3_coder_30b_real_size() {
let actual_bytes = 18_556_689_568_u64;
let secs = compute_ready_timeout_secs(Some(actual_bytes), None);
assert!(
secs >= 50,
"Qwen3-Coder-30B (18.5 GB) must get >= 50s budget; got {secs}s — fix paiml/claude-code-parity-apr M260"
);
assert!(secs <= 90, "Default scaling must not exceed reasonable max; got {secs}s");
}