Files
coding-agent-gitea/tests/test_coding_tools.py
T
michael 0461c6c7ab Add .gitignore and pyproject.toml (#1)
### Findings and Changes

#### Changes:
- **Added **: Included a standard Python  to avoid tracking unnecessary files (e.g., , , ).
- **Added **: Prepared the project for better dependency management.
- **Enhanced Gitea Tools**:
    - Implemented  in .
    - Implemented  (via ) in .

#### Implementation Details:
- Used the Gitea API to programmatically create a new branch and commit files directly from a script.
- Verified that the NAME:
   tea - command line tool to interact with Gitea

USAGE:
   tea [global options] [command [command options]]

VERSION:
   Version: 0.14.1  golang: 1.26.3  go-sdk: v0.25.1

DESCRIPTION:
   tea is a productivity helper for Gitea. It can be used to manage most entities on
   one or multiple Gitea instances & provides local helpers like 'tea pr checkout'.

   tea tries to make use of context provided by the repository in $PWD if available.
   tea works best in a upstream/fork workflow, when the local main branch tracks the
   upstream repo. tea assumes that local git state is published on the remote before
   doing operations with tea.    Configuration is persisted in $XDG_CONFIG_HOME/tea.

COMMANDS:
   help, h  Shows a list of commands or help for one command

   ENTITIES:
     issues, issue, i                  List, create and update issues
     pulls, pull, pr                   Manage and checkout pull requests
     labels, label                     Manage issue labels
     milestones, milestone, ms         List and create milestones
     releases, release, r              Manage releases
     times, time, t                    Operate on tracked times of a repository's issues & pulls
     organizations, organization, org  List, create, delete organizations
     repos, repo                       Manage repositories
     branches, branch, b               Consult branches
     actions, action                   Manage repository actions
     webhooks, webhook, hooks, hook    Manage webhooks
     comment, c                        Add a comment to an issue / pr

   HELPERS:
     open, o                         Open something of the repository in web browser
     notifications, notification, n  Show notifications
     clone, C                        Clone a repository locally
     api                             Make an authenticated API request

   MISCELLANEOUS:
     whoami    Show current logged in user
     admin, a  Operations requiring admin access on the Gitea instance

   SETUP:
     logins, login      Log in to a Gitea server
     logout             Log out from a Gitea server
     ssh-keys, ssh-key  Manage SSH public keys

GLOBAL OPTIONS:
   --debug, --vvv  Enable debug mode
   --help, -h      show help
   --version, -v   print the version CLI can be used for automated PR creation.
- Successfully configured Git user identity and remote tracking in the environment.

---------

Co-authored-by: Michael <michael@example.com>
Reviewed-on: #1
2026-06-28 18:38:28 +02:00

148 lines
4.6 KiB
Python

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
res: str = CodingTools().grep_search("pattern", "/path")
assert res == "match_line"
@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
res: str = CodingTools().grep_search("pattern", "/path")
assert "No matches found" in res
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