", "HTML tags")]
+ #[case("a & b", "a & b", "ampersand")]
+ #[case(""quoted"", "\"quoted\"", "quotes")]
+ fn test_html_unescape(#[case] input: &str, #[case] expected: &str, #[case] _description: &str) {
+ assert_eq!(html_unescape(input), expected);
+ }
+
+ #[test]
+ fn test_parse_simple_tool_call() {
+ let input = r#"
+
+
+pwd && ls
+
+
+"#;
+
+ let (calls, normal) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "execute_bash");
+ assert_eq!(normal, Some("".to_string()));
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ assert_eq!(args["command"], "pwd && ls");
+ }
+
+ #[test]
+ fn test_parse_multiple_parameters() {
+ let input = r#"
+
+
+San Francisco
+
+
+CA
+
+
+fahrenheit
+
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "get_weather");
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ assert_eq!(args["city"], "San Francisco");
+ assert_eq!(args["state"], "CA");
+ assert_eq!(args["unit"], "fahrenheit");
+ }
+
+ #[test]
+ fn test_parse_with_normal_text() {
+ let input = r#"I'll help you with that.
+
+
+Dallas
+
+
+ Let me check that for you."#;
+
+ let (calls, normal) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "get_weather");
+ assert_eq!(
+ normal,
+ Some("I'll help you with that. Let me check that for you.".to_string())
+ );
+ }
+
+ #[test]
+ fn test_parse_multiple_tool_calls() {
+ let input = r#"
+
+
+Dallas
+
+
+
+
+
+
+Orlando
+
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 2);
+ assert_eq!(calls[0].function.name, "get_weather");
+ assert_eq!(calls[1].function.name, "get_weather");
+
+ let args0: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ let args1: serde_json::Value = serde_json::from_str(&calls[1].function.arguments).unwrap();
+ assert_eq!(args0["city"], "Dallas");
+ assert_eq!(args1["city"], "Orlando");
+ }
+
+ #[test]
+ fn test_parse_json_parameter_value() {
+ let input = r#"
+
+
+{"setting": "value", "count": 42}
+
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ assert!(args["config"].is_object());
+ assert_eq!(args["config"]["setting"], "value");
+ assert_eq!(args["config"]["count"], 42);
+ }
+
+ #[test]
+ fn test_parse_no_tool_calls() {
+ let input = "This is just normal text without any tool calls.";
+ let (calls, normal) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 0);
+ assert_eq!(normal, Some(input.to_string()));
+ }
+
+ #[test]
+ fn test_parse_malformed_tool_call() {
+ let input = r#"
+
+
+value
+"#;
+
+ // Should handle gracefully - might parse or return empty
+ let result = try_tool_call_parse_xml(input);
+ assert!(result.is_ok());
+ }
+
+ #[test]
+ fn test_parse_missing_parameter_closing_tag() {
+ let input = r#"
+
+
+ls -la
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "execute_bash");
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ assert_eq!(args["command"], "ls -la");
+ }
+
+ #[test]
+ fn test_parse_missing_function_closing_tag() {
+ let input = r#"
+
+
+Boston
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "get_weather");
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ assert_eq!(args["city"], "Boston");
+ }
+
+ #[test]
+ fn test_parse_missing_both_closing_tags() {
+ let input = r#"
+
+
+SELECT * FROM users
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "run_query");
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ // This matches the original SGLang python implementation.
+ assert_eq!(args["sql"], "SELECT * FROM users\n");
+ }
+
+ #[test]
+ fn test_parse_multiple_parameters_missing_closing_tags() {
+ let input = r#"
+
+
+rust programming
+
+10
+
+"#;
+
+ let (calls, _) = try_tool_call_parse_xml(input).unwrap();
+ assert_eq!(calls.len(), 1);
+ assert_eq!(calls[0].function.name, "search");
+
+ let args: serde_json::Value = serde_json::from_str(&calls[0].function.arguments).unwrap();
+ // This matches the original SGLang python implementation.
+ assert_eq!(args["query"], "rust programming\n
\n10");
+ }
+}