1+ import pytest
2+ from unittest .mock import MagicMock , patch , PropertyMock
3+ from llama_github .data_retrieval .github_api import GitHubAPIHandler
4+ from llama_github .data_retrieval .github_entities import Repository , RepositoryPool
5+
6+ class TestRepository :
7+ def test_repository_initialization (self , mock_github_instance , mock_repo_object ):
8+ mock_github_instance .get_repo .return_value = mock_repo_object
9+
10+ repo = Repository ("owner/test-repo" , mock_github_instance )
11+
12+ assert repo .full_name == "owner/test-repo"
13+ assert repo .id == 12345
14+ assert repo .language == "Python"
15+
16+ def test_get_readme_caching (self , mock_github_instance , mock_repo_object ):
17+ mock_github_instance .get_repo .return_value = mock_repo_object
18+ repo = Repository ("owner/test-repo" , mock_github_instance )
19+
20+ # Mock the internal repo object's get_readme
21+ mock_readme = MagicMock ()
22+ mock_readme .decoded_content = b"# Readme"
23+ mock_repo_object .get_readme .return_value = mock_readme
24+
25+ # First call
26+ content1 = repo .get_readme ()
27+ assert content1 == "# Readme"
28+
29+ # Second call should not trigger API
30+ content2 = repo .get_readme ()
31+ assert content2 == "# Readme"
32+ assert mock_repo_object .get_readme .call_count == 1
33+
34+ def test_get_file_content_base64 (self , mock_github_instance , mock_repo_object , mock_content_file ):
35+ mock_github_instance .get_repo .return_value = mock_repo_object
36+ mock_repo_object .get_contents .return_value = mock_content_file
37+
38+ repo = Repository ("owner/test-repo" , mock_github_instance )
39+ content = repo .get_file_content ("src/test.py" )
40+
41+ assert content == 'print("hello")'
42+
43+ class TestRepositoryPool :
44+ def setup_method (self ):
45+ # Reset singleton for testing
46+ RepositoryPool ._instance = None
47+ RepositoryPool ._instance_lock = MagicMock () # Reset lock mock if needed
48+
49+ def test_singleton_behavior (self , mock_github_instance ):
50+ pool1 = RepositoryPool (mock_github_instance )
51+ pool2 = RepositoryPool (mock_github_instance )
52+ assert pool1 is pool2
53+
54+ def test_get_repository_caching (self , mock_github_instance , mock_repo_object ):
55+ mock_github_instance .get_repo .return_value = mock_repo_object
56+ pool = RepositoryPool (mock_github_instance )
57+
58+ repo1 = pool .get_repository ("owner/test-repo" )
59+ repo2 = pool .get_repository ("owner/test-repo" )
60+
61+ assert repo1 is repo2
62+ # Ensure we didn't create two Repository objects
63+ assert len (pool ._pool ) == 1
64+
65+ def test_cleanup_logic (self , mock_github_instance ):
66+ # This test requires careful mocking of time and threads
67+ # We will mock the _cleanup method to avoid thread waiting
68+ pool = RepositoryPool (mock_github_instance , cleanup_interval = 0.1 , max_idle_time = 0.1 )
69+ pool .stop_cleanup () # Stop the real thread immediately
70+
71+ # Manually insert an expired repo
72+ mock_repo = MagicMock ()
73+ mock_repo .last_read_time = datetime (2000 , 1 , 1 , tzinfo = timezone .utc )
74+ mock_repo .creation_time = datetime (2000 , 1 , 1 , tzinfo = timezone .utc )
75+
76+ with pool ._registry_lock :
77+ pool ._pool ["expired/repo" ] = mock_repo
78+ pool ._locks_registry ["expired/repo" ] = MagicMock ()
79+
80+ # Manually invoke cleanup logic once
81+ with patch ('llama_github.data_retrieval.github_entities.datetime' ) as mock_dt :
82+ mock_dt .now .return_value = datetime (2025 , 1 , 1 , tzinfo = timezone .utc )
83+
84+ # Extract the logic from _cleanup loop for testing
85+ with pool ._registry_lock :
86+ current_time = mock_dt .now (timezone .utc )
87+ if (current_time - mock_repo .last_read_time ).total_seconds () > pool .max_idle_time :
88+ del pool ._locks_registry ["expired/repo" ]
89+ mock_repo .clear_cache ()
90+
91+ mock_repo .clear_cache .assert_called ()
92+
93+ class TestGitHubAPIHandler :
94+ def test_search_code_integration (self , mock_github_instance ):
95+ handler = GitHubAPIHandler (mock_github_instance )
96+
97+ # Mock search_code response
98+ mock_code_result = MagicMock ()
99+ mock_code_result .name = "test.py"
100+ mock_code_result .path = "test.py"
101+ mock_code_result .repository .full_name = "owner/repo"
102+ mock_code_result .html_url = "http://url"
103+
104+ mock_github_instance .search_code .return_value = [mock_code_result ]
105+
106+ # Mock RepositoryPool to return a mock repo that returns content
107+ with patch .object (handler , 'get_repository' ) as mock_get_repo :
108+ mock_repo = MagicMock ()
109+ mock_repo .get_file_content .return_value = "content"
110+ mock_get_repo .return_value = mock_repo
111+
112+ results = handler .search_code ("query" )
113+
114+ assert len (results ) == 1
115+ assert results [0 ]['content' ] == "content"
0 commit comments