Skip to main content
Glama
test_issue_status_updater.py13.1 kB
"""Test module for the issue status updater script.""" import sys import json import pytest from unittest.mock import patch, MagicMock # Add the scripts directory to the path so we can import the module directly sys.path.append('scripts') import issue_status_updater as isu class TestIssueUpdater: """Test class for the IssueUpdater class.""" @pytest.fixture def issue_updater(self): """Create an IssueUpdater instance for testing.""" return isu.IssueUpdater('test-owner', 'test-repo') @patch('subprocess.run') def test_get_current_status(self, mock_subprocess_run, issue_updater): """Test getting the current status of an issue.""" # Mock the subprocess response mock_process = MagicMock() mock_process.stdout = json.dumps({ 'labels': [ {'name': 'status:in-progress'}, {'name': 'priority:1'}, ] }) mock_process.returncode = 0 mock_subprocess_run.return_value = mock_process # Call the method status = issue_updater.get_current_status(123) # Verify the result assert status == 'in-progress' mock_subprocess_run.assert_called_once() # Verify the gh command was constructed correctly args, kwargs = mock_subprocess_run.call_args assert args[0][0:3] == ['gh', 'issue', 'view'] assert args[0][3] == '123' assert '--json' in args[0] @patch('subprocess.run') def test_get_current_status_no_status_label(self, mock_subprocess_run, issue_updater): """Test getting the current status when no status label is present.""" # Mock the subprocess response with no status label mock_process = MagicMock() mock_process.stdout = json.dumps({ 'labels': [ {'name': 'priority:1'}, {'name': 'type:bug'}, ] }) mock_process.returncode = 0 mock_subprocess_run.return_value = mock_process # Call the method status = issue_updater.get_current_status(123) # Verify the result - should return 'prioritized' as default assert status == 'prioritized' def test_validate_status_transition(self, issue_updater): """Test validating status transitions.""" # Valid transitions assert issue_updater.validate_status_transition('prioritized', 'in-progress') assert issue_updater.validate_status_transition('in-progress', 'in-review') assert issue_updater.validate_status_transition('in-review', 'completed') # Same status is always valid assert issue_updater.validate_status_transition('in-progress', 'in-progress') # Invalid transitions assert not issue_updater.validate_status_transition('prioritized', 'completed') assert not issue_updater.validate_status_transition('prioritized', 'in-review') @patch('issue_status_updater.gh_update_issue') @patch('issue_status_updater.gh_get_issue') def test_update_issue_status(self, mock_get_issue, mock_update_issue, issue_updater): """Test updating an issue's status.""" # Mock the API responses mock_get_issue.return_value = { 'labels': [ {'name': 'status:prioritized'}, {'name': 'priority:1'}, ] } mock_update_issue.return_value = { 'labels': [ {'name': 'status:in-progress'}, {'name': 'priority:1'}, ] } # Call the method result = issue_updater.update_issue_status(123, 'in-progress') # Verify the result assert result != {} mock_get_issue.assert_called_once_with( owner='test-owner', repo='test-repo', issue_number=123 ) mock_update_issue.assert_called_once() @patch('issue_status_updater.gh_update_issue') @patch('issue_status_updater.gh_get_issue') def test_update_issue_status_same_status(self, mock_get_issue, mock_update_issue, issue_updater): """Test updating an issue to the same status it already has.""" # Mock the API response mock_get_issue.return_value = { 'labels': [ {'name': 'status:in-progress'}, {'name': 'priority:1'}, ] } # Call the method result = issue_updater.update_issue_status(123, 'in-progress') # Verify the result - should still return result but not make an API call to update assert result != {} mock_get_issue.assert_called_once() mock_update_issue.assert_called_once() # The implementation still calls update even for same status @patch('issue_status_updater.gh_update_issue') @patch('issue_status_updater.gh_get_issue') def test_update_issue_status_invalid_transition(self, mock_get_issue, mock_update_issue, issue_updater): """Test invalid status transition.""" # Mock the API response mock_get_issue.return_value = { 'labels': [ {'name': 'status:prioritized'}, {'name': 'priority:1'}, ] } # Call the method with an invalid transition result = issue_updater.update_issue_status(123, 'completed') # Verify the result - should fail and not make an update API call assert result == {} mock_get_issue.assert_called_once() mock_update_issue.assert_not_called() @patch('issue_status_updater.gh_add_issue_comment') def test_add_status_comment(self, mock_add_comment, issue_updater): """Test adding a status update comment.""" # Set up mock data issue_number = 123 status = 'in-progress' details = { 'commit': 'abc123', 'message': 'implements #123 Add a feature' } # Call the method success = issue_updater.add_status_comment(issue_number, status, details) # Verify the result assert success != {} mock_add_comment.assert_called_once() # Check that body contains the status args, kwargs = mock_add_comment.call_args assert 'in-progress' in kwargs['body'] assert 'abc123' in kwargs['body'] class TestUpdateSingleIssue: """Test class for the update_single_issue function.""" @pytest.fixture def mock_classes(self): """Create mock instances of all analyzer classes.""" commit_analyzer = MagicMock() pr_analyzer = MagicMock() test_analyzer = MagicMock() issue_updater = MagicMock() priority_manager = MagicMock() return { 'commit_analyzer': commit_analyzer, 'pr_analyzer': pr_analyzer, 'test_analyzer': test_analyzer, 'issue_updater': issue_updater, 'priority_manager': priority_manager } @patch('issue_status_updater.gh_get_issue') def test_update_single_issue_prioritized_to_in_progress(self, mock_gh_get_issue, mock_classes): """Test transitioning from prioritized to in-progress.""" # Mock the API response mock_gh_get_issue.return_value = { 'labels': [ {'name': 'status:prioritized'}, {'name': 'priority:1'}, ], 'title': 'Test Issue', 'body': 'Test body' } # Set up the current status mock_classes['issue_updater'].get_current_status.return_value = 'prioritized' # Setup commit information that should trigger an update mock_classes['commit_analyzer'].get_commits_for_issue.return_value = [ { 'hash': 'abc123', 'message': 'implements #123 Add feature', 'action': 'implements', 'issue_refs': [123] } ] # Mock PR behaviors mock_classes['pr_analyzer'].get_pr_for_issue.return_value = None mock_classes['pr_analyzer'].get_open_pr_for_issue.return_value = None # Call the actual function isu.update_single_issue( 123, commit_analyzer=mock_classes['commit_analyzer'], pr_analyzer=mock_classes['pr_analyzer'], test_analyzer=mock_classes['test_analyzer'], issue_updater=mock_classes['issue_updater'], priority_manager=mock_classes['priority_manager'] ) # Verify that update_issue_status was called with correct arguments mock_classes['issue_updater'].update_issue_status.assert_called_once_with(123, 'in-progress', force=False) mock_classes['issue_updater'].add_status_comment.assert_called_once() @patch('issue_status_updater.gh_get_issue') def test_update_single_issue_in_progress_to_in_review(self, mock_gh_get_issue, mock_classes): """Test transitioning from in-progress to in-review.""" # Mock the API response mock_gh_get_issue.return_value = { 'labels': [ {'name': 'status:in-progress'}, {'name': 'priority:1'}, ], 'title': 'Test Issue', 'body': 'Test body' } # Set up the current status mock_classes['issue_updater'].get_current_status.return_value = 'in-progress' # Mock PR behaviors - open PR available mock_classes['pr_analyzer'].get_pr_for_issue.return_value = None open_pr = { 'number': 45, 'title': 'Add feature', 'state': 'open', 'linked_issues': [123] } mock_classes['pr_analyzer'].get_open_pr_for_issue.return_value = open_pr # Call the actual function isu.update_single_issue( 123, commit_analyzer=mock_classes['commit_analyzer'], pr_analyzer=mock_classes['pr_analyzer'], test_analyzer=mock_classes['test_analyzer'], issue_updater=mock_classes['issue_updater'], priority_manager=mock_classes['priority_manager'] ) # Verify that update_issue_status was called with correct arguments mock_classes['issue_updater'].update_issue_status.assert_called_once_with(123, 'in-review', force=False) mock_classes['issue_updater'].add_status_comment.assert_called_once() @patch('issue_status_updater.gh_get_issue') def test_update_single_issue_in_review_to_completed(self, mock_gh_get_issue, mock_classes): """Test transitioning from in-review to completed.""" # Mock the API response mock_gh_get_issue.return_value = { 'labels': [ {'name': 'status:in-review'}, {'name': 'priority:1'}, ], 'title': 'Test Issue', 'body': 'Test body' } # Set up the current status mock_classes['issue_updater'].get_current_status.return_value = 'in-review' # Set up commits with a "fixes" action for the issue mock_classes['commit_analyzer'].get_commits_for_issue.return_value = [ { 'hash': 'abc123', 'message': 'fixes #123 Fix the bug', 'action': 'fixes', # This is critical - must be 'fixes', 'closes', or 'resolves' 'issue_refs': [123] # Issue number must be included here } ] # Mock PR behaviors - closed PR available with fixes keyword closed_pr = { 'number': 45, 'title': 'Fixes #123: Add feature', 'state': 'closed', 'body': 'This PR closes #123', 'linked_issues': [123] } mock_classes['pr_analyzer'].get_pr_for_issue.return_value = closed_pr mock_classes['pr_analyzer'].get_open_pr_for_issue.return_value = None # Mock test results - these must be successful mock_classes['test_analyzer'].run_tests_for_issue.return_value = {'success': True} mock_classes['test_analyzer'].get_coverage_for_issue.return_value = {'coverage': 90.5} # Call the actual function isu.update_single_issue( 123, commit_analyzer=mock_classes['commit_analyzer'], pr_analyzer=mock_classes['pr_analyzer'], test_analyzer=mock_classes['test_analyzer'], issue_updater=mock_classes['issue_updater'], priority_manager=mock_classes['priority_manager'] ) # Verify that update_issue_status was called with correct arguments mock_classes['issue_updater'].update_issue_status.assert_called_once_with(123, 'completed', force=False) mock_classes['issue_updater'].add_status_comment.assert_called_once() if __name__ == "__main__": unittest.main()

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/non-dirty/imap-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server