Add tests for GitHub utilities (#438)
Co-authored-by: UltralyticsAssistant <[email protected]>
This commit is contained in:
co-authored by
UltralyticsAssistant
parent
0479623d9b
commit
9a71915810
@@ -0,0 +1,19 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
|
||||
# Import CLI command modules
|
||||
from actions import (
|
||||
first_interaction,
|
||||
summarize_pr,
|
||||
summarize_release,
|
||||
update_markdown_code_blocks,
|
||||
)
|
||||
|
||||
|
||||
def test_importable_modules():
|
||||
"""Test that all modules can be imported without errors."""
|
||||
# This is a simple test to ensure modules can be imported successfully
|
||||
assert hasattr(first_interaction, "main")
|
||||
assert hasattr(summarize_pr, "main")
|
||||
assert hasattr(summarize_release, "main")
|
||||
assert hasattr(update_markdown_code_blocks, "main")
|
||||
@@ -0,0 +1,63 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from actions.utils.common_utils import (
|
||||
allow_redirect,
|
||||
brave_search,
|
||||
clean_url,
|
||||
remove_html_comments,
|
||||
)
|
||||
|
||||
|
||||
def test_remove_html_comments():
|
||||
"""Test removing HTML comments from strings."""
|
||||
test_str = "Before <!-- Comment --> After"
|
||||
assert remove_html_comments(test_str) == "Before After"
|
||||
|
||||
# Multiline comment
|
||||
test_str = "Before\n<!-- Comment\nline 2\nline 3 -->\nAfter"
|
||||
assert remove_html_comments(test_str) == "Before\n\nAfter"
|
||||
|
||||
# No comments
|
||||
test_str = "No comments here"
|
||||
assert remove_html_comments(test_str) == "No comments here"
|
||||
|
||||
|
||||
def test_clean_url():
|
||||
"""Test cleaning URL strings."""
|
||||
# Test removing quotes and trailing characters
|
||||
assert clean_url('"https://example.com"') == "https://example.com"
|
||||
assert clean_url("'https://example.com'") == "https://example.com"
|
||||
assert clean_url("https://example.com.") == "https://example.com"
|
||||
assert clean_url("https://example.com,") == "https://example.com"
|
||||
|
||||
# Test git URLs
|
||||
assert clean_url("git+https://github.com/user/repo.git@main") == "https://github.com/user/repo"
|
||||
|
||||
|
||||
def test_allow_redirect():
|
||||
"""Test allowing URL redirects based on rules."""
|
||||
# Should not allow - start ignores
|
||||
assert not allow_redirect("https://youtu.be/xyz", "https://youtube.com")
|
||||
|
||||
# Should not allow - end ignores
|
||||
assert not allow_redirect("https://example.com", "https://example.com/404")
|
||||
|
||||
# Empty end URL
|
||||
assert not allow_redirect("https://example.com", "")
|
||||
|
||||
|
||||
@patch("requests.get")
|
||||
def test_brave_search(mock_get):
|
||||
"""Test Brave search API integration."""
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {
|
||||
"web": {"results": [{"url": "https://example.com"}, {"url": "https://example.org"}]}
|
||||
}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
results = brave_search("test query", "test-api-key", count=2)
|
||||
assert results == ["https://example.com", "https://example.org"]
|
||||
mock_get.assert_called_once()
|
||||
@@ -0,0 +1,64 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from actions.utils.github_utils import Action, check_pypi_version, ultralytics_actions_info
|
||||
|
||||
|
||||
def test_action_init():
|
||||
"""Test Action class initialization with default values."""
|
||||
with patch.dict("os.environ", {"GITHUB_TOKEN": "test-token", "GITHUB_EVENT_NAME": "push"}):
|
||||
with patch(
|
||||
"actions.utils.github_utils.Action._load_event_data",
|
||||
return_value={"repository": {"full_name": "test/repo"}},
|
||||
):
|
||||
action = Action()
|
||||
assert action.token == "test-token"
|
||||
assert action.event_name == "push"
|
||||
assert action.repository == "test/repo"
|
||||
|
||||
|
||||
def test_action_request_methods():
|
||||
"""Test Action HTTP request methods."""
|
||||
with patch("requests.get") as mock_get:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
action = Action(token="test-token")
|
||||
response = action.get("https://api.github.com/test")
|
||||
|
||||
assert response == mock_response
|
||||
mock_get.assert_called_once()
|
||||
|
||||
|
||||
def test_load_event_data():
|
||||
"""Test loading event data from file."""
|
||||
with patch("pathlib.Path.exists", return_value=True):
|
||||
with patch("pathlib.Path.read_text", return_value='{"test": "data"}'):
|
||||
action = Action()
|
||||
data = action._load_event_data("fake_path")
|
||||
assert data == {"test": "data"}
|
||||
|
||||
|
||||
def test_check_pypi_version():
|
||||
"""Test check_pypi_version function."""
|
||||
with patch("tomllib.load", return_value={"project": {"name": "test-package", "version": "1.0.0"}}):
|
||||
with patch("requests.get") as mock_get:
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"info": {"version": "0.9.0"}}
|
||||
mock_get.return_value = mock_response
|
||||
|
||||
local_version, online_version, publish = check_pypi_version()
|
||||
|
||||
assert local_version == "1.0.0"
|
||||
assert online_version == "0.9.0"
|
||||
assert publish is True
|
||||
|
||||
|
||||
def test_ultralytics_actions_info():
|
||||
"""Test ultralytics_actions_info function."""
|
||||
with patch("actions.utils.github_utils.Action.print_info") as mock_print_info:
|
||||
ultralytics_actions_info()
|
||||
mock_print_info.assert_called_once()
|
||||
@@ -0,0 +1,84 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
import importlib
|
||||
import re
|
||||
|
||||
import pytest
|
||||
|
||||
from actions import __version__
|
||||
from actions.utils import (
|
||||
GITHUB_API_URL,
|
||||
GITHUB_GRAPHQL_URL,
|
||||
Action,
|
||||
allow_redirect,
|
||||
check_pypi_version,
|
||||
get_completion,
|
||||
remove_html_comments,
|
||||
ultralytics_actions_info,
|
||||
)
|
||||
|
||||
|
||||
def test_version_format():
|
||||
"""Test that version follows the expected format."""
|
||||
version_pattern = re.compile(r"^\d+\.\d+\.\d+$")
|
||||
assert version_pattern.match(__version__), f"Version {__version__} does not match pattern"
|
||||
|
||||
|
||||
def test_util_module_imports():
|
||||
"""Test that all utilities can be imported from the utils module."""
|
||||
# Test the utils module exports
|
||||
assert GITHUB_API_URL.startswith("https://api.github.com")
|
||||
assert GITHUB_GRAPHQL_URL.startswith("https://api.github.com/graphql")
|
||||
|
||||
# Check that Action class exists and is importable
|
||||
assert hasattr(Action, "__init__")
|
||||
assert hasattr(Action, "get")
|
||||
assert hasattr(Action, "post")
|
||||
|
||||
# Check function imports
|
||||
assert callable(allow_redirect)
|
||||
assert callable(check_pypi_version)
|
||||
assert callable(get_completion)
|
||||
assert callable(remove_html_comments)
|
||||
assert callable(ultralytics_actions_info)
|
||||
|
||||
|
||||
def test_all_modules_importable():
|
||||
"""Test that all modules can be imported without errors."""
|
||||
modules = [
|
||||
"actions",
|
||||
"actions.utils",
|
||||
"actions.utils.common_utils",
|
||||
"actions.utils.github_utils",
|
||||
"actions.utils.openai_utils",
|
||||
"actions.first_interaction",
|
||||
"actions.summarize_pr",
|
||||
"actions.summarize_release",
|
||||
"actions.update_markdown_code_blocks",
|
||||
"actions.dispatch_actions",
|
||||
]
|
||||
|
||||
for module_name in modules:
|
||||
module = importlib.import_module(module_name)
|
||||
assert module is not None, f"Failed to import {module_name}"
|
||||
|
||||
|
||||
def test_cli_entry_points():
|
||||
"""Test that CLI entry points are defined in pyproject.toml."""
|
||||
from pathlib import Path
|
||||
|
||||
pyproject_path = Path(__file__).parents[1] / "pyproject.toml"
|
||||
if not pyproject_path.exists():
|
||||
pytest.skip("pyproject.toml not found")
|
||||
|
||||
content = pyproject_path.read_text()
|
||||
expected_names = [
|
||||
"ultralytics-actions-first-interaction",
|
||||
"ultralytics-actions-summarize-pr",
|
||||
"ultralytics-actions-summarize-release",
|
||||
"ultralytics-actions-update-markdown-code-blocks",
|
||||
"ultralytics-actions-info",
|
||||
]
|
||||
|
||||
for name in expected_names:
|
||||
assert name in content, f"Entry point {name} not found in pyproject.toml"
|
||||
@@ -0,0 +1,65 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
from actions.utils.openai_utils import get_completion, remove_outer_codeblocks
|
||||
|
||||
|
||||
def test_remove_outer_codeblocks():
|
||||
"""Test removing outer code block markers from strings."""
|
||||
# Test with python code block
|
||||
input_str = "```python\ndef test():\n return True\n```"
|
||||
expected = "def test():\n return True"
|
||||
assert remove_outer_codeblocks(input_str) == expected
|
||||
|
||||
# Test with no language specified
|
||||
input_str = "```\ndef test():\n return True\n```"
|
||||
expected = "def test():\n return True"
|
||||
assert remove_outer_codeblocks(input_str) == expected
|
||||
|
||||
# Test with no code blocks
|
||||
input_str = "def test():\n return True"
|
||||
assert remove_outer_codeblocks(input_str) == input_str
|
||||
|
||||
|
||||
@patch("requests.post")
|
||||
def test_get_completion(mock_post):
|
||||
"""Test OpenAI API completion function with mocked response."""
|
||||
# Setup mock response
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"choices": [{"message": {"content": "Test response from OpenAI"}}]}
|
||||
mock_post.return_value = mock_response
|
||||
|
||||
# Test with basic messages
|
||||
messages = [{"role": "system", "content": "You are a helpful assistant"}, {"role": "user", "content": "Hello"}]
|
||||
|
||||
# Use a context manager for the environment variable
|
||||
with patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}, clear=False):
|
||||
with patch("actions.utils.openai_utils.OPENAI_API_KEY", "test-key"):
|
||||
result = get_completion(messages, check_links=False)
|
||||
|
||||
assert result == "Test response from OpenAI"
|
||||
mock_post.assert_called_once()
|
||||
|
||||
|
||||
@patch("requests.post")
|
||||
@patch("actions.utils.openai_utils.check_links_in_string")
|
||||
def test_get_completion_with_link_check(mock_check_links, mock_post):
|
||||
"""Test get_completion with link checking."""
|
||||
# Setup mocks
|
||||
mock_response = MagicMock()
|
||||
mock_response.status_code = 200
|
||||
mock_response.json.return_value = {"choices": [{"message": {"content": "Response with https://example.com link"}}]}
|
||||
mock_post.return_value = mock_response
|
||||
mock_check_links.return_value = True
|
||||
|
||||
messages = [{"role": "user", "content": "Hello"}]
|
||||
|
||||
# Use a context manager for the environment variable
|
||||
with patch.dict("os.environ", {"OPENAI_API_KEY": "test-key"}, clear=False):
|
||||
with patch("actions.utils.openai_utils.OPENAI_API_KEY", "test-key"):
|
||||
result = get_completion(messages)
|
||||
|
||||
assert result == "Response with https://example.com link"
|
||||
mock_check_links.assert_called_once()
|
||||
@@ -0,0 +1,106 @@
|
||||
# Ultralytics 🚀 AGPL-3.0 License - https://ultralytics.com/license
|
||||
|
||||
from pathlib import Path
|
||||
from unittest.mock import mock_open, patch
|
||||
|
||||
from actions.update_markdown_code_blocks import (
|
||||
add_indentation,
|
||||
extract_code_blocks,
|
||||
generate_temp_filename,
|
||||
process_markdown_file,
|
||||
remove_indentation,
|
||||
)
|
||||
|
||||
|
||||
def test_extract_code_blocks():
|
||||
"""Test extracting code blocks from markdown content."""
|
||||
# Test with Python and Bash code blocks
|
||||
markdown_content = """
|
||||
# Test Markdown
|
||||
|
||||
```python
|
||||
def test():
|
||||
return True
|
||||
```
|
||||
|
||||
And some bash code:
|
||||
|
||||
```bash
|
||||
echo "Hello World"
|
||||
```
|
||||
"""
|
||||
code_blocks = extract_code_blocks(markdown_content)
|
||||
|
||||
assert len(code_blocks["python"]) == 1
|
||||
assert code_blocks["python"][0][1] == "def test():\n return True"
|
||||
|
||||
assert len(code_blocks["bash"]) == 1
|
||||
assert code_blocks["bash"][0][1] == 'echo "Hello World"'
|
||||
|
||||
|
||||
def test_remove_indentation():
|
||||
"""Test removing indentation from code blocks."""
|
||||
code_block = " line 1\n line 2\n line 3"
|
||||
result = remove_indentation(code_block, 4)
|
||||
|
||||
assert result == "line 1\nline 2\nline 3"
|
||||
|
||||
# Test with mixed indentation
|
||||
code_block = " line 1\n line 2\n line 3"
|
||||
result = remove_indentation(code_block, 2)
|
||||
|
||||
assert result == " line 1\nline 2\n line 3"
|
||||
|
||||
|
||||
def test_add_indentation():
|
||||
"""Test adding indentation to code blocks."""
|
||||
code_block = "line 1\nline 2\nline 3"
|
||||
result = add_indentation(code_block, 4)
|
||||
|
||||
assert result == " line 1\n line 2\n line 3"
|
||||
|
||||
# Test with empty lines
|
||||
code_block = "line 1\n\nline 3"
|
||||
result = add_indentation(code_block, 2)
|
||||
|
||||
assert result == " line 1\n\n line 3"
|
||||
|
||||
|
||||
def test_generate_temp_filename():
|
||||
"""Test generating temporary filenames."""
|
||||
file_path = Path("docs/guide.md")
|
||||
|
||||
filename = generate_temp_filename(file_path, 0, "python")
|
||||
|
||||
assert "guide_docs_p0_" in filename
|
||||
assert filename.endswith(".py")
|
||||
|
||||
filename = generate_temp_filename(file_path, 1, "bash")
|
||||
|
||||
assert "guide_docs_b1_" in filename
|
||||
assert filename.endswith(".sh")
|
||||
|
||||
|
||||
@patch("pathlib.Path.read_text")
|
||||
@patch("pathlib.Path.write_text")
|
||||
@patch("builtins.open", new_callable=mock_open)
|
||||
def test_process_markdown_file(mock_file, mock_write, mock_read_text):
|
||||
"""Test processing markdown files."""
|
||||
mock_read_text.return_value = """
|
||||
# Test
|
||||
|
||||
```python
|
||||
def test():
|
||||
return True
|
||||
```
|
||||
"""
|
||||
|
||||
file_path = Path("test.md")
|
||||
temp_dir = Path("temp")
|
||||
|
||||
markdown_content, temp_files = process_markdown_file(file_path, temp_dir)
|
||||
|
||||
assert markdown_content is not None
|
||||
assert len(temp_files) == 1
|
||||
assert temp_files[0][1] == "def test():\n return True"
|
||||
mock_file.assert_called_once()
|
||||
Reference in New Issue
Block a user