PyGithub MCP Server

"""Issue labels integration tests. This module tests the issue label operations using the real GitHub API. """ import pytest from datetime import datetime from pygithub_mcp_server.operations.issues import ( create_issue, update_issue, add_issue_labels, remove_issue_label, ) from pygithub_mcp_server.schemas.issues import ( CreateIssueParams, UpdateIssueParams, AddIssueLabelsParams, RemoveIssueLabelParams, ) @pytest.mark.integration def test_add_issue_labels(test_owner, test_repo_name, unique_id, with_retry): """Test adding labels to an issue.""" # Setup owner = test_owner repo = test_repo_name title = f"Test Issue (Add Labels) {unique_id}" # Create an issue @with_retry def create_test_issue(): return create_issue(CreateIssueParams( owner=owner, repo=repo, title=title )) issue = create_test_issue() try: # Add labels @with_retry def add_test_labels(): return add_issue_labels(AddIssueLabelsParams( owner=owner, repo=repo, issue_number=issue["issue_number"], labels=["bug", "enhancement"] )) labels = add_test_labels() # Verify assert isinstance(labels, list) assert len(labels) >= 2 label_names = [label["name"] for label in labels] assert "bug" in label_names assert "enhancement" in label_names finally: # Cleanup try: @with_retry def close_issue(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"], state="closed" )) close_issue() except Exception as e: print(f"Failed to close issue during cleanup: {e}") @pytest.mark.integration def test_remove_issue_label(test_owner, test_repo_name, unique_id, with_retry): """Test removing a label from an issue.""" # Setup owner = test_owner repo = test_repo_name title = f"Test Issue (Remove Label) {unique_id}" # Create an issue with labels @with_retry def create_test_issue(): issue = create_issue(CreateIssueParams( owner=owner, repo=repo, title=title )) add_issue_labels(AddIssueLabelsParams( owner=owner, repo=repo, issue_number=issue["issue_number"], labels=["bug", "enhancement"] )) return issue issue = create_test_issue() try: # Verify labels were added @with_retry def get_issue_with_labels(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) # Using update with no changes to get current state issue_with_labels = get_issue_with_labels() label_names = [label["name"] for label in issue_with_labels["labels"]] assert "bug" in label_names assert "enhancement" in label_names # Remove one label @with_retry def remove_test_label(): return remove_issue_label(RemoveIssueLabelParams( owner=owner, repo=repo, issue_number=issue["issue_number"], label="bug" )) remove_test_label() # Verify label was removed @with_retry def get_issue_after_remove(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) # Using update with no changes to get current state issue_after_remove = get_issue_after_remove() label_names_after = [label["name"] for label in issue_after_remove["labels"]] assert "bug" not in label_names_after assert "enhancement" in label_names_after finally: # Cleanup try: @with_retry def close_issue(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"], state="closed" )) close_issue() except Exception as e: print(f"Failed to close issue during cleanup: {e}") @pytest.mark.integration def test_add_issue_labels_multiple_calls(test_owner, test_repo_name, unique_id, with_retry): """Test adding labels to an issue with multiple calls.""" # Setup owner = test_owner repo = test_repo_name title = f"Test Issue (Add Labels Multiple) {unique_id}" # Create an issue @with_retry def create_test_issue(): return create_issue(CreateIssueParams( owner=owner, repo=repo, title=title )) issue = create_test_issue() try: # Add first label @with_retry def add_first_label(): return add_issue_labels(AddIssueLabelsParams( owner=owner, repo=repo, issue_number=issue["issue_number"], labels=["bug"] )) first_labels = add_first_label() # Verify assert isinstance(first_labels, list) assert any(label["name"] == "bug" for label in first_labels) # Add second label @with_retry def add_second_label(): return add_issue_labels(AddIssueLabelsParams( owner=owner, repo=repo, issue_number=issue["issue_number"], labels=["enhancement"] )) second_labels = add_second_label() # Verify both labels are present assert isinstance(second_labels, list) label_names = [label["name"] for label in second_labels] assert "bug" in label_names assert "enhancement" in label_names finally: # Cleanup try: @with_retry def close_issue(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"], state="closed" )) close_issue() except Exception as e: print(f"Failed to close issue during cleanup: {e}") @pytest.mark.integration def test_remove_nonexistent_label(test_owner, test_repo_name, unique_id, with_retry): """Test removing a non-existent label from an issue.""" # Setup owner = test_owner repo = test_repo_name title = f"Test Issue (Remove Nonexistent Label) {unique_id}" # Create an issue @with_retry def create_test_issue(): return create_issue(CreateIssueParams( owner=owner, repo=repo, title=title )) issue = create_test_issue() try: # Remove a non-existent label nonexistent_label = f"nonexistent-{unique_id}" @with_retry def remove_nonexistent_label(): # This should not raise an error, GitHub API silently ignores removing non-existent labels return remove_issue_label(RemoveIssueLabelParams( owner=owner, repo=repo, issue_number=issue["issue_number"], label=nonexistent_label )) # This should not raise an exception remove_nonexistent_label() # Verify issue state is still accessible @with_retry def get_issue_after_remove(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) # Using update with no changes to get current state issue_after_remove = get_issue_after_remove() assert issue_after_remove["issue_number"] == issue["issue_number"] finally: # Cleanup try: @with_retry def close_issue(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"], state="closed" )) close_issue() except Exception as e: print(f"Failed to close issue during cleanup: {e}") @pytest.mark.integration def test_label_lifecycle(test_owner, test_repo_name, unique_id, with_retry): """Test complete label lifecycle (add → verify → remove → verify).""" # Setup owner = test_owner repo = test_repo_name title = f"Test Issue (Label Lifecycle) {unique_id}" # Create an issue @with_retry def create_test_issue(): return create_issue(CreateIssueParams( owner=owner, repo=repo, title=title )) issue = create_test_issue() try: # Verify no labels initially assert not issue["labels"], "Issue should not have labels initially" # Add labels @with_retry def add_test_labels(): return add_issue_labels(AddIssueLabelsParams( owner=owner, repo=repo, issue_number=issue["issue_number"], labels=["bug", "enhancement", "documentation"] )) labels = add_test_labels() # Verify labels were added assert len(labels) >= 3 label_names = [label["name"] for label in labels] assert "bug" in label_names assert "enhancement" in label_names assert "documentation" in label_names # Remove one label @with_retry def remove_bug_label(): return remove_issue_label(RemoveIssueLabelParams( owner=owner, repo=repo, issue_number=issue["issue_number"], label="bug" )) remove_bug_label() # Verify label was removed @with_retry def get_issue_after_remove(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) # Using update with no changes to get current state issue_after_remove = get_issue_after_remove() label_names_after = [label["name"] for label in issue_after_remove["labels"]] assert "bug" not in label_names_after assert "enhancement" in label_names_after assert "documentation" in label_names_after # Remove another label @with_retry def remove_enhancement_label(): return remove_issue_label(RemoveIssueLabelParams( owner=owner, repo=repo, issue_number=issue["issue_number"], label="enhancement" )) remove_enhancement_label() # Verify second label was removed @with_retry def get_issue_after_second_remove(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) issue_after_second_remove = get_issue_after_second_remove() label_names_after_second = [label["name"] for label in issue_after_second_remove["labels"]] assert "bug" not in label_names_after_second assert "enhancement" not in label_names_after_second assert "documentation" in label_names_after_second # Remove last label @with_retry def remove_documentation_label(): return remove_issue_label(RemoveIssueLabelParams( owner=owner, repo=repo, issue_number=issue["issue_number"], label="documentation" )) remove_documentation_label() # Verify all labels are removed @with_retry def get_issue_after_all_removed(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"] )) issue_after_all_removed = get_issue_after_all_removed() assert not issue_after_all_removed["labels"], "All labels should be removed" finally: # Cleanup try: @with_retry def close_issue(): return update_issue(UpdateIssueParams( owner=owner, repo=repo, issue_number=issue["issue_number"], state="closed" )) close_issue() except Exception as e: print(f"Failed to close issue during cleanup: {e}")