File size: 8,038 Bytes
88d205f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#!/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("[email protected]: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("[email protected]:user/repo.git"),
            "https://github.com/user/repo.git"
        )
        self.assertEqual(
            normalize_github_url("[email protected]: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("[email protected]: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()