152 lines
5.5 KiB
Python
152 lines
5.5 KiB
Python
# Copyright (c) 2025 ByteDance Ltd. and/or its affiliates
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
import unittest
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from trae_agent.agent.agent_basics import AgentError
|
|
from trae_agent.agent.trae_agent import TraeAgent
|
|
from trae_agent.utils.config import Config
|
|
from trae_agent.utils.legacy_config import LegacyConfig
|
|
from trae_agent.utils.llm_clients.llm_basics import LLMResponse
|
|
|
|
|
|
class TestTraeAgentExtended(unittest.TestCase):
|
|
def setUp(self):
|
|
test_config = {
|
|
"default_provider": "anthropic",
|
|
"max_steps": 20,
|
|
"model_providers": {
|
|
"anthropic": {
|
|
"model": "claude-sonnet-4-20250514",
|
|
"api_key": "test-dummy-api-key", # dummy api key
|
|
"max_tokens": 4096,
|
|
"temperature": 0.5,
|
|
"top_p": 1,
|
|
"top_k": 0,
|
|
"parallel_tool_calls": False,
|
|
"max_retries": 10,
|
|
}
|
|
},
|
|
}
|
|
self.config = Config.create_from_legacy_config(legacy_config=LegacyConfig(test_config))
|
|
|
|
# Avoid create real LLMClient instance to avoid actual API calls
|
|
self.llm_client_patcher = patch("trae_agent.agent.base_agent.LLMClient")
|
|
mock_llm_client = self.llm_client_patcher.start()
|
|
mock_llm_client.return_value.client = MagicMock()
|
|
|
|
if self.config.trae_agent:
|
|
self.agent = TraeAgent(self.config.trae_agent)
|
|
else:
|
|
self.fail("trae_agent config is None")
|
|
self.test_project_path = "/test/project"
|
|
self.test_patch_path = "/test/patch.diff"
|
|
|
|
def tearDown(self):
|
|
self.llm_client_patcher.stop()
|
|
|
|
def test_new_task_initialization(self):
|
|
with self.assertRaises(AgentError):
|
|
self.agent.new_task("test", {}) # Missing required params
|
|
|
|
valid_args = {
|
|
"project_path": self.test_project_path,
|
|
"issue": "Test issue",
|
|
"base_commit": "abc123",
|
|
"must_patch": "true",
|
|
"patch_path": self.test_patch_path,
|
|
}
|
|
self.agent.new_task("test-task", valid_args)
|
|
|
|
self.assertEqual(self.agent.project_path, self.test_project_path)
|
|
self.assertEqual(self.agent.must_patch, "true")
|
|
self.assertEqual(len(self.agent.tools), 4)
|
|
self.assertTrue(any(tool.get_name() == "bash" for tool in self.agent.tools))
|
|
|
|
@patch("subprocess.check_output")
|
|
@patch("os.chdir")
|
|
@patch("os.path.isdir", return_value=True)
|
|
def test_git_diff_generation(self, mock_isdir, mock_chdir, mock_subprocess):
|
|
mock_subprocess.return_value = b"test diff"
|
|
self.agent.project_path = self.test_project_path
|
|
|
|
diff = self.agent.get_git_diff()
|
|
self.assertEqual(diff, "test diff")
|
|
mock_subprocess.assert_called_with(["git", "--no-pager", "diff"])
|
|
|
|
def test_patch_filtering(self):
|
|
test_patch = """diff --git a/tests/test_example.py b/tests/test_example.py
|
|
--- a/tests/test_example.py
|
|
+++ b/tests/test_example.py
|
|
@@ -5,6 +5,7 @@
|
|
def test_example(self):
|
|
assert True
|
|
"""
|
|
filtered = self.agent.remove_patches_to_tests(test_patch)
|
|
self.assertEqual(filtered, "")
|
|
|
|
def test_task_completion_detection(self):
|
|
mock_response = MagicMock(spec=LLMResponse)
|
|
|
|
# Test empty patch scenario
|
|
self.agent.must_patch = "true"
|
|
self.assertFalse(self.agent._is_task_completed(mock_response))
|
|
|
|
# Test valid patch scenario
|
|
with patch.object(self.agent, "get_git_diff", return_value="valid patch"):
|
|
self.assertTrue(self.agent._is_task_completed(mock_response))
|
|
|
|
def test_tool_initialization(self):
|
|
tools = [
|
|
"bash",
|
|
"str_replace_based_edit_tool",
|
|
"sequentialthinking",
|
|
"task_done",
|
|
]
|
|
self.agent.new_task("test", {"project_path": self.test_project_path}, tools)
|
|
tool_names = [tool.get_name() for tool in self.agent.tools]
|
|
|
|
self.assertEqual(len(self.agent.tools), len(tools))
|
|
self.assertIn("bash", tool_names)
|
|
self.assertIn("str_replace_based_edit_tool", tool_names)
|
|
self.assertIn("sequentialthinking", tool_names)
|
|
self.assertIn("task_done", tool_names)
|
|
|
|
def test_protected_attributes_access_restrictions(self):
|
|
"""Test that protected attributes cannot be accessed directly from outside the class."""
|
|
|
|
# Test that accessing protected attributes raises AttributeError
|
|
with self.assertRaises(AttributeError):
|
|
self.agent.llm_client = 5
|
|
|
|
with self.assertRaises(AttributeError):
|
|
self.agent.max_steps = None
|
|
|
|
with self.assertRaises(AttributeError):
|
|
self.agent.model_config = False
|
|
|
|
with self.assertRaises(AttributeError):
|
|
self.agent.initial_messages = "random"
|
|
|
|
with self.assertRaises(AttributeError):
|
|
_ = self.agent.tool_caller
|
|
|
|
def test_public_property_access_allowed(self):
|
|
"""Test that public properties can be accessed properly."""
|
|
|
|
# Test that public properties work correctly
|
|
self.assertIsNotNone(self.agent.llm_client)
|
|
self.assertIsNone(self.agent.cli_console)
|
|
|
|
# Test that public property setters work
|
|
from trae_agent.utils.cli import CLIConsole
|
|
|
|
mock_console = MagicMock(spec=CLIConsole)
|
|
self.agent.set_cli_console(mock_console)
|
|
self.assertEqual(self.agent.cli_console, mock_console)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|