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: [1m0.14.1[0m 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
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, AsyncMock, patch
|
||||
from pathlib import Path
|
||||
from main import JSONFormatter
|
||||
from gitea.tools.coding_tools import CodingTools
|
||||
from core.dispatcher import AgentDispatcher
|
||||
from core.queue import WorkItem
|
||||
from gitea.client import GiteaClient
|
||||
from gitea.tools.gitea_tools import GiteaTools
|
||||
from gitea.models import IssueModel
|
||||
|
||||
pytestmark = pytest.mark.anyio
|
||||
|
||||
|
||||
def test_json_formatter() -> None:
|
||||
formatter = JSONFormatter()
|
||||
record = logging.LogRecord(
|
||||
name="test-logger",
|
||||
level=logging.INFO,
|
||||
pathname="test.py",
|
||||
lineno=10,
|
||||
msg="Test log message",
|
||||
args=(),
|
||||
exc_info=None
|
||||
)
|
||||
formatted = formatter.format(record)
|
||||
data = json.loads(formatted)
|
||||
assert data["level"] == "INFO"
|
||||
assert data["logger"] == "test-logger"
|
||||
assert data["message"] == "Test log message"
|
||||
assert "timestamp" in data
|
||||
|
||||
|
||||
def test_read_file_limit(tmp_path: Path) -> None:
|
||||
# Create a large file
|
||||
large_file = tmp_path / "large_file.txt"
|
||||
lines = [f"Line {i}\n" for i in range(1, 400)]
|
||||
large_file.write_text("".join(lines), encoding="utf-8")
|
||||
|
||||
coding_tools = CodingTools(str(tmp_path))
|
||||
# Test read with default limit (250)
|
||||
result = coding_tools.read_file("large_file.txt", offset=1)
|
||||
result_lines = [line.strip() for line in result.splitlines() if line.strip()]
|
||||
assert len(result_lines) == 250
|
||||
assert result_lines[0] == "1: Line 1"
|
||||
assert result_lines[-1] == "250: Line 250"
|
||||
|
||||
# Test read with custom limit
|
||||
result_custom = coding_tools.read_file("large_file.txt", offset=10, limit=10)
|
||||
result_custom_lines = [line.strip() for line in result_custom.splitlines() if line.strip()]
|
||||
assert len(result_custom_lines) == 10
|
||||
assert result_custom_lines[0] == "10: Line 10"
|
||||
assert result_custom_lines[-1] == "19: Line 19"
|
||||
|
||||
|
||||
def test_parse_verification_commands(tmp_path: Path) -> None:
|
||||
agents_md = tmp_path / "AGENTS.md"
|
||||
agents_md.write_text(
|
||||
"# Agent Instructions\n\n"
|
||||
"## Verification\n"
|
||||
"Please verify your changes with:\n"
|
||||
"```bash\n"
|
||||
"pytest -v\n"
|
||||
"npm run lint\n"
|
||||
"```\n\n"
|
||||
"## Something Else\n"
|
||||
"```bash\n"
|
||||
"ignored command\n"
|
||||
"```\n",
|
||||
encoding="utf-8"
|
||||
)
|
||||
|
||||
coding_tools = CodingTools(str(tmp_path))
|
||||
commands = coding_tools._parse_verification_commands()
|
||||
assert commands == ["pytest -v", "npm run lint"]
|
||||
|
||||
|
||||
def test_run_verification_failure(tmp_path: Path) -> None:
|
||||
agents_md = tmp_path / "AGENTS.md"
|
||||
agents_md.write_text(
|
||||
"## Verification\n"
|
||||
"```bash\n"
|
||||
"false\n"
|
||||
"```\n",
|
||||
encoding="utf-8"
|
||||
)
|
||||
coding_tools = CodingTools(str(tmp_path))
|
||||
success, log_msg = coding_tools.run_verification()
|
||||
assert not success
|
||||
assert "false" in log_msg
|
||||
|
||||
|
||||
@patch("core.dispatcher.CodingAgent")
|
||||
async def test_dispatch_planning_and_coding_phases(mock_agent_class: MagicMock) -> None:
|
||||
mock_client: MagicMock = MagicMock(spec=GiteaClient)
|
||||
mock_tools: MagicMock = MagicMock(spec=GiteaTools)
|
||||
|
||||
# Mock no existing PRs
|
||||
mock_client.list_repo_pull_requests.return_value = []
|
||||
mock_client.get_issue_comments.return_value = []
|
||||
|
||||
# Mock CodingAgent instances
|
||||
mock_planning_agent = MagicMock()
|
||||
mock_planning_agent.run_with_tools = AsyncMock(return_value="Plan: Modify file A")
|
||||
|
||||
mock_coding_agent = MagicMock()
|
||||
mock_coding_agent.run_with_tools = AsyncMock(return_value="PR #1 Created")
|
||||
|
||||
mock_agent_class.side_effect = [mock_planning_agent, mock_coding_agent]
|
||||
|
||||
dispatcher = AgentDispatcher(client=mock_client, tools=mock_tools)
|
||||
|
||||
work_item = WorkItem(
|
||||
repo_full_name="meeks/repo1",
|
||||
task_type="issue",
|
||||
task_number=42,
|
||||
task_info=IssueModel(number=42, title="fix bug", body="bug details"),
|
||||
priority=0
|
||||
)
|
||||
|
||||
# We mock os.path.isdir to return True so os.chdir won't fail or crash in test
|
||||
with patch("os.path.isdir", return_value=True), patch("os.chdir"):
|
||||
results = await dispatcher.dispatch("meeks/repo1", [work_item])
|
||||
|
||||
assert len(results) == 1
|
||||
assert results[0] == "PR #1 Created"
|
||||
|
||||
# Verify both agents ran
|
||||
assert mock_planning_agent.run_with_tools.call_count == 1
|
||||
assert mock_coding_agent.run_with_tools.call_count == 1
|
||||
|
||||
# Verify planning prompt was passed correct context
|
||||
planning_call_args = mock_planning_agent.run_with_tools.call_args[0]
|
||||
assert "PHASE 1: PLANNING PHASE" in planning_call_args[0]
|
||||
|
||||
# Verify coding prompt received the generated plan
|
||||
coding_call_args = mock_coding_agent.run_with_tools.call_args[0]
|
||||
assert "PHASE 2: EXECUTION/CODING PHASE" in coding_call_args[0]
|
||||
assert "Plan: Modify file A" in coding_call_args[0]
|
||||
Reference in New Issue
Block a user