#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Unit tests for the Repository Service """ import unittest from unittest.mock import patch, MagicMock import os import sys import shutil from pathlib import Path # Add the project root directory to the Python path project_root = Path(__file__).resolve().parent.parent sys.path.insert(0, str(project_root)) from src.services.repository_service import ( validate_github_url, normalize_github_url, extract_repo_name, clone_repository, get_repository_info, cleanup_repository, cleanup_all_repositories ) class TestRepositoryService(unittest.TestCase): """Test cases for the repository service functions""" def setUp(self): """Set up test fixtures""" self.test_repo_dir = "test_repos" os.makedirs(self.test_repo_dir, exist_ok=True) def tearDown(self): """Tear down test fixtures""" if os.path.exists(self.test_repo_dir): shutil.rmtree(self.test_repo_dir) def test_validate_github_url(self): """Test validate_github_url function""" # Valid URLs self.assertTrue(validate_github_url("https://github.com/user/repo")) self.assertTrue(validate_github_url("https://github.com/user/repo.git")) self.assertTrue(validate_github_url("git@github.com:user/repo.git")) self.assertTrue(validate_github_url("https://github.com/user/repo-with-dash")) self.assertTrue(validate_github_url("https://github.com/user/repo_with_underscore")) # Invalid URLs self.assertFalse(validate_github_url("https://gitlab.com/user/repo")) self.assertFalse(validate_github_url("https://github.com")) self.assertFalse(validate_github_url("https://github.com/user")) self.assertFalse(validate_github_url("not a url")) def test_normalize_github_url(self): """Test normalize_github_url function""" # HTTPS URLs self.assertEqual( normalize_github_url("https://github.com/user/repo"), "https://github.com/user/repo.git" ) self.assertEqual( normalize_github_url("https://github.com/user/repo.git"), "https://github.com/user/repo.git" ) # SSH URLs self.assertEqual( normalize_github_url("git@github.com:user/repo.git"), "https://github.com/user/repo.git" ) self.assertEqual( normalize_github_url("git@github.com:user/repo"), "https://github.com/user/repo.git" ) # URLs with trailing slashes self.assertEqual( normalize_github_url("https://github.com/user/repo/"), "https://github.com/user/repo.git" ) # Invalid URLs should return None self.assertIsNone(normalize_github_url("https://gitlab.com/user/repo")) self.assertIsNone(normalize_github_url("not a url")) def test_extract_repo_name(self): """Test extract_repo_name function""" self.assertEqual(extract_repo_name("https://github.com/user/repo"), "repo") self.assertEqual(extract_repo_name("https://github.com/user/repo.git"), "repo") self.assertEqual(extract_repo_name("git@github.com:user/repo.git"), "repo") self.assertEqual(extract_repo_name("https://github.com/user/repo-with-dash"), "repo-with-dash") # Invalid URLs should return None self.assertIsNone(extract_repo_name("https://github.com")) self.assertIsNone(extract_repo_name("not a url")) @patch('git.Repo.clone_from') def test_clone_repository(self, mock_clone_from): """Test clone_repository function""" # Mock the Git clone operation mock_repo = MagicMock() mock_clone_from.return_value = mock_repo # Test with default branch repo_path = clone_repository( "https://github.com/user/repo", output_dir=self.test_repo_dir ) # Verify the result expected_path = os.path.join(self.test_repo_dir, "repo") self.assertEqual(repo_path, expected_path) mock_clone_from.assert_called_once() # Test with specific branch mock_clone_from.reset_mock() repo_path = clone_repository( "https://github.com/user/repo", branch="dev", output_dir=self.test_repo_dir ) # Verify the result self.assertEqual(repo_path, expected_path) mock_clone_from.assert_called_once() # Test with invalid URL with self.assertRaises(ValueError): clone_repository( "not a url", output_dir=self.test_repo_dir ) @patch('git.Repo') @patch('os.path.getsize') @patch('os.walk') def test_get_repository_info(self, mock_walk, mock_getsize, mock_repo): """Test get_repository_info function""" # Mock the Git repository mock_repo_instance = MagicMock() mock_repo.return_value = mock_repo_instance # Mock the active branch mock_branch = MagicMock() mock_branch.name = "main" mock_repo_instance.active_branch = mock_branch # Mock the head commit mock_commit = MagicMock() mock_commit.hexsha = "abc123" mock_repo_instance.head.commit = mock_commit # Mock the remote URL mock_remote = MagicMock() mock_remote.url = "https://github.com/user/repo.git" mock_repo_instance.remotes.origin = mock_remote # Mock the repository size mock_getsize.return_value = 1024 # Mock the file count mock_walk.return_value = [ ("/test/repo", ["dir1"], ["file1.py", "file2.py"]), ("/test/repo/dir1", [], ["file3.py"]) ] # Test the function repo_info = get_repository_info("/test/repo") # Verify the result self.assertEqual(repo_info["branch"], "main") self.assertEqual(repo_info["commit"], "abc123") self.assertEqual(repo_info["remote_url"], "https://github.com/user/repo.git") self.assertEqual(repo_info["size"], 1024) self.assertEqual(repo_info["file_count"], 3) @patch('shutil.rmtree') @patch('os.path.exists') def test_cleanup_repository(self, mock_exists, mock_rmtree): """Test cleanup_repository function""" # Mock the path exists check mock_exists.return_value = True # Test the function cleanup_repository("/test/repo") # Verify the result mock_exists.assert_called_once_with("/test/repo") mock_rmtree.assert_called_once_with("/test/repo") # Test with non-existent path mock_exists.reset_mock() mock_rmtree.reset_mock() mock_exists.return_value = False cleanup_repository("/test/repo") mock_exists.assert_called_once_with("/test/repo") mock_rmtree.assert_not_called() @patch('os.listdir') @patch('os.path.isdir') @patch('shutil.rmtree') def test_cleanup_all_repositories(self, mock_rmtree, mock_isdir, mock_listdir): """Test cleanup_all_repositories function""" # Mock the directory listing mock_listdir.return_value = ["repo1", "repo2", "file.txt"] # Mock the isdir check mock_isdir.side_effect = lambda path: path.endswith("repo1") or path.endswith("repo2") # Test the function cleanup_all_repositories(self.test_repo_dir) # Verify the result mock_listdir.assert_called_once_with(self.test_repo_dir) self.assertEqual(mock_isdir.call_count, 3) # Called for each item in the directory self.assertEqual(mock_rmtree.call_count, 2) # Called for each directory if __name__ == "__main__": unittest.main()