Test Fixes: - Fix mock_config fixture to use 'owner/repo' format (was separate fields) - Update test_client_initialization to match current client API - Add required 'org' argument to get_org_labels, list_repos, aggregate_issues tests - Update error message assertion in test_no_repo_specified_error - Fix test_create_issue to mock is_org_repo and label resolution - Update aggregate_issues tests in test_issues.py with org argument Documentation Updates: - Expand tools table from 8 to 36 tools (organized by category) - Update directory structure to show all 6 tool files - Remove unused GITEA_OWNER from configuration docs - Add automatic repository detection documentation - Add project directory detection strategies - Update test count from 42 to 64 - Create CHANGELOG.md with full version history All 64 tests now pass. No production code changes. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
164 lines
5.8 KiB
Python
164 lines
5.8 KiB
Python
"""
|
|
Unit tests for issue tools with branch detection.
|
|
"""
|
|
import pytest
|
|
from unittest.mock import Mock, patch, AsyncMock
|
|
from mcp_server.tools.issues import IssueTools
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_gitea_client():
|
|
"""Fixture providing mocked Gitea client"""
|
|
client = Mock()
|
|
client.mode = 'project'
|
|
return client
|
|
|
|
|
|
@pytest.fixture
|
|
def issue_tools(mock_gitea_client):
|
|
"""Fixture providing IssueTools instance"""
|
|
return IssueTools(mock_gitea_client)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_issues_development_branch(issue_tools):
|
|
"""Test listing issues on development branch (allowed)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='feat/test-feature'):
|
|
issue_tools.gitea.list_issues = Mock(return_value=[{'number': 1}])
|
|
|
|
issues = await issue_tools.list_issues(state='open')
|
|
|
|
assert len(issues) == 1
|
|
issue_tools.gitea.list_issues.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_issue_development_branch(issue_tools):
|
|
"""Test creating issue on development branch (allowed)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='development'):
|
|
issue_tools.gitea.create_issue = Mock(return_value={'number': 1})
|
|
|
|
issue = await issue_tools.create_issue('Test', 'Body')
|
|
|
|
assert issue['number'] == 1
|
|
issue_tools.gitea.create_issue.assert_called_once()
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_issue_main_branch_blocked(issue_tools):
|
|
"""Test creating issue on main branch (blocked)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='main'):
|
|
with pytest.raises(PermissionError) as exc_info:
|
|
await issue_tools.create_issue('Test', 'Body')
|
|
|
|
assert "Cannot create issues on branch 'main'" in str(exc_info.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_create_issue_staging_branch_allowed(issue_tools):
|
|
"""Test creating issue on staging branch (allowed for documentation)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='staging'):
|
|
issue_tools.gitea.create_issue = Mock(return_value={'number': 1})
|
|
|
|
issue = await issue_tools.create_issue('Test', 'Body')
|
|
|
|
assert issue['number'] == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_update_issue_main_branch_blocked(issue_tools):
|
|
"""Test updating issue on main branch (blocked)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='main'):
|
|
with pytest.raises(PermissionError) as exc_info:
|
|
await issue_tools.update_issue(1, title='Updated')
|
|
|
|
assert "Cannot update issues on branch 'main'" in str(exc_info.value)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_list_issues_main_branch_allowed(issue_tools):
|
|
"""Test listing issues on main branch (allowed - read-only)"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='main'):
|
|
issue_tools.gitea.list_issues = Mock(return_value=[{'number': 1}])
|
|
|
|
issues = await issue_tools.list_issues(state='open')
|
|
|
|
assert len(issues) == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_get_issue(issue_tools):
|
|
"""Test getting specific issue"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='development'):
|
|
issue_tools.gitea.get_issue = Mock(return_value={'number': 1, 'title': 'Test'})
|
|
|
|
issue = await issue_tools.get_issue(1)
|
|
|
|
assert issue['number'] == 1
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_add_comment(issue_tools):
|
|
"""Test adding comment to issue"""
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='development'):
|
|
issue_tools.gitea.add_comment = Mock(return_value={'body': 'Test comment'})
|
|
|
|
comment = await issue_tools.add_comment(1, 'Test comment')
|
|
|
|
assert comment['body'] == 'Test comment'
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_aggregate_issues_company_mode(issue_tools):
|
|
"""Test aggregating issues in company mode"""
|
|
issue_tools.gitea.mode = 'company'
|
|
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='development'):
|
|
issue_tools.gitea.aggregate_issues = Mock(return_value={
|
|
'repo1': [{'number': 1}],
|
|
'repo2': [{'number': 2}]
|
|
})
|
|
|
|
aggregated = await issue_tools.aggregate_issues(org='test_owner')
|
|
|
|
assert 'repo1' in aggregated
|
|
assert 'repo2' in aggregated
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_aggregate_issues_project_mode(issue_tools):
|
|
"""Test that aggregate_issues works in project mode with org argument"""
|
|
issue_tools.gitea.mode = 'project'
|
|
|
|
with patch.object(issue_tools, '_get_current_branch', return_value='development'):
|
|
issue_tools.gitea.aggregate_issues = Mock(return_value={
|
|
'repo1': [{'number': 1}]
|
|
})
|
|
|
|
# aggregate_issues now works in any mode when org is provided
|
|
aggregated = await issue_tools.aggregate_issues(org='test_owner')
|
|
|
|
assert 'repo1' in aggregated
|
|
|
|
|
|
def test_branch_detection():
|
|
"""Test branch detection logic"""
|
|
tools = IssueTools(Mock())
|
|
|
|
# Test development branches
|
|
with patch.object(tools, '_get_current_branch', return_value='development'):
|
|
assert tools._check_branch_permissions('create_issue') is True
|
|
|
|
with patch.object(tools, '_get_current_branch', return_value='feat/new-feature'):
|
|
assert tools._check_branch_permissions('create_issue') is True
|
|
|
|
# Test production branches
|
|
with patch.object(tools, '_get_current_branch', return_value='main'):
|
|
assert tools._check_branch_permissions('create_issue') is False
|
|
assert tools._check_branch_permissions('list_issues') is True
|
|
|
|
# Test staging branches
|
|
with patch.object(tools, '_get_current_branch', return_value='staging'):
|
|
assert tools._check_branch_permissions('create_issue') is True
|
|
assert tools._check_branch_permissions('update_issue') is False
|