Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Run Tests

on:
pull_request:

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.10'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt

- name: Run tests
run: |
pytest tests/ -v
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
4 changes: 4 additions & 0 deletions requirements-dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Development dependencies
pytest==7.4.0
pytest-cov==4.1.0
pytest-mock==3.11.1
10 changes: 10 additions & 0 deletions run_tests.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

# Run tests with coverage
python -m pytest tests/ -v --cov=src --cov-report=term-missing

# To run specific test files:
# python -m pytest tests/test_agents.py -v

# To run integration tests only:
# python -m pytest tests/test_integration.py -v -m integration
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# This file is intentionally empty to make the directory a Python package
86 changes: 86 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""
Pytest configuration file with fixtures
"""
import os
import sys
import pytest
from unittest.mock import MagicMock

# Add src directory to path
src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(src_dir)


@pytest.fixture
def mock_issue():
"""Create a mock GitHub issue"""
issue = MagicMock()
issue.number = 123
issue.title = "Test Issue"
issue.body = "This is a test issue"
issue.state = "open"
issue.html_url = "https://github.com/test/repo/issues/123"

# Add labels
label = MagicMock()
label.name = "bug"
issue.labels = [label]

return issue


@pytest.fixture
def mock_issue_with_label(label_name="bug"):
"""Create a mock GitHub issue with specific label"""
def _create_issue(label_name):
issue = MagicMock()
issue.number = 123
issue.title = "Test Issue"
issue.body = "This is a test issue"
issue.state = "open"

# Add label
label = MagicMock()
label.name = label_name
issue.labels = [label]

return issue

return _create_issue(label_name)


@pytest.fixture
def mock_pull_request():
"""Create a mock GitHub pull request"""
pr = MagicMock()
pr.number = 456
pr.title = "Test PR"
pr.body = "This is a test pull request"
pr.state = "open"
pr.html_url = "https://github.com/test/repo/pull/456"

# Add head reference
head = MagicMock()
head.ref = "feature-branch"
pr.head = head

return pr


@pytest.fixture
def mock_repository():
"""Create a mock GitHub repository"""
repo = MagicMock()
repo.full_name = "test/repo"
repo.name = "repo"
repo.clone_url = "https://github.com/test/repo.git"
repo.default_branch = "main"

return repo


@pytest.fixture
def mock_github_client():
"""Create a mock GitHub client"""
client = MagicMock()
return client
111 changes: 111 additions & 0 deletions tests/test_agents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
"""
Tests for the agents module
"""
from autogen import UserProxyAgent, AssistantAgent
from src.agents import (
create_user_agent,
create_agent,
generate_prompt,
is_terminate_msg,
register_functions
)
import os
import sys
import pytest
from unittest.mock import MagicMock, patch

# Add src directory to path
src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(src_dir)


def test_create_user_agent():
"""Test creation of user agent"""
user_agent = create_user_agent()
assert isinstance(user_agent, UserProxyAgent)
assert user_agent.name == "User"
assert user_agent.human_input_mode == "NEVER"


def test_create_agent():
"""Test creation of assistant agent"""
llm_config = {"model": "test-model", "api_key": "test-key"}
agent = create_agent("file_assistant", llm_config)
assert isinstance(agent, AssistantAgent)
assert agent.name == "file_assistant"
assert "analyze this GitHub issue" in agent.system_message.lower()


def test_is_terminate_msg():
"""Test terminate message detection"""
assert is_terminate_msg({"content": "Done. TERMINATE"}) is True
assert is_terminate_msg({"content": "Not done yet"}) is False
assert is_terminate_msg({"content": "TERMINATE."}) is True
assert is_terminate_msg({"content": ""}) is False


@patch('src.agents.parse_comments')
def test_generate_prompt(mock_parse_comments):
"""Test prompt generation for different agent types"""
# Setup mock
mock_parse_comments.return_value = (
"Last comment", "Comments string", ["comment1"])

# Mock issue and details
issue = MagicMock()
issue.number = 123
issue.title = "Test Issue"

details = {
"title": "Test Issue",
"body": "This is a test issue"
}

# Test file_assistant prompt
prompt = generate_prompt(
"file_assistant", "test/repo", "/path/to/repo", details, issue)
assert "Please analyze this GitHub issue" in prompt
assert "Repository: test/repo" in prompt

# Test edit_assistant prompt
prompt = generate_prompt(
"edit_assistant", "test/repo", "/path/to/repo", details, issue)
assert "Suggest what changes can be made to resolve this issue" in prompt

# Test feedback_assistant prompt
prompt = generate_prompt(
"feedback_assistant",
"test/repo",
"/path/to/repo",
details,
issue,
original_response="Original response",
feedback_text="Feedback text"
)
assert "Process this user feedback" in prompt
assert "Original response" in prompt
assert "Feedback text" in prompt


def test_register_functions():
"""Test function registration with agent"""
agent = MagicMock()
agent.register_for_llm = MagicMock(return_value=lambda x: x)
agent.register_for_execution = MagicMock(return_value=lambda x: x)

# Define test function
def test_func():
"""Test function docstring"""
pass

# Test LLM registration
result = register_functions(agent, "llm", [test_func])
assert agent.register_for_llm.called
assert agent.register_for_llm.call_args[1]["name"] == "test_func"
assert "Test function docstring" in agent.register_for_llm.call_args[1]["description"]

# Test execution registration
agent.reset_mock()
result = register_functions(agent, "execution", [test_func])
assert agent.register_for_execution.called
assert agent.register_for_execution.call_args[1]["name"] == "test_func"
131 changes: 131 additions & 0 deletions tests/test_bot_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
"""
Tests for the bot_tools module
"""
from src.bot_tools import (
get_local_repo_path,
search_for_pattern,
search_for_file,
estimate_tokens,
readfile,
readlines
)
import os
import sys
import pytest
from unittest.mock import MagicMock, patch

# Add src directory to path
src_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(src_dir)


def test_get_local_repo_path():
"""Test getting local repository path"""
# Test with existing repo
with patch('os.path.exists', return_value=True):
path = get_local_repo_path("owner/repo")
assert "owner/repo" in path
assert path.endswith("owner/repo")

# Test with non-existing repo
with patch('os.path.exists', return_value=False):
result = get_local_repo_path("nonexistent/repo")
assert "not found" in result


@patch('os.popen')
def test_search_for_pattern(mock_popen):
"""Test searching for pattern in files"""
# Setup mock
mock_popen.return_value.read.return_value = "/path/to/file1.py\n/path/to/file2.py"

# Call function
result = search_for_pattern("/search/dir", "pattern")

# Verify
mock_popen.assert_called_with(
"grep -irl pattern /search/dir --include='*.py'")
assert result == "/path/to/file1.py\n/path/to/file2.py"


@patch('os.popen')
def test_search_for_file(mock_popen):
"""Test searching for file by name"""
# Setup mock
mock_popen.return_value.read.return_value = "/path/to/file.py"

# Call function
result = search_for_file("/search/dir", "file.py")

# Verify
mock_popen.assert_called_with("find /search/dir -iname '*file.py*'")
assert result == "/path/to/file.py"

# Test file not found
mock_popen.return_value.read.return_value = ""
result = search_for_file("/search/dir", "nonexistent.py")
assert result == "File not found"


def test_estimate_tokens():
"""Test token estimation"""
# Test with normal text
text = "This is a test string with multiple words."
assert estimate_tokens(text) == 8

# Test with empty text
assert estimate_tokens("") == 0

# Test with None
assert estimate_tokens(None) == 0


@patch('builtins.open')
def test_readfile(mock_open):
"""Test reading file with line numbers"""
# Setup mock
mock_file = MagicMock()
mock_file.__enter__.return_value.readlines.return_value = [
"Line 1\n", "Line 2\n", "Line 3\n"]
mock_open.return_value = mock_file

# Call function
result = readfile("/path/to/file.py")

# Verify
mock_open.assert_called_with("/path/to/file.py", 'r')
assert "0000: Line 1" in result
assert "0001: Line 2" in result
assert "0002: Line 3" in result

# Test file not found
mock_open.side_effect = FileNotFoundError()
result = readfile("/nonexistent/file.py")
assert "File not found" in result

# Test other error
mock_open.side_effect = Exception("Test error")
result = readfile("/error/file.py")
assert "Error reading file" in result


@patch('builtins.open')
def test_readlines(mock_open):
"""Test reading specific lines from file"""
# Setup mock
mock_file = MagicMock()
mock_file.__enter__.return_value.readlines.return_value = [
"Line 1\n", "Line 2\n", "Line 3\n", "Line 4\n", "Line 5\n"
]
mock_open.return_value = mock_file

# Call function
result = readlines("/path/to/file.py", 1, 4)

# Verify
mock_open.assert_called_with("/path/to/file.py", 'r')
assert "0001: Line 2" in result
assert "0002: Line 3" in result
assert "0003: Line 4" in result
assert "0000: Line 1" not in result
assert "0004: Line 5" not in result
Loading