use super::{Tool, encode, of_one, one, validate};
use serde_json::{Value, json};
fn bash() -> Tool {
Tool {
name: "Bash".to_owned(),
description: "run a command in a shell".to_owned(),
input_schema: json!({"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"]}),
subject_cwd: false,
}
}
#[test]
fn an_element_is_three_facts_and_the_schema_is_untouched() {
let said = one(&bash());
let o = said.as_object().expect("an object");
assert_eq!(o.len(), 3, "nothing else rides along: {said}");
assert_eq!(o["input_schema"], bash().input_schema);
assert_eq!(of_one(&said), Ok(bash()));
}
#[test]
fn a_set_encodes_as_its_array() {
let other = Tool {
name: "Read".to_owned(),
description: "read a file".to_owned(),
input_schema: json!({"type": "object"}),
subject_cwd: false,
};
let said = encode(&[bash(), other.clone()]);
let rows = said.as_array().expect("an array");
assert_eq!(rows.len(), 2);
assert_eq!(of_one(&rows[0]), Ok(bash()));
assert_eq!(of_one(&rows[1]), Ok(other));
}
#[test]
fn an_element_that_is_missing_something_refuses_by_key() {
assert_eq!(
of_one(&json!("Bash")),
Err("tool: not a JSON object".to_owned())
);
assert_eq!(
of_one(&json!({"description": "d", "input_schema": {}})),
Err("tool: missing field \"name\"".to_owned())
);
assert_eq!(
of_one(&json!({"name": "Bash", "input_schema": {}})),
Err("tool: missing field \"description\"".to_owned())
);
assert_eq!(
of_one(&json!({"name": "Bash", "description": "d"})),
Err("tool: missing field \"input_schema\"".to_owned())
);
assert_eq!(
of_one(&json!({"name": 7, "description": "d", "input_schema": {}})),
Err("tool: field \"name\" is not a string".to_owned())
);
}
#[test]
fn a_schema_is_carried_whatever_it_is() {
let odd = json!({"name": "Bash", "description": "d", "input_schema": "not a schema"});
assert_eq!(
of_one(&odd).map(|t| t.input_schema),
Ok(Value::String("not a schema".to_owned()))
);
}
#[test]
fn a_name_that_is_not_a_single_path_component_is_refused() {
for unusable in ["", ".", "..", "tools/Bash", "tools\\Bash", "Ba\0sh"] {
let named = Tool {
name: unusable.to_owned(),
..bash()
};
let refusal = validate(&[named]).expect_err("refused");
assert!(refusal.starts_with("unusable tool name"), "{refusal}");
}
}
#[test]
fn one_name_twice_in_one_set_declines_naming_it() {
let refusal = validate(&[bash(), bash()]).expect_err("refused");
assert_eq!(refusal, "duplicate tool name \"Bash\"");
}
#[test]
fn an_addressable_set_passes_and_so_does_an_empty_one() {
assert_eq!(validate(&[bash()]), Ok(()));
assert_eq!(validate(&[]), Ok(()));
}
#[test]
fn subject_cwd_rides_only_when_true_and_a_mistyped_one_refuses() {
let mut consenting = bash();
consenting.subject_cwd = true;
let spelled = one(&consenting);
assert_eq!(spelled.get("subject_cwd"), Some(&json!(true)));
assert_eq!(of_one(&spelled), Ok(consenting.clone()));
let plain = one(&bash());
assert!(plain.get("subject_cwd").is_none(), "absence is the default");
assert_eq!(of_one(&plain), Ok(bash()));
let mut mistyped = spelled;
mistyped["subject_cwd"] = json!("yes");
assert_eq!(
of_one(&mistyped),
Err("tool: field \"subject_cwd\" is not a boolean".to_owned())
);
}