Additional tests for improved coverage (#439)

Signed-off-by: Glenn Jocher <[email protected]>
Co-authored-by: UltralyticsAssistant <[email protected]>
This commit is contained in:
Glenn Jocher
2025-05-10 18:16:25 +02:00
committed by GitHub
co-authored by UltralyticsAssistant
parent 9a71915810
commit 7ba774a7b1
4 changed files with 462 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
from datetime import datetime
from unittest.mock import MagicMock, patch
from actions.dispatch_actions import (
RUN_CI_KEYWORD,
get_pr_branch,
main,
trigger_and_get_workflow_info,
update_comment,
)
def test_get_pr_branch():
"""Test getting PR branch name."""
mock_event = MagicMock()
mock_event.event_data = {"issue": {"number": 123}}
mock_event.get_repo_data.return_value = {"head": {"ref": "feature-branch"}}
branch = get_pr_branch(mock_event)
assert branch == "feature-branch"
mock_event.get_repo_data.assert_called_once_with("pulls/123")
def test_trigger_and_get_workflow_info():
"""Test triggering workflows and getting info."""
mock_event = MagicMock()
mock_event.repository = "test/repo"
# Mock the workflow and runs responses separately
workflow_response = MagicMock()
workflow_response.status_code = 200
workflow_response.json.return_value = {"name": "CI Workflow"}
runs_response = MagicMock()
runs_response.status_code = 200
runs_response.json.return_value = {
"workflow_runs": [{"html_url": "https://github.com/test/repo/actions/runs/123", "run_number": 42}]
}
# Set up get method to return different responses for different URLs
def get_side_effect(url):
if "workflows/ci.yml" in url and "runs" not in url:
return workflow_response
elif "workflows/ci.yml/runs" in url:
return runs_response
# Return default response for unexpected URLs
default = MagicMock()
default.status_code = 404
return default
mock_event.get.side_effect = get_side_effect
# Use patch to skip time.sleep and limit to one workflow
with patch("time.sleep"):
with patch("actions.dispatch_actions.WORKFLOW_FILES", ["ci.yml"]):
results = trigger_and_get_workflow_info(mock_event, "feature-branch")
# Check results
assert len(results) == 1
assert results[0]["name"] == "CI Workflow"
assert results[0]["run_number"] == 42
def test_update_comment_function():
"""Test updating comment with workflow info."""
mock_event = MagicMock()
mock_event.repository = "test/repo"
mock_event.event_data = {"comment": {"id": 456}}
comment_body = f"Run tests please {RUN_CI_KEYWORD}"
triggered_actions = [
{
"name": "CI Workflow",
"file": "ci.yml",
"url": "https://github.com/test/repo/actions/workflows/ci.yml",
"run_number": 42,
}
]
# Mock datetime to have a consistent timestamp
with patch("actions.dispatch_actions.datetime") as mock_datetime:
mock_datetime.now.return_value = datetime(2023, 1, 1, 12, 0, 0)
# Call without capturing return value
update_comment(mock_event, comment_body, triggered_actions, "feature-branch")
# Check that patch was called with expected content
mock_event.patch.assert_called_once()
# Verify key content in the comment body
args, kwargs = mock_event.patch.call_args
assert "https://api.github.com/repos/test/repo/issues/comments/456" in args[0]
assert "Actions Trigger" in kwargs["json"]["body"]
assert "CI Workflow" in kwargs["json"]["body"]
assert "2023-01-01 12:00:00 UTC" in kwargs["json"]["body"]
def test_main_triggers_workflows():
"""Test main function when comment contains trigger keyword."""
with patch("actions.dispatch_actions.Action") as MockAction:
# Configure mock
mock_event = MockAction.return_value
mock_event.event_name = "issue_comment"
mock_event.repository = "test/repo"
mock_event.event_data = {
"action": "created",
"issue": {"pull_request": {}},
"comment": {"body": f"Please run CI {RUN_CI_KEYWORD}", "user": {"login": "testuser"}, "id": 789},
}
mock_event.is_org_member.return_value = True
# Create minimal patches for the functions called by main
with patch("actions.dispatch_actions.get_pr_branch") as mock_get_branch:
with patch("actions.dispatch_actions.trigger_and_get_workflow_info") as mock_trigger:
with patch("actions.dispatch_actions.update_comment"):
# Set return values
mock_get_branch.return_value = "feature-branch"
mock_trigger.return_value = [{"name": "CI", "file": "ci.yml", "url": "url", "run_number": 1}]
# Call the function
main()
# Verify main component calls were made
mock_event.is_org_member.assert_called_once_with("testuser")
mock_get_branch.assert_called_once()
mock_trigger.assert_called_once()
def test_main_skips_non_pr_comments():
"""Test main function skips non-PR comments."""
with patch("actions.dispatch_actions.Action") as MockAction:
# Configure mock
mock_event = MockAction.return_value
mock_event.event_name = "issue_comment"
mock_event.event_data = {
"action": "created",
"issue": {}, # No pull_request key
"comment": {"body": f"Please run CI {RUN_CI_KEYWORD}", "user": {"login": "testuser"}},
}
main()
# Verify toggle_eyes_reaction was not called
mock_event.toggle_eyes_reaction.assert_not_called()
+116
View File
@@ -0,0 +1,116 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
from unittest.mock import MagicMock, patch
from actions.first_interaction import (
create_alert_label,
get_event_content,
get_first_interaction_response,
get_relevant_labels,
)
def test_get_event_content_issue():
"""Test extracting content from issue event."""
# Create mock event
mock_event = MagicMock()
mock_event.event_name = "issues"
mock_event.event_data = {
"action": "opened",
"issue": {
"number": 123,
"node_id": "node123",
"title": "Test Issue",
"body": "Issue description",
"user": {"login": "testuser"},
},
}
number, node_id, title, body, username, issue_type, action = get_event_content(mock_event)
assert number == 123
assert node_id == "node123"
assert title == "Test Issue"
assert body == "Issue description"
assert username == "testuser"
assert issue_type == "issue"
assert action == "opened"
def test_get_event_content_pr():
"""Test extracting content from PR event."""
# Create mock event
mock_event = MagicMock()
mock_event.event_name = "pull_request"
mock_event.event_data = {"action": "opened", "pull_request": {"number": 456}}
# Mock PR data returned from API
mock_event.get_repo_data.return_value = {
"number": 456,
"node_id": "node456",
"title": "Test PR",
"body": "PR description",
"user": {"login": "testuser"},
}
number, node_id, title, body, username, issue_type, action = get_event_content(mock_event)
assert number == 456
assert node_id == "node456"
assert title == "Test PR"
assert body == "PR description"
assert username == "testuser"
assert issue_type == "pull request"
assert action == "opened"
@patch("actions.first_interaction.get_completion")
def test_get_relevant_labels(mock_get_completion):
"""Test getting relevant labels for an issue."""
mock_get_completion.return_value = "bug, enhancement"
available_labels = {
"bug": "A bug in the software",
"enhancement": "New feature or request",
"documentation": "Improvements to documentation",
}
labels = get_relevant_labels(
issue_type="issue",
title="Bug: App crashes on startup",
body="The application crashes when started",
available_labels=available_labels,
current_labels=[],
)
assert labels == ["bug", "enhancement"]
mock_get_completion.assert_called_once()
@patch("actions.first_interaction.get_completion")
def test_get_first_interaction_response(mock_get_completion):
"""Test generating first interaction response."""
mock_get_completion.return_value = "Thank you for your issue"
mock_event = MagicMock()
mock_event.repository = "test/repo"
response = get_first_interaction_response(
event=mock_event, issue_type="issue", title="Test Issue", body="Issue description", username="testuser"
)
assert response == "Thank you for your issue"
mock_get_completion.assert_called_once()
def test_create_alert_label():
"""Test creating Alert label."""
mock_event = MagicMock()
create_alert_label(mock_event)
mock_event.post.assert_called_once()
args, kwargs = mock_event.post.call_args
assert "labels" in args[0]
assert kwargs["json"]["name"] == "Alert"
assert kwargs["json"]["color"] == "FF0000"
+73
View File
@@ -0,0 +1,73 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
from unittest.mock import MagicMock, patch
from actions.summarize_pr import (
generate_issue_comment,
generate_merge_message,
generate_pr_summary,
update_pr_description,
)
@patch("actions.summarize_pr.get_completion")
def test_generate_pr_summary(mock_get_completion):
"""Test generating PR summary with expected formatting."""
mock_get_completion.return_value = "Test PR summary content"
summary = generate_pr_summary("test/repo", "diff content")
assert summary.startswith("## 🛠️ PR Summary")
assert "Test PR summary content" in summary
mock_get_completion.assert_called_once()
@patch("actions.summarize_pr.get_completion")
def test_generate_merge_message(mock_get_completion):
"""Test generating merge thank you messages."""
mock_get_completion.return_value = "Thank you for your contribution!"
message = generate_merge_message(
pr_summary="Feature implementation", pr_credit="@testuser", pr_url="https://github.com/test/repo/pull/1"
)
assert message == "Thank you for your contribution!"
mock_get_completion.assert_called_once()
@patch("actions.summarize_pr.get_completion")
def test_generate_issue_comment(mock_get_completion):
"""Test generating issue comments about PR fixes."""
mock_get_completion.return_value = "This issue is fixed in PR #123"
comment = generate_issue_comment(
pr_url="https://api.github.com/repos/owner/repo/pulls/123",
pr_summary="Fixed bug",
pr_credit="@testuser",
pr_title="Bug fix PR",
)
assert comment == "This issue is fixed in PR #123"
mock_get_completion.assert_called_once()
def test_update_pr_description():
"""Test updating PR description with summary."""
# Mock Action class
mock_event = MagicMock()
mock_event.repository = "test/repo"
mock_event.pr = {"number": 123}
# Mock response for get request
mock_response = MagicMock()
mock_response.json.return_value = {"body": "Original description"}
mock_event.get.return_value = mock_response
# Test updating description
update_pr_description(mock_event, "## 🛠️ PR Summary\nNew summary")
# Verify patch was called with correct parameters
mock_event.patch.assert_called_once()
args, kwargs = mock_event.patch.call_args
assert args[0] == "https://api.github.com/repos/test/repo/pulls/123"
assert "Original description" in kwargs["json"]["body"]
assert "## 🛠️ PR Summary\nNew summary" in kwargs["json"]["body"]
+127
View File
@@ -0,0 +1,127 @@
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
from unittest.mock import MagicMock, patch
from actions.summarize_release import (
create_github_release,
generate_release_summary,
get_prs_between_tags,
get_release_diff,
)
def test_get_release_diff():
"""Test retrieving diff between tags."""
mock_event = MagicMock()
mock_event.repository = "test/repo"
mock_event.headers_diff = {"Accept": "application/vnd.github.v3.diff"}
# Mock response
mock_response = MagicMock()
mock_response.status_code = 200
mock_response.text = "diff content"
mock_event.get.return_value = mock_response
diff = get_release_diff(mock_event, "v1.0.0", "v1.1.0")
assert diff == "diff content"
mock_event.get.assert_called_once_with(
"https://api.github.com/repos/test/repo/compare/v1.0.0...v1.1.0", headers=mock_event.headers_diff
)
def test_get_prs_between_tags():
"""Test retrieving PRs between tags."""
mock_event = MagicMock()
mock_event.repository = "test/repo"
# Mock compare response
mock_compare_response = MagicMock()
mock_compare_response.json.return_value = {
"commits": [{"commit": {"message": "Fix bug #123"}}, {"commit": {"message": "Add feature #456"}}]
}
# Mock PR responses
mock_pr1_response = MagicMock()
mock_pr1_response.status_code = 200
mock_pr1_response.json.return_value = {
"number": 123,
"title": "Fix bug",
"body": "Fixes a bug",
"user": {"login": "user1"},
"html_url": "https://github.com/test/repo/pull/123",
"merged_at": "2023-01-01T12:00:00Z",
}
mock_pr2_response = MagicMock()
mock_pr2_response.status_code = 200
mock_pr2_response.json.return_value = {
"number": 456,
"title": "Add feature",
"body": "Adds a new feature",
"user": {"login": "user2"},
"html_url": "https://github.com/test/repo/pull/456",
"merged_at": "2023-01-02T12:00:00Z",
}
# Set up get method to return different responses
mock_event.get.side_effect = [mock_compare_response, mock_pr1_response, mock_pr2_response]
# Use patch to skip the sleep
with patch("time.sleep"):
prs = get_prs_between_tags(mock_event, "v1.0.0", "v1.1.0")
assert len(prs) == 2
assert prs[0]["number"] == 123
assert prs[1]["number"] == 456
@patch("actions.summarize_release.get_completion")
def test_generate_release_summary(mock_get_completion):
"""Test generating release summary."""
mock_get_completion.return_value = "Release summary content"
mock_event = MagicMock()
mock_event.repository = "test/repo"
# Create test data with matching author for new contributor
test_prs = [
{
"number": 123,
"title": "Fix bug",
"body": "Fixes a bug",
"author": "newuser", # Match the contributor name
"html_url": "https://github.com/test/repo/pull/123",
"merged_at": "2023-01-01T12:00:00Z",
}
]
# Mock new contributors function
with patch("actions.summarize_release.get_new_contributors", return_value=["newuser"]):
summary = generate_release_summary(
event=mock_event, diff="diff content", prs=test_prs, latest_tag="v1.1.0", previous_tag="v1.0.0"
)
assert "Release summary content" in summary
assert "What's Changed" in summary
assert "https://github.com/test/repo/compare/v1.0.0...v1.1.0" in summary
mock_get_completion.assert_called_once()
def test_create_github_release():
"""Test creating GitHub release."""
mock_event = MagicMock()
mock_event.repository = "test/repo"
create_github_release(event=mock_event, tag_name="v1.1.0", name="Version 1.1.0", body="Release notes")
mock_event.post.assert_called_once_with(
"https://api.github.com/repos/test/repo/releases",
json={
"tag_name": "v1.1.0",
"name": "Version 1.1.0",
"body": "Release notes",
"draft": False,
"prerelease": False,
},
)