use super::super::utils::CustomFormatParser;
#[test]
fn test_parser_initializes_successfully() {
let _parser = CustomFormatParser::new();
}
#[test]
fn test_parser_default_initializes() {
let _parser1 = CustomFormatParser::new();
let _parser2 = CustomFormatParser::default();
}
#[test]
fn test_parse_gpt_oss_v1_format() {
let parser = CustomFormatParser::new();
let content = r#"commentary to=functions.get_weather <|constrain|>json<|message|>{"city": "London", "units": "celsius"}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should parse GPT-OSS v1 format");
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "get_weather");
assert_eq!(match_result.arguments["city"], "London");
assert_eq!(match_result.arguments["units"], "celsius");
}
#[test]
fn test_parse_gpt_oss_v1_with_text_around() {
let parser = CustomFormatParser::new();
let content = r#"Let me check the weather. commentary to=functions.get_weather <|constrain|>json<|message|>{"city": "NYC"} I'll get that for you."#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "get_weather");
assert!(match_result.cleaned_content.contains("Let me check"));
assert!(match_result.cleaned_content.contains("I'll get that"));
}
#[test]
fn test_parse_xml_tool_call_format() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "search", "arguments": {"query": "rust"}}</tool_call>"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should parse XML tool_call format");
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "search");
assert_eq!(match_result.arguments["query"], "rust");
assert!(
match_result.cleaned_content.is_empty(),
"Content should be cleaned"
);
}
#[test]
fn test_parse_xml_tool_call_without_closing_tag() {
let parser = CustomFormatParser::new();
let content =
r#"<tool_call>{"name": "calculator", "arguments": {"operation": "add", "a": 5, "b": 3}}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should parse XML without closing tag");
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "calculator");
assert_eq!(match_result.arguments["operation"], "add");
}
#[test]
fn test_parse_xml_tool_call_multiline() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>
{
"name": "get_user",
"arguments": {
"user_id": 123
}
}
</tool_call>"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should parse multiline XML");
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "get_user");
assert_eq!(match_result.arguments["user_id"], 123);
}
#[test]
fn test_parse_deepseek_tool_request_format() {
let parser = CustomFormatParser::new();
let content = r#"[TOOL_REQUEST]{"name": "file_read", "arguments": {"path": "/tmp/file.txt"}}[END_TOOL_REQUEST]"#;
let result = parser.parse(content).unwrap();
assert!(
result.is_some(),
"Should parse DeepSeek TOOL_REQUEST format"
);
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "file_read");
assert_eq!(match_result.arguments["path"], "/tmp/file.txt");
}
#[test]
fn test_parse_deepseek_with_surrounding_text() {
let parser = CustomFormatParser::new();
let content = r#"I'll read the file now. [TOOL_REQUEST]{"name": "read", "arguments": {"file": "data.json"}}[END_TOOL_REQUEST] Done."#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "read");
assert!(match_result.cleaned_content.contains("I'll read"));
assert!(match_result.cleaned_content.contains("Done"));
}
#[test]
fn test_parse_tool_call_with_args_format() {
let parser = CustomFormatParser::new();
let content = r#"Tool call: calculate with args: {"x": 10, "y": 20}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should parse tool_call_with_args format");
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "calculate");
assert_eq!(match_result.arguments["x"], 10);
assert_eq!(match_result.arguments["y"], 20);
}
#[test]
fn test_parse_json_only_format() {
let parser = CustomFormatParser::new();
let content =
r#"{"name": "send_email", "arguments": {"to": "user@example.com", "subject": "Hello"}}"#;
let result = parser.parse(content).unwrap();
if let Some(match_result) = result {
assert_eq!(match_result.function_name, "send_email");
assert_eq!(match_result.arguments["to"], "user@example.com");
}
}
#[test]
fn test_parse_returns_none_for_plain_text() {
let parser = CustomFormatParser::new();
let content = "This is just a regular response with no tool calls.";
let result = parser.parse(content).unwrap();
assert!(result.is_none(), "Plain text should not match any pattern");
}
#[test]
fn test_parse_returns_none_for_empty_string() {
let parser = CustomFormatParser::new();
let content = "";
let result = parser.parse(content).unwrap();
assert!(result.is_none(), "Empty string should not match");
}
#[test]
fn test_parse_returns_none_for_incomplete_format() {
let parser = CustomFormatParser::new();
let content = "<tool_call>incomplete json";
let result = parser.parse(content);
assert!(result.is_err() || result.unwrap().is_none());
}
#[test]
fn test_parse_handles_invalid_json() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "func", "arguments": {invalid json}}</tool_call>"#;
let result = parser.parse(content);
assert!(result.is_err() || result.unwrap().is_none());
}
#[test]
fn test_parse_handles_missing_name_field() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"arguments": {"x": 1}}</tool_call>"#;
let result = parser.parse(content);
assert!(result.is_err(), "Should error on missing 'name' field");
}
#[test]
fn test_parse_handles_missing_arguments_field() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "test_func"}</tool_call>"#;
let result = parser.parse(content);
assert!(result.is_err(), "Should error on missing 'arguments' field");
}
#[test]
fn test_cleaned_content_removes_tool_marker() {
let parser = CustomFormatParser::new();
let content =
r#"Let me help. <tool_call>{"name": "help", "arguments": {}}</tool_call> Here you go."#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert!(!match_result.cleaned_content.contains("<tool_call>"));
assert!(!match_result.cleaned_content.contains("</tool_call>"));
assert!(match_result.cleaned_content.contains("Let me help"));
assert!(match_result.cleaned_content.contains("Here you go"));
}
#[test]
fn test_raw_match_preserves_full_pattern() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "test", "arguments": {"key": "value"}}</tool_call>"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert!(match_result.raw_match.starts_with("<tool_call>"));
assert!(match_result.raw_match.ends_with("</tool_call>"));
assert!(match_result.raw_match.contains("test"));
}
#[test]
fn test_parser_tries_patterns_in_order() {
let parser = CustomFormatParser::new();
let content = r#"commentary to=functions.test <|constrain|>json<|message|>{"x": 1}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some(), "Should match first applicable pattern");
}
#[test]
fn test_parse_handles_nested_objects() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "create_user", "arguments": {"user": {"name": "John", "address": {"city": "NYC", "zip": "10001"}}}}</tool_call>"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "create_user");
assert_eq!(match_result.arguments["user"]["name"], "John");
assert_eq!(match_result.arguments["user"]["address"]["city"], "NYC");
}
#[test]
fn test_parse_handles_array_arguments() {
let parser = CustomFormatParser::new();
let content = r#"<tool_call>{"name": "batch_process", "arguments": {"items": [1, 2, 3, 4, 5]}}</tool_call>"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "batch_process");
assert!(match_result.arguments["items"].is_array());
assert_eq!(match_result.arguments["items"].as_array().unwrap().len(), 5);
}
#[test]
fn test_clean_tool_call_patterns_removes_xml_tags() {
let content = "Some text <tool_call>{\"broken\": json}</tool_call> more text";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert!(!cleaned.contains("<tool_call>"));
assert!(!cleaned.contains("</tool_call>"));
assert!(cleaned.contains("Some text"));
assert!(cleaned.contains("more text"));
}
#[test]
fn test_clean_tool_call_patterns_removes_deepseek_format() {
let content = "Result: [TOOL_REQUEST]{\"invalid\": data}[END_TOOL_REQUEST] Done";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert!(!cleaned.contains("[TOOL_REQUEST]"));
assert!(!cleaned.contains("[END_TOOL_REQUEST]"));
assert!(cleaned.contains("Result:"));
assert!(cleaned.contains("Done"));
}
#[test]
fn test_clean_tool_call_patterns_removes_tool_call_format() {
let content = "I'll help. Tool call: search with args: {\"query\": \"test\"} Here.";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert!(!cleaned.contains("Tool call:"));
assert!(!cleaned.contains("with args:"));
assert!(cleaned.contains("I'll help."));
assert!(cleaned.contains("Here."));
}
#[test]
fn test_clean_tool_call_patterns_handles_unclosed_xml() {
let content = "Text <tool_call>incomplete json without closing";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert!(!cleaned.contains("<tool_call>"));
assert!(cleaned.contains("Text"));
}
#[test]
fn test_clean_tool_call_patterns_no_patterns_unchanged() {
let content = "This is just regular text without any tool calls.";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert_eq!(cleaned, content);
}
#[test]
fn test_clean_tool_call_patterns_empty_string() {
let content = "";
let cleaned = CustomFormatParser::clean_tool_call_patterns(content);
assert_eq!(cleaned, "");
}
#[test]
fn test_extract_balanced_json_simple() {
let text = r#"{"key": "value"} extra text"#;
let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_some());
let (json_str, end_pos) = result.unwrap();
assert_eq!(json_str, r#"{"key": "value"}"#);
assert!(end_pos > 0);
}
#[test]
fn test_extract_balanced_json_nested() {
let text = r#"{"outer": {"inner": {"deep": true}}} more"#;
let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_some());
let (json_str, _) = result.unwrap();
assert!(json_str.contains("outer"));
assert!(json_str.contains("inner"));
assert!(json_str.contains("deep"));
}
#[test]
fn test_extract_balanced_json_with_braces_in_strings() {
let text = r#"{"code": "if (x) { return }"} rest"#;
let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_some());
let (json_str, _) = result.unwrap();
assert!(json_str.contains("if (x) { return }"));
}
#[test]
fn test_extract_balanced_json_no_json() {
let text = "This is not JSON";
let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_none());
}
#[test]
fn test_extract_balanced_json_unbalanced() {
let text = r#"{"key": "value""#; let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_none());
}
#[test]
fn test_extract_balanced_json_with_leading_whitespace() {
let text = " {\"key\": 1}";
let result = CustomFormatParser::extract_balanced_json(text);
assert!(result.is_some());
let (json_str, _) = result.unwrap();
assert_eq!(json_str, r#"{"key": 1}"#);
}
#[test]
fn test_parse_deepseek_without_end_marker_returns_none() {
let parser = CustomFormatParser::new();
let content = r#"[TOOL_REQUEST]{"name": "test", "arguments": {"x": 1}}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_none());
}
#[test]
fn test_parse_deepseek_multiline() {
let parser = CustomFormatParser::new();
let content = r#"[TOOL_REQUEST]{
"name": "multiline_tool",
"arguments": {
"param1": "value1",
"param2": 42
}
}[END_TOOL_REQUEST]"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "multiline_tool");
assert_eq!(match_result.arguments["param1"], "value1");
assert_eq!(match_result.arguments["param2"], 42);
}
#[test]
fn test_count_json_braces_simple() {
let (open, close) = CustomFormatParser::count_json_braces(r#"{"key": "value"}"#);
assert_eq!(open, 1);
assert_eq!(close, 1);
}
#[test]
fn test_count_json_braces_nested() {
let (open, close) = CustomFormatParser::count_json_braces(r#"{"a": {"b": {"c": 1}}}"#);
assert_eq!(open, 3);
assert_eq!(close, 3);
}
#[test]
fn test_count_json_braces_ignores_braces_in_strings() {
let (open, close) = CustomFormatParser::count_json_braces(r#"{"code": "if (x) { } else { }"}"#);
assert_eq!(open, 1);
assert_eq!(close, 1);
}
#[test]
fn test_count_json_braces_unbalanced() {
let (open, close) = CustomFormatParser::count_json_braces(r#"{"key": "value""#);
assert_eq!(open, 1);
assert_eq!(close, 0);
}
#[test]
fn test_count_json_braces_with_escaped_quotes() {
let (open, close) = CustomFormatParser::count_json_braces(r#"{"text": "say \"hello\""}"#);
assert_eq!(open, 1);
assert_eq!(close, 1);
}
#[test]
fn test_attempt_json_repair_already_valid() {
let text = r#"{"key": "value"}"#;
let repaired = CustomFormatParser::attempt_json_repair(text);
assert_eq!(repaired, text);
}
#[test]
fn test_attempt_json_repair_missing_one_brace() {
let text = r#"{"key": "value""#;
let repaired = CustomFormatParser::attempt_json_repair(text);
assert_eq!(repaired, r#"{"key": "value"}"#);
}
#[test]
fn test_attempt_json_repair_missing_multiple_braces() {
let text = r#"{"outer": {"inner": "value""#;
let repaired = CustomFormatParser::attempt_json_repair(text);
assert_eq!(repaired, r#"{"outer": {"inner": "value"}}"#);
}
#[test]
fn test_attempt_json_repair_non_json_unchanged() {
let text = "This is not JSON";
let repaired = CustomFormatParser::attempt_json_repair(text);
assert_eq!(repaired, text);
}
#[test]
fn test_attempt_json_repair_with_whitespace() {
let text = " {\"key\": \"value\" ";
let repaired = CustomFormatParser::attempt_json_repair(text);
assert_eq!(repaired, r#"{"key": "value"}"#);
}
#[test]
fn test_add_missing_braces_single() {
let repaired = CustomFormatParser::add_missing_braces(r#"{"key": "value""#, 1);
assert_eq!(repaired, r#"{"key": "value"}"#);
}
#[test]
fn test_add_missing_braces_multiple() {
let repaired = CustomFormatParser::add_missing_braces(r#"{"a": {"b": 1"#, 2);
assert_eq!(repaired, r#"{"a": {"b": 1}}"#);
}
#[test]
fn test_add_missing_braces_zero() {
let text = r#"{"key": "value"}"#;
let repaired = CustomFormatParser::add_missing_braces(text, 0);
assert_eq!(repaired, text);
}
#[test]
fn test_parse_gpt_oss_with_nested_json_arguments() {
let parser = CustomFormatParser::new();
let content = r#"commentary to=functions.complex_call <|constrain|>json<|message|>{"nested": {"level": {"deep": {"value": 42}}}}"#;
let result = parser.parse(content).unwrap();
assert!(result.is_some());
let match_result = result.unwrap();
assert_eq!(match_result.function_name, "complex_call");
assert_eq!(
match_result.arguments["nested"]["level"]["deep"]["value"],
42
);
}