import os import subprocess from pathlib import Path from unittest.mock import MagicMock, patch import pytest from gitea.tools.coding_tools import CodingTools def test_list_files(tmp_path: Path) -> None: d: Path = tmp_path / "sub" d.mkdir() f: Path = d / "hello.txt" f.write_text("content") res: str = CodingTools().list_files(str(d)) assert "hello.txt" in res def test_list_files_error() -> None: res: str = CodingTools().list_files("/nonexistent/directory/path/here") assert "Error listing files" in res def test_read_file(tmp_path: Path) -> None: f: Path = tmp_path / "test.txt" f.write_text("line1\nline2\nline3\n") res: str = CodingTools().read_file(str(f), offset=1, limit=2) assert "1: line1" in res assert "2: line2" in res assert "3: line3" not in res def test_read_file_empty_or_out_of_bounds(tmp_path: Path) -> None: f: Path = tmp_path / "test.txt" f.write_text("") res: str = CodingTools().read_file(str(f), offset=10, limit=2) assert res == "File is empty or offset out of bounds." def test_read_file_error() -> None: res: str = CodingTools().read_file("/nonexistent/file/path/here") assert "Error reading file" in res def test_write_file(tmp_path: Path) -> None: f: Path = tmp_path / "new_dir" / "test.txt" res: str = CodingTools().write_file(str(f), "content") assert "written successfully" in res assert f.read_text() == "content" def test_write_file_error() -> None: res: str = CodingTools().write_file("", "content") assert "Error writing file" in res def test_edit_file(tmp_path: Path) -> None: f: Path = tmp_path / "test.txt" f.write_text("hello world") res: str = CodingTools().edit_file(str(f), "world", "there") assert "edited successfully" in res assert f.read_text() == "hello there" def test_edit_file_not_found(tmp_path: Path) -> None: f: Path = tmp_path / "test.txt" f.write_text("hello world") res: str = CodingTools().edit_file(str(f), "nonexistent", "there") assert "not found" in res def test_edit_file_error() -> None: res: str = CodingTools().edit_file("/nonexistent/file/path/here", "world", "there") assert "Error editing file" in res @patch("subprocess.Popen") def test_run_command_success(mock_popen: MagicMock) -> None: mock_process: MagicMock = MagicMock() mock_process.communicate.return_value = ("output_stdout", "output_stderr") mock_process.returncode = 0 mock_popen.return_value = mock_process res: str = CodingTools().run_command("echo hello") assert "output_stdout" in res assert "output_stderr" in res @patch("subprocess.Popen") def test_run_command_failure(mock_popen: MagicMock) -> None: mock_process: MagicMock = MagicMock() mock_process.communicate.return_value = ("output_stdout", "output_stderr") mock_process.returncode = 1 mock_popen.return_value = mock_process res: str = CodingTools().run_command("false") assert "Command failed with exit code 1" in res def test_run_command_error() -> None: with patch("subprocess.Popen", side_effect=ValueError("Invalid run")): res: str = CodingTools().run_command("echo") assert "Error running command" in res @patch("subprocess.Popen") def test_run_command_timeout(mock_popen: MagicMock) -> None: mock_process: MagicMock = MagicMock() mock_process.communicate.side_effect = [ subprocess.TimeoutExpired(cmd="test", timeout=1), ("stdout_after_kill", "stderr_after_kill") ] mock_popen.return_value = mock_process res: str = CodingTools().run_command("hang_cmd", timeout=1) assert "Command timed out after 1 seconds" in res assert "stdout_after_kill" in res mock_process.kill.assert_called_once() @patch("subprocess.Popen") def test_grep_search_success(mock_popen: MagicMock) -> None: mock_process: MagicMock = MagicMock() mock_process.communicate.return_value = ("match_line", "") mock_process.returncode = 0 mock_popen.return_value = mock_process tools = CodingTools() res: str = tools.grep_search("pattern", "/path") assert res == "match_line" mock_popen.assert_called_once_with( ["grep", "-ri", "pattern", tools._resolve_path("/path")], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=tools.repo_path, ) @patch("subprocess.Popen") def test_grep_search_no_matches(mock_popen: MagicMock) -> None: mock_process: MagicMock = MagicMock() mock_process.communicate.return_value = ("", "") mock_process.returncode = 1 mock_popen.return_value = mock_process tools = CodingTools() res: str = tools.grep_search("pattern", "/path") assert "No matches found" in res mock_popen.assert_called_once_with( ["grep", "-ri", "pattern", tools._resolve_path("/path")], shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, cwd=tools.repo_path, ) def test_grep_search_error() -> None: with patch("subprocess.Popen", side_effect=ValueError("Invalid run")): res: str = CodingTools().grep_search("pattern") assert "Error during grep search" in res