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 | |
| 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) | |
| if __name__ == '__main__': | |
| unittest.main() | |