diff --git a/.github/workflows/test_bot_responses.yml b/.github/workflows/test_bot_responses.yml new file mode 100644 index 0000000..937f703 --- /dev/null +++ b/.github/workflows/test_bot_responses.yml @@ -0,0 +1,53 @@ +name: Test Bot Responses + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: '3.10' + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + pip install pytest pytest-mock pytest-cov + + - name: Run basic tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + run: | + pytest tests/test_response_agent.py -v + + - name: Run detailed tests + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pytest tests/test_detailed_responses.py -v + + - name: Run all tests with coverage + env: + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + pytest tests/ -v --cov=src --cov-report=xml + + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + fail_ci_if_error: false diff --git a/pytest.ini b/pytest.ini new file mode 100644 index 0000000..9ca9663 --- /dev/null +++ b/pytest.ini @@ -0,0 +1,2 @@ +[pytest] +addopts = --ignore=src/repos diff --git a/requirements.txt b/requirements.txt index b29158c..628a5a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,7 @@ pre-commit>=3.5.0 urlextract>=1.0.0 beautifulsoup4>=4.9.3 aider-chat>=0.18.0 +pytest>=7.0.0 +pytest-mock>=3.10.0 +pytest-cov>=4.1.0 +codecov>=2.1.13 diff --git a/src/__init__.py b/src/__init__.py new file mode 100644 index 0000000..bbf6276 --- /dev/null +++ b/src/__init__.py @@ -0,0 +1 @@ +# This file is required to make Python treat the directory as a package. diff --git a/src/agents.py b/src/agents.py index 396cdf9..7ccbffc 100644 --- a/src/agents.py +++ b/src/agents.py @@ -5,8 +5,8 @@ import autogen import subprocess from autogen import ConversableAgent, AssistantAgent, UserProxyAgent -import bot_tools -from git_utils import ( +from src import bot_tools +from src.git_utils import ( get_github_client, get_repository, get_issue_comments, @@ -14,7 +14,7 @@ from github.Issue import Issue import random import string -import triggers +from src import triggers from urlextract import URLExtract diff --git a/src/bot_tools.py b/src/bot_tools.py index ec89bf2..3d78c0a 100644 --- a/src/bot_tools.py +++ b/src/bot_tools.py @@ -267,3 +267,97 @@ def search_github(query: str) -> str: from git_utils import perform_github_search return perform_github_search(query) + +# Testing utilities + + +def create_mock_issue( + issue_number: int = 1, + title: str = "Test Issue", + body: str = "This is a test issue", + labels: list = None, + user_login: str = "test_user" +) -> dict: + """ + Create a mock issue for testing purposes. + + Args: + issue_number: The issue number + title: The issue title + body: The issue body + labels: List of label names + user_login: Username of issue creator + + Returns: + A dictionary representing a GitHub issue + """ + if labels is None: + labels = ["blech_bot"] + + return { + "number": issue_number, + "title": title, + "body": body, + "labels": [{"name": label} for label in labels], + "user": {"login": user_login}, + "comments_url": f"https://api.github.com/repos/test/test/issues/{issue_number}/comments", + "html_url": f"https://github.com/test/test/issues/{issue_number}" + } + + +def create_mock_comment( + comment_id: int = 1, + body: str = "Test comment", + user_login: str = "test_user", + created_at: str = "2023-01-01T00:00:00Z" +) -> dict: + """ + Create a mock comment for testing purposes. + + Args: + comment_id: The comment ID + body: The comment body + user_login: Username of commenter + created_at: Creation timestamp + + Returns: + A dictionary representing a GitHub comment + """ + return { + "id": comment_id, + "body": body, + "user": {"login": user_login}, + "created_at": created_at, + "html_url": f"https://github.com/test/test/issues/comments/{comment_id}" + } + + +def create_mock_pull_request( + pr_number: int = 1, + title: str = "Test PR", + body: str = "This is a test PR", + branch: str = "test-branch", + user_login: str = "test_user" +) -> dict: + """ + Create a mock pull request for testing purposes. + + Args: + pr_number: The PR number + title: The PR title + body: The PR body + branch: The branch name + user_login: Username of PR creator + + Returns: + A dictionary representing a GitHub pull request + """ + return { + "number": pr_number, + "title": title, + "body": body, + "head": {"ref": branch}, + "user": {"login": user_login}, + "html_url": f"https://github.com/test/test/pull/{pr_number}", + "comments_url": f"https://api.github.com/repos/test/test/issues/{pr_number}/comments" + } diff --git a/src/git_utils.py b/src/git_utils.py index 616bbb3..a7fe82e 100644 --- a/src/git_utils.py +++ b/src/git_utils.py @@ -5,7 +5,7 @@ import os import subprocess import git -from branch_handler import ( +from src.branch_handler import ( get_issue_related_branches, get_current_branch, checkout_branch, diff --git a/src/response_agent.py b/src/response_agent.py index 86cd7c3..15f5715 100644 --- a/src/response_agent.py +++ b/src/response_agent.py @@ -5,20 +5,20 @@ from dotenv import load_dotenv import string -import triggers -from agents import ( +from src import triggers +from src.agents import ( create_user_agent, create_agent, generate_prompt, parse_comments ) from urlextract import URLExtract -import agents +from src import agents from autogen import AssistantAgent -import bot_tools +from src import bot_tools import os -from git_utils import ( +from src.git_utils import ( get_github_client, get_repository, write_issue_response, @@ -35,7 +35,7 @@ from github.Repository import Repository from github.Issue import Issue from github.PullRequest import PullRequest -from branch_handler import ( +from src.branch_handler import ( checkout_branch, back_to_master_branch, delete_branch diff --git a/src/triggers.py b/src/triggers.py index d5f1da4..430ee60 100644 --- a/src/triggers.py +++ b/src/triggers.py @@ -2,7 +2,7 @@ Functions to check specific conditions """ from github import Issue -from git_utils import get_issue_comments +from src.git_utils import get_issue_comments def has_blech_bot_tag(issue: Issue) -> bool: @@ -131,7 +131,7 @@ def has_user_comment_on_pr(issue: Issue) -> bool: Returns: True if there is a user comment on a PR that needs processing """ - from git_utils import has_linked_pr, get_linked_pr + from src.git_utils import has_linked_pr, get_linked_pr # First check issue comments comments = get_issue_comments(issue) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..0d6f850 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,2 @@ +# Tests package +# This file is required to make Python treat the directory as a package. diff --git a/tests/test_detailed_responses.py b/tests/test_detailed_responses.py new file mode 100644 index 0000000..c9d6c53 --- /dev/null +++ b/tests/test_detailed_responses.py @@ -0,0 +1,325 @@ +""" +Tests for detailed issue and PR handling in response_agent.py +""" +from src.bot_tools import create_mock_issue, create_mock_pull_request +import src.response_agent as response_agent +import os +import sys +import pytest +from unittest.mock import MagicMock, patch + +# Add the src directory to the path +src_dir = os.path.join(os.path.dirname( + os.path.dirname(os.path.abspath(__file__))), 'src') +sys.path.append(src_dir) + +# Import after adding src to path + +# Mock classes from test_response_agent.py + + +class MockLabel: + def __init__(self, name): + self.name = name + + +class MockUser: + def __init__(self, login): + self.login = login + + +class MockComment: + def __init__(self, data): + self.id = data["id"] + self.body = data["body"] + self.user = MockUser(data["user"]["login"]) + self.created_at = data["created_at"] + self.html_url = data.get("html_url", "") + + +class MockIssue: + def __init__(self, data): + self.number = data["number"] + self.title = data["title"] + self.body = data["body"] + self.labels = [MockLabel(label["name"]) for label in data["labels"]] + self.user = MockUser(data["user"]["login"]) + self.comments_url = data["comments_url"] + self.html_url = data["html_url"] + self._comments = [] + + # Add additional attributes from data if they exist + self.state = data.get("state", "open") + self.created_at = data.get("created_at", "2023-01-01T00:00:00Z") + self.updated_at = data.get("updated_at", "2023-01-01T01:00:00Z") + self.closed_at = data.get("closed_at") + self.repository_url = data.get( + "repository_url", "https://api.github.com/repos/test/test") + self.assignees = data.get("assignees", []) + self.milestone = data.get("milestone") + self.locked = data.get("locked", False) + self.active_lock_reason = data.get("active_lock_reason") + self.pull_request = data.get("pull_request") + self.author_association = data.get("author_association", "CONTRIBUTOR") + self.reactions = data.get("reactions", {"total_count": 0}) + + # Store the original data for reference + self._data = data + + def get_comments(self): + return self._comments + + def add_to_labels(self, label_name): + self.labels.append(MockLabel(label_name)) + + def create_issue_comment(self, body): + comment = MockComment({ + "id": len(self._comments) + 1, + "body": body, + "user": {"login": "blech_bot"}, + "created_at": "2023-01-01T00:00:00Z" + }) + self._comments.append(comment) + return comment + + def to_dict(self): + """Convert the mock issue to a dictionary for testing get_issue_details""" + return self._data + + +class MockRef: + def __init__(self, ref): + self.ref = ref + + +class MockPullRequest(MockIssue): + def __init__(self, data): + super().__init__(data) + self.head = MockRef(data["head"]["ref"]) + self.base = MockRef(data.get("base", {}).get("ref", "main")) + self.merged = data.get("merged", False) + self.mergeable = data.get("mergeable", True) + self.merged_at = data.get("merged_at") + self.merge_commit_sha = data.get("merge_commit_sha") + self.draft = data.get("draft", False) + self.additions = data.get("additions", 0) + self.deletions = data.get("deletions", 0) + self.changed_files = data.get("changed_files", 0) + + +class MockRepository: + def __init__(self, name="test/test"): + self.full_name = name + self.default_branch = "main" + + def get_pull(self, number): + return MockPullRequest(create_mock_pull_request(number)) + +# Test fixtures + + +@pytest.fixture +def mock_detailed_issue(): + """Create a mock issue with all details needed for get_issue_details""" + return MockIssue(create_mock_issue( + title="[blech_bot] Detailed test issue", + body="This is a detailed test issue with all required fields", + detailed=True, + created_at="2023-01-01T00:00:00Z", + updated_at="2023-01-02T00:00:00Z" + )) + + +@pytest.fixture +def mock_detailed_pr(): + """Create a mock PR with all details needed for testing""" + return MockPullRequest(create_mock_pull_request( + title="[blech_bot] Detailed test PR", + body="This is a detailed test PR with all required fields", + detailed=True, + created_at="2023-01-01T00:00:00Z", + updated_at="2023-01-02T00:00:00Z" + )) + +# Tests for detailed issue handling + + +@patch('src.git_utils.get_issue_comments') +def test_get_issue_details_with_detailed_issue(mock_get_comments, mock_detailed_issue): + """Test that get_issue_details works with a detailed mock issue""" + mock_get_comments.return_value = [] + + # Directly patch the get_issue_details function + with patch('src.response_agent.get_issue_details', autospec=True) as mock_get_details: + # Configure the mock to return a dictionary based on our detailed issue + expected_details = { + 'number': mock_detailed_issue.number, + 'title': mock_detailed_issue.title, + 'body': mock_detailed_issue.body, + 'state': mock_detailed_issue.state, + 'created_at': mock_detailed_issue.created_at, + 'updated_at': mock_detailed_issue.updated_at, + 'user': {'login': mock_detailed_issue.user.login}, + 'labels': [{'name': label.name} for label in mock_detailed_issue.labels], + 'comments': [] + } + mock_get_details.return_value = expected_details + + # Call the function + details = response_agent.get_issue_details(mock_detailed_issue) + + # Verify the result + assert details == expected_details + mock_get_details.assert_called_once_with(mock_detailed_issue) + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.is_pull_request') +@patch('response_agent.check_triggers') +@patch('response_agent.response_selector') +def test_process_detailed_issue_new_response( + mock_response_selector, mock_check_triggers, mock_is_pr, + mock_has_tag, mock_get_repo_path, mock_get_repo, mock_get_client, + mock_detailed_issue +): + """Test processing a detailed issue with a new response""" + # Setup mocks + mock_is_pr.return_value = False + mock_has_tag.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + mock_check_triggers.return_value = "new_response" + + # Mock response function + mock_response_func = MagicMock() + mock_response_func.return_value = ("Detailed test response", [ + "Detailed test response"]) + mock_response_selector.return_value = mock_response_func + + # Mock write_issue_response + with patch('response_agent.write_issue_response') as mock_write_response: + success, _ = response_agent.process_issue( + mock_detailed_issue, "test/test") + + # Verify success and that write_issue_response was called with the detailed response + assert success is True + mock_write_response.assert_called_once() + mock_response_func.assert_called_once_with( + mock_detailed_issue, "test/test") + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.is_pull_request') +@patch('response_agent.triggers.has_develop_issue_trigger') +def test_process_detailed_issue_develop_flow( + mock_has_develop, mock_is_pr, mock_has_tag, + mock_get_repo_path, mock_get_repo, mock_get_client, + mock_detailed_issue +): + """Test processing a detailed issue with develop flow""" + # Setup mocks + mock_is_pr.return_value = False + mock_has_tag.return_value = True + mock_has_develop.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock develop_issue_flow + with patch('response_agent.develop_issue_flow') as mock_develop_flow: + mock_develop_flow.return_value = (True, None) + + success, _ = response_agent.process_issue( + mock_detailed_issue, "test/test") + + # Verify success and that develop_issue_flow was called + assert success is True + mock_develop_flow.assert_called_once_with( + mock_detailed_issue, "test/test", is_pr=False) + +# Tests for detailed PR handling + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.is_pull_request') +def test_process_detailed_pr_flow( + mock_is_pr, mock_has_tag, mock_get_repo_path, + mock_get_repo, mock_get_client, mock_detailed_pr +): + """Test processing a detailed PR""" + # Setup mocks + mock_is_pr.return_value = True + mock_has_tag.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock standalone_pr_flow + with patch('response_agent.standalone_pr_flow') as mock_pr_flow: + mock_pr_flow.return_value = (True, None) + + success, _ = response_agent.process_issue( + mock_detailed_pr, "test/test") + + # Verify success and that standalone_pr_flow was called with the detailed PR + assert success is True + mock_pr_flow.assert_called_once_with(mock_detailed_pr, "test/test") + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.is_pull_request') +@patch('response_agent.get_pr_branch') +def test_standalone_pr_flow_with_detailed_pr( + mock_get_pr_branch, mock_is_pr, mock_has_tag, + mock_get_repo_path, mock_get_repo, mock_get_client, + mock_detailed_pr +): + """Test standalone PR flow with a detailed PR""" + # Setup mocks + mock_is_pr.return_value = True + mock_has_tag.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + mock_get_pr_branch.return_value = "test-branch" + + # Mock the necessary functions for standalone_pr_flow + with patch('response_agent.checkout_branch') as mock_checkout: + with patch('response_agent.summarize_relevant_comments') as mock_summarize: + mock_summarize.return_value = ([], [], "Test summary") + + with patch('response_agent.generate_edit_command_response') as mock_generate: + mock_generate.return_value = ( + "Test response", ["Test response"]) + + with patch('response_agent.run_aider') as mock_run_aider: + mock_run_aider.return_value = "Aider output" + + with patch('response_agent.push_changes_with_authentication') as mock_push: + mock_push.return_value = (True, None) + + with patch('response_agent.write_pr_comment') as mock_write_comment: + with patch('response_agent.back_to_master_branch') as mock_back: + + # Call the function through process_issue + with patch('response_agent.standalone_pr_flow', wraps=response_agent.standalone_pr_flow) as wrapped_flow: + success, _ = response_agent.process_issue( + mock_detailed_pr, "test/test") + + # Verify success + assert success is True + wrapped_flow.assert_called_once_with( + mock_detailed_pr, "test/test") diff --git a/tests/test_response_agent.py b/tests/test_response_agent.py new file mode 100644 index 0000000..50043bb --- /dev/null +++ b/tests/test_response_agent.py @@ -0,0 +1,306 @@ +""" +Tests for the response_agent.py module +""" +import src.response_agent as response_agent +from src.bot_tools import create_mock_issue, create_mock_comment, create_mock_pull_request +from src import bot_tools +import os +import sys +import pytest +from unittest.mock import MagicMock, patch + +# Add the src directory to the path +src_dir = os.path.join(os.path.dirname( + os.path.dirname(os.path.abspath(__file__))), 'src') +sys.path.append(src_dir) + +# Import after adding src to path + + +# Mock GitHub objects + +class MockIssue: + def __init__(self, data): + self.number = data["number"] + self.title = data["title"] + self.body = data["body"] + self.labels = [MockLabel(label["name"]) for label in data["labels"]] + self.user = MockUser(data["user"]["login"]) + self.comments_url = data["comments_url"] + self.html_url = data["html_url"] + self._comments = [] + + def get_comments(self): + return self._comments + + def add_to_labels(self, label_name): + self.labels.append(MockLabel(label_name)) + + def create_issue_comment(self, body): + comment = MockComment({ + "id": len(self._comments) + 1, + "body": body, + "user": {"login": "blech_bot"}, + "created_at": "2023-01-01T00:00:00Z" + }) + self._comments.append(comment) + return comment + + +class MockLabel: + def __init__(self, name): + self.name = name + + +class MockUser: + def __init__(self, login): + self.login = login + + +class MockComment: + def __init__(self, data): + self.id = data["id"] + self.body = data["body"] + self.user = MockUser(data["user"]["login"]) + self.created_at = data["created_at"] + self.html_url = data.get("html_url", "") + + +class MockPullRequest(MockIssue): + def __init__(self, data): + super().__init__(data) + self.head = MockRef(data["head"]["ref"]) + + +class MockRef: + def __init__(self, ref): + self.ref = ref + + +class MockRepository: + def __init__(self, name="test/test"): + self.full_name = name + self.default_branch = "main" + + def get_pull(self, number): + return MockPullRequest(create_mock_pull_request(number)) + +# Test fixtures + + +@pytest.fixture +def mock_github_client(): + client = MagicMock() + client.get_repo.return_value = MockRepository() + return client + + +@pytest.fixture +def mock_issue(): + return MockIssue(create_mock_issue()) + + +@pytest.fixture +def mock_issue_with_feedback(): + issue = MockIssue(create_mock_issue()) + # Add bot response + issue._comments.append(MockComment(create_mock_comment( + body="Initial response\n\n---\n*This response was automatically generated by blech_bot using model gpt-4o*", + user_login="blech_bot" + ))) + # Add user feedback + issue._comments.append(MockComment(create_mock_comment( + body="Can you explain more about X?", + user_login="test_user" + ))) + return issue + + +@pytest.fixture +def mock_issue_with_edit_command(): + issue = MockIssue(create_mock_issue( + title="[blech_bot] Fix bug in function X", + body="Please generate an edit command to fix the bug in function X" + )) + issue.labels.append(MockLabel("blech_bot")) + return issue + + +@pytest.fixture +def mock_pr(): + return MockPullRequest(create_mock_pull_request()) + +# Tests for response generation + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +def test_generate_new_response(mock_get_repo_path, mock_get_repo, mock_get_client, mock_issue): + # Setup mocks + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock the LLM response + with patch('response_agent.create_agent') as mock_create_agent: + mock_agent = MagicMock() + mock_agent.initiate_chat.return_value.chat_history = [ + {"role": "assistant", "content": "Test response"} + ] + mock_create_agent.return_value = mock_agent + + # Test the function + with patch('response_agent.get_issue_details') as mock_get_details: + mock_get_details.return_value = { + "title": "Test Issue", "body": "Test body"} + response, _ = response_agent.generate_new_response( + mock_issue, "test/test") + + # Verify response contains signature + assert "Test response" in response + assert "*This response was automatically generated by blech_bot" in response + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +def test_generate_feedback_response(mock_get_repo_path, mock_get_repo, mock_get_client, mock_issue_with_feedback): + # Setup mocks + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock the LLM response + with patch('response_agent.create_agent') as mock_create_agent: + mock_agent = MagicMock() + mock_agent.initiate_chat.return_value.chat_history = [ + {"role": "assistant", "content": "Updated response with more details"} + ] + mock_create_agent.return_value = mock_agent + + # Test the function + with patch('response_agent.get_issue_details') as mock_get_details: + mock_get_details.return_value = { + "title": "Test Issue", "body": "Test body"} + with patch('response_agent.get_issue_comments') as mock_get_comments: + mock_get_comments.return_value = mock_issue_with_feedback._comments + response, _ = response_agent.generate_feedback_response( + mock_issue_with_feedback, "test/test") + + # Verify response contains signature + assert "Updated response with more details" in response + assert "*This response was automatically generated by blech_bot" in response + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +def test_generate_edit_command_response(mock_get_repo_path, mock_get_repo, mock_get_client, mock_issue_with_edit_command): + # Setup mocks + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock the LLM response + with patch('response_agent.create_agent') as mock_create_agent: + mock_agent = MagicMock() + mock_agent.initiate_chat.return_value.chat_history = [ + {"role": "assistant", "content": "Edit command to fix function X:\n```python\ndef fix_function_x():\n return 'fixed'\n```"} + ] + mock_create_agent.return_value = mock_agent + + # Test the function + with patch('response_agent.get_issue_details') as mock_get_details: + mock_get_details.return_value = { + "title": "Test Issue", "body": "Test body"} + response, _ = response_agent.generate_edit_command_response( + mock_issue_with_edit_command, "test/test") + + # Verify response contains signature + assert "Edit command to fix function X" in response + assert "*This response was automatically generated by blech_bot" in response + +# Tests for issue/PR processing flows + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +def test_process_issue_new_response(mock_has_tag, mock_get_repo_path, mock_get_repo, mock_get_client, mock_issue): + # Setup mocks + mock_has_tag.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock check_triggers and response_selector + with patch('response_agent.check_triggers') as mock_check_triggers: + mock_check_triggers.return_value = "new_response" + with patch('response_agent.response_selector') as mock_response_selector: + mock_response_func = MagicMock() + mock_response_func.return_value = ( + "Test response", ["Test response"]) + mock_response_selector.return_value = mock_response_func + + # Mock write_issue_response + with patch('response_agent.write_issue_response') as mock_write_response: + success, _ = response_agent.process_issue( + mock_issue, "test/test") + + # Verify success and that write_issue_response was called + assert success is True + mock_write_response.assert_called_once() + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.triggers.has_develop_issue_trigger') +@patch('response_agent.is_pull_request') +def test_process_issue_develop_flow(mock_is_pr, mock_has_develop, mock_has_tag, + mock_get_repo_path, mock_get_repo, mock_get_client, mock_issue): + # Setup mocks + mock_is_pr.return_value = False + mock_has_tag.return_value = True + mock_has_develop.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock develop_issue_flow + with patch('response_agent.develop_issue_flow') as mock_develop_flow: + mock_develop_flow.return_value = (True, None) + + success, _ = response_agent.process_issue(mock_issue, "test/test") + + # Verify success and that develop_issue_flow was called + assert success is True + mock_develop_flow.assert_called_once_with( + mock_issue, "test/test", is_pr=False) + + +@patch('response_agent.get_github_client') +@patch('response_agent.get_repository') +@patch('response_agent.bot_tools.get_local_repo_path') +@patch('response_agent.triggers.has_blech_bot_tag') +@patch('response_agent.is_pull_request') +def test_process_pr_flow(mock_is_pr, mock_has_tag, mock_get_repo_path, mock_get_repo, mock_get_client, mock_pr): + # Setup mocks + mock_is_pr.return_value = True + mock_has_tag.return_value = True + mock_get_repo_path.return_value = "/tmp/test_repo" + mock_get_repo.return_value = MockRepository() + mock_get_client.return_value = MagicMock() + + # Mock standalone_pr_flow + with patch('response_agent.standalone_pr_flow') as mock_pr_flow: + mock_pr_flow.return_value = (True, None) + + success, _ = response_agent.process_issue(mock_pr, "test/test") + + # Verify success and that standalone_pr_flow was called + assert success is True + mock_pr_flow.assert_called_once_with(mock_pr, "test/test")