use super::*;
#[test]
fn classify_phantom_rewrite_alias() {
let got = classify_phantom_reach("bash", &serde_json::json!({"command": "ls"}), "ok", true);
assert_eq!(
got,
Some(crate::PhantomResolution::Rewrite("run_command".into()))
);
}
#[test]
fn classify_phantom_correct_alias() {
let got = classify_phantom_reach(
"str_replace_editor",
&serde_json::json!({}),
"ignored",
false,
);
match got {
Some(crate::PhantomResolution::Correct(msg)) => {
assert!(msg.contains("edit_file"), "guidance names the tool: {msg}");
}
other => panic!("expected Correct, got {other:?}"),
}
}
#[test]
fn classify_phantom_unknown_name() {
let got = classify_phantom_reach(
"summon_kraken",
&serde_json::json!({}),
"unknown tool: summon_kraken",
false,
);
assert_eq!(got, Some(crate::PhantomResolution::Unknown));
}
#[test]
fn classify_phantom_plan_alias_is_correct() {
let got = classify_phantom_reach("make_plan", &serde_json::json!({}), "ignored", false);
match got {
Some(crate::PhantomResolution::Correct(msg)) => {
assert!(
msg.contains("update_plan"),
"guidance names the tool: {msg}"
);
}
other => panic!("expected Correct, got {other:?}"),
}
}
#[test]
fn classify_phantom_state_get_miss() {
let got = classify_phantom_reach(
"state_get",
&serde_json::json!({"key": "nope"}),
"no such key: nope",
true,
);
assert_eq!(
got,
Some(crate::PhantomResolution::RealToolMiss(
"state_get on an unset key".into()
))
);
}
#[test]
fn classify_phantom_recall_miss() {
let got = classify_phantom_reach(
"recall",
&serde_json::json!({"query": "zzz"}),
"no matches in past conversations for \"zzz\" — try different keywords",
true,
);
assert_eq!(
got,
Some(crate::PhantomResolution::RealToolMiss(
"recall returned no matches".into()
))
);
}
#[test]
fn classify_phantom_resume_reach_is_a_rewrite() {
let got = classify_phantom_reach("where_were_we", &serde_json::json!({}), "ignored", false);
assert_eq!(
got,
Some(crate::PhantomResolution::Rewrite("resume_context".into()))
);
}
#[test]
fn classify_phantom_real_success_is_none() {
let got = classify_phantom_reach(
"read_file",
&serde_json::json!({"path": "src/lib.rs"}),
"line 1\nline 2\n",
true,
);
assert_eq!(got, None);
}
#[test]
fn tool_search_is_a_real_tool_name() {
assert!(ALL_TOOL_NAMES.contains(&"tool_search"));
}
#[test]
fn discovery_verbs_alias_to_tool_search() {
for verb in [
"find_tool",
"search_tools",
"list_tools",
"which_tool",
"available_tools",
"what_tools",
"tools",
] {
match resolve_tool_alias(verb) {
Some(AliasOutcome::Rewrite(c)) => assert_eq!(c, "tool_search", "verb: {verb}"),
other => panic!(
"expected Rewrite(tool_search) for {verb}, got something else: {}",
other.is_some()
),
}
}
}
#[test]
fn tool_search_is_not_an_alias_of_itself() {
assert!(resolve_tool_alias("tool_search").is_none());
}
#[test]
fn classify_phantom_discovery_reach_is_a_rewrite() {
let got = classify_phantom_reach("find_tool", &serde_json::json!({}), "ignored", false);
assert_eq!(
got,
Some(crate::PhantomResolution::Rewrite("tool_search".into()))
);
}
#[test]
fn classify_phantom_tool_search_real_call_is_none() {
let got = classify_phantom_reach(
"tool_search",
&serde_json::json!({"query": "read"}),
"Tools matching \"read\":\n- read_file — Read a file",
true,
);
assert_eq!(got, None);
}
#[test]
fn tool_search_is_not_a_hallucination() {
assert!(!is_hallucination(
"tool_search",
&serde_json::json!({"query": "x"})
));
}
#[test]
fn hallucination_detection_coverage() {
assert!(is_hallucination(
"run_command",
&serde_json::json!({"command": "list_dir ."})
));
assert!(!is_hallucination(
"run_command",
&serde_json::json!({"command": "cargo test"})
));
assert!(is_hallucination(
"definitely_not_a_real_tool",
&serde_json::json!({})
));
assert!(!is_hallucination(
"my_server__some_tool",
&serde_json::json!({})
));
for t in [
"list_dir",
"read_file",
"write_file",
"edit_file",
"delete_file",
"use_skill",
"web_fetch",
"save_note",
"recall",
] {
assert!(!is_hallucination(t, &serde_json::json!({"path": "."})));
}
}