use serde::{Deserialize, Serialize};
use subako_function_sdk::{Output, ToolError, execute};
#[derive(Deserialize)]
struct Greet {
name: String,
}
#[derive(Serialize)]
struct Greeting {
text: String,
}
fn route(tool: &str, input: subako_function_sdk::Input) -> Result<Output, ToolError> {
match tool {
"greet" => {
let greet: Greet = input.parse()?;
Output::json(&Greeting {
text: format!("hello {}", greet.name),
})
}
other => Err(ToolError::unknown_tool(other)),
}
}
fn args(tool: &str) -> Vec<String> {
vec!["pkg".to_string(), tool.to_string()]
}
#[test]
fn a_known_tool_writes_its_output_and_exits_zero() {
let (stdout, code) = execute(&args("greet"), br#"{"name":"ada"}"#, route);
assert_eq!(code, 0);
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&stdout).unwrap(),
serde_json::json!({"text": "hello ada"})
);
}
#[test]
fn an_unknown_tool_is_a_tool_error() {
let (stdout, code) = execute(&args("nope"), b"{}", route);
assert_eq!(code, 1);
assert_eq!(
serde_json::from_slice::<serde_json::Value>(&stdout).unwrap(),
serde_json::json!({"error": "unknown tool: nope"})
);
}
#[test]
fn input_that_is_not_json_is_a_tool_error() {
let (stdout, code) = execute(&args("greet"), b"not json", route);
assert_eq!(code, 1);
let value: serde_json::Value = serde_json::from_slice(&stdout).unwrap();
assert!(
value["error"]
.as_str()
.unwrap()
.starts_with("input is not JSON")
);
}
#[test]
fn input_of_the_wrong_shape_names_the_problem() {
let (stdout, code) = execute(&args("greet"), br#"{"name": 3}"#, route);
assert_eq!(code, 1);
let value: serde_json::Value = serde_json::from_slice(&stdout).unwrap();
assert!(
value["error"]
.as_str()
.unwrap()
.starts_with("invalid input")
);
}
#[test]
fn a_missing_tool_argument_is_a_tool_error() {
let (stdout, code) = execute(&["pkg".to_string()], b"{}", route);
assert_eq!(code, 1);
let value: serde_json::Value = serde_json::from_slice(&stdout).unwrap();
assert_eq!(value["error"], "no tool named on the command line");
}
#[test]
fn output_that_json_cannot_hold_is_a_tool_error() {
let unjsonable = std::collections::BTreeMap::from([((1, 2), 0)]);
let error = Output::json(&unjsonable).err().unwrap();
assert!(
error.message().starts_with("output is not JSON"),
"{error:?}"
);
}
#[test]
fn a_tool_error_takes_a_message_of_either_string_type() {
assert_eq!(ToolError::from("no").message(), "no");
assert_eq!(ToolError::from("no".to_string()).message(), "no");
}