""" 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/", "not@a-valid-url.com", "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.""" @patch('requests.get') 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") @patch('requests.get') 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"]) @patch('requests.get') 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"]) @patch('requests.get') 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.""" @patch('requests.get') 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") @patch('requests.get') 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) @patch('requests.get') 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"]) @patch('functions.github._get_user_repositories') @patch('functions.github._get_github_user_info') @patch('functions.github._extract_github_username') 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()