"""Verification test for emulated function call parsing fix. Tests that the FunctionCallResponseParser correctly handles text-formatted function calls like: Request function call: write_to_file Parameters: { "path": "test.txt", "content": "Hello" } """ import pytest from api_utils.utils_ext.function_call_response_parser import FunctionCallResponseParser class TestEmulatedFunctionCallParsing: """Test suite for emulated text-based function call parsing.""" def test_pattern_matches_simple_function_call(self): """Test that EMULATED_FUNCTION_CALL_PATTERN matches basic format.""" text = "Request function call: write_to_file\nParameters:\n{}" match = FunctionCallResponseParser.EMULATED_FUNCTION_CALL_PATTERN.search(text) assert match is not None assert match.group(1).strip() == "write_to_file" def test_pattern_matches_with_colon_namespace(self): """Test parsing function names with namespace like default_api:func.""" text = "Request function call: default_api:apply_diff\nParameters:\n{}" match = FunctionCallResponseParser.EMULATED_FUNCTION_CALL_PATTERN.search(text) assert match is not None assert match.group(1).strip() == "default_api:apply_diff" def test_params_pattern_extracts_json(self): """Test that EMULATED_PARAMS_PATTERN extracts JSON block.""" text = """Parameters: { "path": "test.txt", "content": "Hello World" }""" match = FunctionCallResponseParser.EMULATED_PARAMS_PATTERN.search(text) assert match is not None # Should capture the JSON block import json params = json.loads(match.group(1)) assert params["path"] == "test.txt" assert params["content"] == "Hello World" def test_parse_emulated_single_call(self): """Test parsing a single emulated function call.""" # Create a mock parser (we'll test the method directly) class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = """Request function call: write_to_file Parameters: { "path": "test_tool_capability.txt", "content": "This is a test file to verify tool capability." }""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "write_to_file" assert calls[0].arguments["path"] == "test_tool_capability.txt" assert "test file" in calls[0].arguments["content"] def test_parse_emulated_inline_params(self): """Test parsing function call with inline params format.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") # Format seen in Kilo Code logs: "list_files{path:.,recursive:false}" text = """Request function call: list_files Parameters: {"path": ".", "recursive": false}""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "list_files" assert calls[0].arguments.get("path") == "." assert calls[0].arguments.get("recursive") is False def test_parse_multiple_emulated_calls(self): """Test parsing multiple function calls in one response.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = """I'll help you with that. Request function call: read_file Parameters: {"path": "src/main.py"} Then I'll also need to check: Request function call: list_files Parameters: {"path": "src", "recursive": true}""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 2 assert calls[0].name == "read_file" assert calls[0].arguments["path"] == "src/main.py" assert calls[1].name == "list_files" assert calls[1].arguments["path"] == "src" def test_parse_with_control_characters(self): """Test parsing handles control characters in output.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") # Simulate control characters that appeared in logs text = """Request function call: apply_diff Parameters: {"diff": "some content", "path": "file.py"}""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "apply_diff" def test_clean_json_string_removes_control_chars(self): """Test that _clean_json_string removes control characters.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") dirty = '{"path": ".", "value": "test"}' cleaned = parser._clean_json_string(dirty) assert "" not in cleaned assert '"path": "."' in cleaned def test_no_false_positives_for_regular_text(self): """Test that regular text without function calls returns empty list.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = "Here's how to use the write_to_file function in your code." calls = parser._parse_emulated_function_calls(text) assert len(calls) == 0 def test_handles_nested_json_in_params(self): """Test parsing handles nested JSON objects in parameters.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = """Request function call: complex_tool Parameters: { "config": { "nested": { "deep": "value" } }, "options": ["a", "b", "c"] }""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "complex_tool" assert calls[0].arguments["config"]["nested"]["deep"] == "value" assert calls[0].arguments["options"] == ["a", "b", "c"] def test_strips_default_api_prefix(self): """Test that default_api: prefix is stripped from function names.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = """Request function call: default_api:write_to_file Parameters: {"path": "test.txt", "content": "hello"}""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "write_to_file" # Prefix stripped assert calls[0].arguments["path"] == "test.txt" def test_inline_params_with_ctrl46_delimiters(self): """Test parsing inline params using as string delimiters.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = "Request function call: read_file{files:[{path:temp_test.txt}]}" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "read_file" assert "files" in calls[0].arguments assert calls[0].arguments["files"][0]["path"] == "temp_test.txt" def test_multiline_ctrl46_params(self): """Test parsing multi-line content in delimited params.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = """Request function call: default_api:update_todos{todos:[ ] Create file [ ] Verify file [x] Done}""" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "update_todos" # Prefix stripped assert "todos" in calls[0].arguments assert "Create file" in calls[0].arguments["todos"] def test_no_param_function_call(self): """Test parsing function calls with no parameters.""" class MockPage: pass parser = FunctionCallResponseParser(page=MockPage(), req_id="test") text = "Request function call: attempt_completion" calls = parser._parse_emulated_function_calls(text) assert len(calls) == 1 assert calls[0].name == "attempt_completion" assert calls[0].arguments == {} if __name__ == "__main__": pytest.main([__file__, "-v"])