Spaces:
Configuration error
Configuration error
""" | |
Unit tests for the github module. | |
""" | |
import unittest | |
from unittest.mock import patch, MagicMock | |
import requests | |
from functions import github | |
# pylint: disable=protected-access | |
class TestExtractGitHubUsername(unittest.TestCase): | |
"""Test cases for the _extract_github_username function.""" | |
def test_valid_github_urls(self): | |
"""Test extraction from valid GitHub URLs.""" | |
test_cases = [ | |
("https://github.com/octocat", "octocat"), | |
("https://github.com/octocat/", "octocat"), | |
("http://github.com/test-user", "test-user"), | |
("github.com/user_name", "user_name"), | |
("https://github.com/user123", "user123"), | |
("https://github.com/octocat/Hello-World", "octocat"), | |
] | |
for url, expected in test_cases: | |
with self.subTest(url=url): | |
result = github._extract_github_username(url) | |
self.assertEqual(result, expected) | |
def test_invalid_github_urls(self): | |
"""Test handling of invalid GitHub URLs.""" | |
invalid_urls = [ | |
"", | |
"https://gitlab.com/user", | |
"https://github.com/", | |
"[email protected]", | |
"https://github.com/user-with-very-long-name-that-exceeds-github-limit", | |
None | |
] | |
for url in invalid_urls: | |
with self.subTest(url=url): | |
result = github._extract_github_username(url) | |
self.assertIsNone(result) | |
def test_username_validation(self): | |
"""Test username format validation.""" | |
# Valid usernames | |
valid_usernames = ["user", "user-name", "user_name", "user123", "a" * 39] | |
for username in valid_usernames: | |
with self.subTest(username=username): | |
result = github._extract_github_username(f"github.com/{username}") | |
self.assertEqual(result, username) | |
# Invalid usernames | |
invalid_usernames = ["", "a" * 40, "user@name", "user.name"] | |
for username in invalid_usernames: | |
with self.subTest(username=username): | |
result = github._extract_github_username(f"github.com/{username}") | |
self.assertIsNone(result) | |
class TestGetGitHubUserInfo(unittest.TestCase): | |
"""Test cases for the _get_github_user_info function.""" | |
def test_successful_user_info(self, mock_get): | |
"""Test successful user info retrieval.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = { | |
"login": "octocat", | |
"public_repos": 10, | |
"name": "The Octocat" | |
} | |
mock_get.return_value = mock_response | |
result = github._get_github_user_info("octocat") | |
self.assertEqual(result["status"], "success") | |
self.assertIn("data", result) | |
self.assertEqual(result["data"]["login"], "octocat") | |
def test_user_not_found(self, mock_get): | |
"""Test handling of non-existent user.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 404 | |
mock_get.return_value = mock_response | |
result = github._get_github_user_info("nonexistentuser") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("not found", result["message"]) | |
def test_rate_limit_exceeded(self, mock_get): | |
"""Test handling of rate limit exceeded.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 403 | |
mock_get.return_value = mock_response | |
result = github._get_github_user_info("octocat") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("rate limit", result["message"]) | |
def test_network_error(self, mock_get): | |
"""Test handling of network errors.""" | |
mock_get.side_effect = requests.RequestException("Connection error") | |
result = github._get_github_user_info("octocat") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("Network error", result["message"]) | |
class TestGetUserRepositories(unittest.TestCase): | |
"""Test cases for the _get_user_repositories function.""" | |
def test_successful_repository_retrieval(self, mock_get): | |
"""Test successful repository retrieval.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = [ | |
{ | |
"name": "Hello-World", | |
"description": "My first repository", | |
"language": "Python", | |
"stargazers_count": 5, | |
"forks_count": 2 | |
} | |
] | |
mock_get.return_value = mock_response | |
result = github._get_user_repositories("octocat") | |
self.assertEqual(result["status"], "success") | |
self.assertIn("data", result) | |
self.assertEqual(len(result["data"]), 1) | |
self.assertEqual(result["data"][0]["name"], "Hello-World") | |
def test_empty_repository_list(self, mock_get): | |
"""Test handling of empty repository list.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = [] | |
mock_get.return_value = mock_response | |
result = github._get_user_repositories("octocat") | |
self.assertEqual(result["status"], "success") | |
self.assertEqual(len(result["data"]), 0) | |
def test_api_error(self, mock_get): | |
"""Test handling of API errors.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 500 | |
mock_get.return_value = mock_response | |
result = github._get_user_repositories("octocat") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("GitHub API error", result["message"]) | |
class TestProcessRepositoryData(unittest.TestCase): | |
"""Test cases for the _process_repository_data function.""" | |
def test_basic_processing(self): | |
"""Test basic repository data processing.""" | |
raw_repos = [ | |
{ | |
"name": "test-repo", | |
"description": "Test repository", | |
"language": "Python", | |
"stargazers_count": 10, | |
"forks_count": 5, | |
"updated_at": "2024-01-01T00:00:00Z", | |
"html_url": "https://github.com/user/test-repo", | |
"topics": ["python", "test"], | |
"fork": False | |
} | |
] | |
result = github._process_repository_data(raw_repos) | |
self.assertEqual(len(result), 1) | |
processed_repo = result[0] | |
self.assertEqual(processed_repo["name"], "test-repo") | |
self.assertEqual(processed_repo["description"], "Test repository") | |
self.assertEqual(processed_repo["language"], "Python") | |
self.assertEqual(processed_repo["stars"], 10) | |
self.assertEqual(processed_repo["forks"], 5) | |
self.assertFalse(processed_repo["is_fork"]) | |
def test_fork_filtering(self): | |
"""Test filtering of unmodified forks.""" | |
raw_repos = [ | |
{ | |
"name": "original-repo", | |
"fork": False, | |
"stargazers_count": 5 | |
}, | |
{ | |
"name": "unmodified-fork", | |
"fork": True, | |
"stargazers_count": 0 | |
}, | |
{ | |
"name": "modified-fork", | |
"fork": True, | |
"stargazers_count": 3 | |
} | |
] | |
result = github._process_repository_data(raw_repos) | |
# Should include original repo and modified fork, exclude unmodified fork | |
self.assertEqual(len(result), 2) | |
repo_names = [repo["name"] for repo in result] | |
self.assertIn("original-repo", repo_names) | |
self.assertIn("modified-fork", repo_names) | |
self.assertNotIn("unmodified-fork", repo_names) | |
def test_missing_fields(self): | |
"""Test handling of missing fields in repository data.""" | |
raw_repos = [ | |
{ | |
"name": "minimal-repo" | |
# Missing most optional fields | |
} | |
] | |
result = github._process_repository_data(raw_repos) | |
self.assertEqual(len(result), 1) | |
processed_repo = result[0] | |
self.assertEqual(processed_repo["name"], "minimal-repo") | |
self.assertEqual(processed_repo["description"], "") | |
self.assertEqual(processed_repo["language"], "") | |
self.assertEqual(processed_repo["stars"], 0) | |
self.assertEqual(processed_repo["forks"], 0) | |
class TestGetGitHubRepositories(unittest.TestCase): | |
"""Test cases for the get_github_repositories function.""" | |
def test_empty_url(self): | |
"""Test handling of empty or None URL.""" | |
test_cases = [None, "", " "] | |
for url in test_cases: | |
with self.subTest(url=url): | |
result = github.get_github_repositories(url) | |
self.assertEqual(result["status"], "error") | |
self.assertIn("No GitHub URL provided", result["message"]) | |
def test_successful_retrieval(self, mock_extract, mock_user_info, mock_repos): | |
"""Test successful repository retrieval.""" | |
mock_extract.return_value = "octocat" | |
mock_user_info.return_value = { | |
"status": "success", | |
"data": {"public_repos": 10} | |
} | |
mock_repos.return_value = { | |
"status": "success", | |
"data": [{"name": "Hello-World", "fork": False, "stargazers_count": 5}] | |
} | |
result = github.get_github_repositories("https://github.com/octocat") | |
self.assertEqual(result["status"], "success") | |
self.assertIn("repositories", result) | |
self.assertIn("metadata", result) | |
self.assertEqual(result["metadata"]["username"], "octocat") | |
def test_invalid_url_format(self): | |
"""Test handling of invalid URL format.""" | |
result = github.get_github_repositories("https://gitlab.com/user") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("Invalid GitHub URL format", result["message"]) | |
class TestFormatRepositoriesForLLM(unittest.TestCase): | |
"""Test cases for the format_repositories_for_llm function.""" | |
def test_successful_formatting(self): | |
"""Test successful formatting of repository data.""" | |
github_result = { | |
"status": "success", | |
"repositories": [ | |
{ | |
"name": "test-repo", | |
"description": "A test repository", | |
"language": "Python", | |
"stars": 10, | |
"forks": 5, | |
"updated_at": "2024-01-01T00:00:00Z", | |
"html_url": "https://github.com/user/test-repo", | |
"topics": ["python", "test"] | |
} | |
], | |
"metadata": { | |
"username": "testuser", | |
"profile_url": "https://github.com/testuser" | |
} | |
} | |
result = github.format_repositories_for_llm(github_result) | |
self.assertIn("=== GITHUB REPOSITORIES ===", result) | |
self.assertIn("testuser", result) | |
self.assertIn("test-repo", result) | |
self.assertIn("A test repository", result) | |
self.assertIn("Python", result) | |
self.assertIn("=== END GITHUB REPOSITORIES ===", result) | |
def test_error_status(self): | |
"""Test formatting when there's an error status.""" | |
github_result = { | |
"status": "error", | |
"message": "User not found" | |
} | |
result = github.format_repositories_for_llm(github_result) | |
self.assertIn("could not be retrieved", result) | |
self.assertIn("User not found", result) | |
def test_no_repositories(self): | |
"""Test formatting when no repositories are found.""" | |
github_result = { | |
"status": "success", | |
"repositories": [], | |
"metadata": {"username": "emptyuser"} | |
} | |
result = github.format_repositories_for_llm(github_result) | |
self.assertIn("No public repositories found", result) | |
self.assertIn("emptyuser", result) | |
def test_many_repositories_limit(self): | |
"""Test that formatting limits to 20 repositories.""" | |
repositories = [] | |
for i in range(25): | |
repositories.append({ | |
"name": f"repo-{i}", | |
"description": f"Repository {i}", | |
"language": "Python", | |
"stars": i, | |
"forks": 0, | |
"updated_at": "2024-01-01T00:00:00Z", | |
"html_url": f"https://github.com/user/repo-{i}", | |
"topics": [] | |
}) | |
github_result = { | |
"status": "success", | |
"repositories": repositories, | |
"metadata": {"username": "manyrepos"} | |
} | |
result = github.format_repositories_for_llm(github_result) | |
# Should mention "and 5 more repositories" | |
self.assertIn("and 5 more repositories", result) | |
# Should contain the first 20 repos | |
self.assertIn("repo-0", result) | |
self.assertIn("repo-19", result) | |
# Should not contain repos beyond 20 | |
self.assertNotIn("repo-20", result) | |
class TestGetRepositoryDetails(unittest.TestCase): | |
"""Test cases for the get_repository_details function.""" | |
def test_invalid_repository_url(self): | |
"""Test handling of invalid repository URLs.""" | |
invalid_urls = [ | |
"", | |
None, | |
"https://gitlab.com/user/repo", | |
"https://github.com/", | |
"https://github.com/user", | |
"not-a-url", | |
] | |
for url in invalid_urls: | |
with self.subTest(url=url): | |
result = github.get_repository_details(url) | |
self.assertEqual(result["status"], "error") | |
self.assertIn("message", result) | |
def test_repository_not_found(self, mock_extract, mock_get_info): | |
"""Test handling of non-existent repository.""" | |
mock_extract.return_value = ("user", "nonexistent-repo") | |
mock_get_info.return_value = { | |
"status": "error", | |
"message": "Repository 'user/nonexistent-repo' not found" | |
} | |
result = github.get_repository_details("https://github.com/user/nonexistent-repo") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("not found", result["message"]) | |
def test_successful_repository_details(self, mock_extract, mock_get_info, | |
mock_languages, mock_readme, mock_contents, | |
mock_releases, mock_contributors): | |
"""Test successful repository details retrieval.""" | |
# Mock URL extraction | |
mock_extract.return_value = ("octocat", "Hello-World") | |
# Mock basic repository info | |
mock_get_info.return_value = { | |
"status": "success", | |
"data": { | |
"name": "Hello-World", | |
"full_name": "octocat/Hello-World", | |
"description": "This your first repo!", | |
"language": "C", | |
"stargazers_count": 80, | |
"forks_count": 9, | |
"watchers_count": 80, | |
"size": 108, | |
"created_at": "2011-01-26T19:01:12Z", | |
"updated_at": "2011-01-26T19:14:43Z", | |
"pushed_at": "2011-01-26T19:06:43Z", | |
"html_url": "https://github.com/octocat/Hello-World", | |
"clone_url": "https://github.com/octocat/Hello-World.git", | |
"ssh_url": "[email protected]:octocat/Hello-World.git", | |
"topics": ["example", "tutorial"], | |
"license": {"name": "MIT License", "spdx_id": "MIT"}, | |
"fork": False, | |
"archived": False, | |
"private": False, | |
"default_branch": "master", | |
"open_issues_count": 0, | |
"has_issues": True, | |
"has_wiki": True, | |
"has_pages": False, | |
"has_projects": True, | |
"visibility": "public" | |
} | |
} | |
# Mock additional data | |
mock_languages.return_value = { | |
"status": "success", | |
"data": {"C": 78.1, "Makefile": 21.9} | |
} | |
mock_readme.return_value = { | |
"status": "success", | |
"data": "# Hello World\n\nThis is a test repository." | |
} | |
mock_contents.return_value = { | |
"status": "success", | |
"data": ["README.md", "hello.c", "Makefile"] | |
} | |
mock_releases.return_value = { | |
"status": "success", | |
"data": [ | |
{ | |
"tag_name": "v1.0.0", | |
"name": "First Release", | |
"published_at": "2011-01-26T19:14:43Z", | |
"prerelease": False, | |
"draft": False | |
} | |
] | |
} | |
mock_contributors.return_value = { | |
"status": "success", | |
"data": [ | |
{ | |
"login": "octocat", | |
"contributions": 32, | |
"html_url": "https://github.com/octocat", | |
"type": "User" | |
} | |
] | |
} | |
result = github.get_repository_details("https://github.com/octocat/Hello-World") | |
# Verify success | |
self.assertEqual(result["status"], "success") | |
self.assertIn("repository", result) | |
repo = result["repository"] | |
# Verify basic info | |
self.assertEqual(repo["name"], "Hello-World") | |
self.assertEqual(repo["full_name"], "octocat/Hello-World") | |
self.assertEqual(repo["description"], "This your first repo!") | |
self.assertEqual(repo["language"], "C") | |
self.assertEqual(repo["stars"], 80) | |
self.assertEqual(repo["forks"], 9) | |
# Verify additional data | |
self.assertEqual(repo["languages"], {"C": 78.1, "Makefile": 21.9}) | |
self.assertIn("Hello World", repo["readme"]) | |
self.assertEqual(repo["file_structure"], ["README.md", "hello.c", "Makefile"]) | |
self.assertEqual(len(repo["releases"]), 1) | |
self.assertEqual(repo["releases"][0]["tag_name"], "v1.0.0") | |
self.assertEqual(len(repo["contributors"]), 1) | |
self.assertEqual(repo["contributors"][0]["login"], "octocat") | |
# Verify boolean flags | |
self.assertFalse(repo["is_fork"]) | |
self.assertFalse(repo["is_archived"]) | |
self.assertFalse(repo["is_private"]) | |
self.assertTrue(repo["has_issues"]) | |
def test_extract_repo_info_valid_urls(self): | |
"""Test _extract_repo_info with valid repository URLs.""" | |
test_cases = [ | |
("https://github.com/octocat/Hello-World", ("octocat", "Hello-World")), | |
("https://github.com/user/repo.git", ("user", "repo")), | |
("https://github.com/org/project/", ("org", "project")), | |
("github.com/test/example", ("test", "example")), | |
("https://github.com/user/repo/issues", ("user", "repo")), | |
] | |
for url, expected in test_cases: | |
with self.subTest(url=url): | |
result = github._extract_repo_info(url) | |
self.assertEqual(result, expected) | |
def test_extract_repo_info_invalid_urls(self): | |
"""Test _extract_repo_info with invalid repository URLs.""" | |
invalid_urls = [ | |
"", | |
"https://gitlab.com/user/repo", | |
"https://github.com/user", | |
"https://github.com/", | |
"not-a-url", | |
] | |
for url in invalid_urls: | |
with self.subTest(url=url): | |
result = github._extract_repo_info(url) | |
self.assertEqual(result, (None, None)) | |
def test_get_repository_info_success(self, mock_get): | |
"""Test _get_repository_info with successful response.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = { | |
"name": "test-repo", | |
"full_name": "user/test-repo" | |
} | |
mock_get.return_value = mock_response | |
result = github._get_repository_info("user", "test-repo") | |
self.assertEqual(result["status"], "success") | |
self.assertIn("data", result) | |
self.assertEqual(result["data"]["name"], "test-repo") | |
def test_get_repository_info_not_found(self, mock_get): | |
"""Test _get_repository_info with 404 response.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 404 | |
mock_get.return_value = mock_response | |
result = github._get_repository_info("user", "nonexistent") | |
self.assertEqual(result["status"], "error") | |
self.assertIn("not found", result["message"]) | |
def test_get_repository_languages_success(self, mock_get): | |
"""Test _get_repository_languages with successful response.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = { | |
"Python": 50000, | |
"JavaScript": 25000, | |
"CSS": 25000 | |
} | |
mock_get.return_value = mock_response | |
result = github._get_repository_languages("user", "repo") | |
self.assertEqual(result["status"], "success") | |
expected_percentages = {"Python": 50.0, "JavaScript": 25.0, "CSS": 25.0} | |
self.assertEqual(result["data"], expected_percentages) | |
def test_get_repository_readme_success(self, mock_get): | |
"""Test _get_repository_readme with successful response.""" | |
# Mock the README metadata response | |
readme_response = MagicMock() | |
readme_response.status_code = 200 | |
readme_response.json.return_value = { | |
"download_url": "https://raw.githubusercontent.com/user/repo/main/README.md" | |
} | |
# Mock the README content response | |
content_response = MagicMock() | |
content_response.status_code = 200 | |
content_response.text = "# Test Repository\n\nThis is a test." | |
mock_get.side_effect = [readme_response, content_response] | |
result = github._get_repository_readme("user", "repo") | |
self.assertEqual(result["status"], "success") | |
self.assertIn("Test Repository", result["data"]) | |
def test_get_repository_contents_success(self, mock_get): | |
"""Test _get_repository_contents with successful response.""" | |
mock_response = MagicMock() | |
mock_response.status_code = 200 | |
mock_response.json.return_value = [ | |
{"name": "src", "type": "dir"}, | |
{"name": "README.md", "type": "file"}, | |
{"name": "setup.py", "type": "file"}, | |
{"name": "tests", "type": "dir"} | |
] | |
mock_get.return_value = mock_response | |
result = github._get_repository_contents("user", "repo") | |
self.assertEqual(result["status"], "success") | |
expected_structure = ["src/", "tests/", "README.md", "setup.py"] | |
self.assertEqual(result["data"], expected_structure) | |
if __name__ == '__main__': | |
unittest.main() | |