use super::*;
#[test]
fn tool_contract_constants_preserve_provider_names_and_runtime_aliases() {
assert_eq!(
MVP_TOOL_CAPABILITIES
.iter()
.map(|tool| tool.canonical_name())
.collect::<Vec<_>>(),
vec![
contract::tool_name::READ,
contract::tool_name::VIEW_IMAGE,
contract::tool_name::BASH,
contract::tool_name::HASH_EDIT,
contract::tool_name::WRITE,
contract::tool_name::GREP,
contract::tool_name::FIND,
contract::tool_name::LIST_FILES,
contract::tool_name::SUBAGENTS,
contract::tool_name::WEB,
contract::tool_name::AST_GREP,
]
);
let definitions = mvp_tool_definitions_json();
let bash = definitions
.as_array()
.unwrap()
.iter()
.find(|definition| definition["name"] == contract::tool_name::BASH)
.unwrap();
assert!(
bash["parameters"]["properties"]
.get(contract::arg_key::COMMAND)
.is_some()
);
assert!(
bash["parameters"]["properties"]
.get(contract::arg_key::CMD)
.is_none()
);
assert_eq!(
ToolCapability::from_dispatch_name(contract::BASH_DISPATCH_ALIASES[0]),
Some(ToolCapability::Bash)
);
}
#[test]
fn cloned_runtimes_share_path_search_service() {
let (temp, runtime) = runtime();
fs::write(temp.path().join("lib.rs"), "").unwrap();
let cloned = runtime
.clone_for_cwd_with_subagent_depth(temp.path(), 1)
.unwrap();
let first = runtime.dispatch("find", json!({"query":"lib"}));
let second = cloned.dispatch("find", json!({"query":"lib"}));
assert!(first.success, "{}", first.content);
assert!(second.success, "{}", second.content);
assert!(Arc::ptr_eq(&runtime.path_search, &cloned.path_search));
}
#[test]
fn grep_args_preserve_pattern_bytes_and_reject_legacy_duplicates() {
let args = serde_json::from_value::<crate::tools::args::GrepArgs>(json!({
"patterns": [" needle "]
}))
.unwrap()
.validate()
.unwrap();
assert_eq!(args.patterns.unwrap(), vec![" needle ".to_string()]);
let both = serde_json::from_value::<crate::tools::args::GrepArgs>(json!({
"patterns": ["needle"],
"pattern": "needle"
}))
.unwrap()
.validate();
assert!(both.is_err());
}
#[test]
fn grep_args_reject_blank_control_and_excessive_patterns() {
let too_many = (0..=crate::tools::args::GREP_MAX_PATTERNS)
.map(|index| format!("pattern-{index}"))
.collect::<Vec<_>>();
let over_total = vec!["x".repeat(crate::tools::args::GREP_MAX_PATTERN_CHARS + 1); 8];
let over_bytes = "😀".repeat(257);
for value in [
json!({"patterns": []}),
json!({"patterns": [" "]}),
json!({"patterns": ["bell\u{7}"]}),
json!({"patterns": too_many}),
json!({"patterns": over_total}),
json!({"patterns": [over_bytes]}),
json!({"patterns": ["needle"], "limit": 0}),
json!({"patterns": ["needle"], "offset": crate::tools::args::GREP_MAX_OFFSET + 1}),
json!({"patterns": ["needle"], "context": crate::tools::args::GREP_MAX_CONTEXT + 1}),
] {
let result = serde_json::from_value::<crate::tools::args::GrepArgs>(value)
.unwrap()
.validate();
assert!(result.is_err(), "accepted invalid grep arguments");
}
}