11"""This module contains the tests for the contributors.py module"""
22
3+ import runpy
34import unittest
4- from unittest .mock import MagicMock , patch
5+ from unittest .mock import MagicMock , call , patch
56
7+ import contributors as contributors_module
68from contributor_stats import ContributorStats
7- from contributors import get_all_contributors , get_contributors
89
910
1011class TestContributors (unittest .TestCase ):
@@ -25,7 +26,7 @@ def test_get_contributors(self, mock_contributor_stats):
2526 mock_repo .contributors .return_value = [mock_user ]
2627 mock_repo .full_name = "owner/repo"
2728
28- get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , "" )
29+ contributors_module . get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , "" )
2930
3031 mock_contributor_stats .assert_called_once_with (
3132 "user" ,
@@ -58,8 +59,8 @@ def test_get_all_contributors_with_organization(self, mock_get_contributors):
5859 ]
5960 ghe = ""
6061
61- result = get_all_contributors (
62- "org" , "" , "2022-01-01" , "2022-12-31" , mock_github_connection , ghe
62+ result = contributors_module . get_all_contributors (
63+ "org" , [] , "2022-01-01" , "2022-12-31" , mock_github_connection , ghe
6364 )
6465
6566 self .assertEqual (
@@ -97,7 +98,7 @@ def test_get_all_contributors_with_repository(self, mock_get_contributors):
9798 ]
9899 ghe = ""
99100
100- result = get_all_contributors (
101+ result = contributors_module . get_all_contributors (
101102 "" , ["owner/repo" ], "2022-01-01" , "2022-12-31" , mock_github_connection , ghe
102103 )
103104
@@ -133,14 +134,22 @@ def test_get_contributors_skip_users_with_no_commits(self, mock_contributor_stat
133134 mock_user2 .avatar_url = "https://avatars.githubusercontent.com/u/12345679?v=4"
134135 mock_user2 .contributions_count = 102
135136
136- mock_repo .contributors .return_value = [mock_user ]
137+ mock_repo .contributors .return_value = [mock_user , mock_user2 ]
137138 mock_repo .full_name = "owner/repo"
138- mock_repo .get_commits .side_effect = StopIteration
139+ mock_repo .commits .side_effect = [
140+ iter ([object ()]), # user has commits in range
141+ iter ([]), # user2 has no commits in range and should be skipped
142+ ]
139143 ghe = ""
140144
141- get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
145+ contributors_module . get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
142146
143- # Note that only user is returned and user2 is not returned here because there were no commits in the date range
147+ mock_repo .commits .assert_has_calls (
148+ [
149+ call (author = "user" , since = "2022-01-01" , until = "2022-12-31" ),
150+ call (author = "user2" , since = "2022-01-01" , until = "2022-12-31" ),
151+ ]
152+ )
144153 mock_contributor_stats .assert_called_once_with (
145154 "user" ,
146155 False ,
@@ -163,13 +172,13 @@ def test_get_contributors_skip_bot(self, mock_contributor_stats):
163172
164173 mock_repo .contributors .return_value = [mock_user ]
165174 mock_repo .full_name = "owner/repo"
166- mock_repo .get_commits .side_effect = StopIteration
167175 ghe = ""
168176
169- get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
177+ contributors_module . get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
170178
171- # Note that only user is returned and user2 is not returned here because there were no commits in the date range
172- mock_contributor_stats .isEmpty ()
179+ # Ensure that the bot user is skipped and ContributorStats is never instantiated
180+ mock_repo .commits .assert_not_called ()
181+ mock_contributor_stats .assert_not_called ()
173182
174183 @patch ("contributors.contributor_stats.ContributorStats" )
175184 def test_get_contributors_no_commit_end_date (self , mock_contributor_stats ):
@@ -187,7 +196,7 @@ def test_get_contributors_no_commit_end_date(self, mock_contributor_stats):
187196 mock_repo .get_commits .side_effect = StopIteration
188197 ghe = ""
189198
190- get_contributors (mock_repo , "2022-01-01" , "" , ghe )
199+ contributors_module . get_contributors (mock_repo , "2022-01-01" , "" , ghe )
191200
192201 # Note that only user is returned and user2 is not returned here because there were no commits in the date range
193202 mock_contributor_stats .assert_called_once_with (
@@ -199,6 +208,143 @@ def test_get_contributors_no_commit_end_date(self, mock_contributor_stats):
199208 "" ,
200209 )
201210
211+ def test_get_contributors_skips_when_no_commits_in_range (self ):
212+ """Test get_contributors skips users with no commits in the date range."""
213+ mock_repo = MagicMock ()
214+ mock_user = MagicMock ()
215+ mock_user .login = "user"
216+ mock_user .avatar_url = "https://avatars.githubusercontent.com/u/12345678?v=4"
217+ mock_user .contributions_count = 100
218+ mock_repo .contributors .return_value = [mock_user ]
219+ mock_repo .full_name = "owner/repo"
220+ mock_repo .commits .return_value = iter ([])
221+
222+ result = contributors_module .get_contributors (
223+ mock_repo , "2022-01-01" , "2022-12-31" , ""
224+ )
225+
226+ self .assertEqual (result , [])
227+
228+ def test_get_contributors_handles_exception (self ):
229+ """Test get_contributors returns None when an exception is raised."""
230+
231+ class BoomIterable : # pylint: disable=too-few-public-methods
232+ """Iterable that raises an exception when iterated over."""
233+
234+ def __iter__ (self ):
235+ raise RuntimeError ("boom" )
236+
237+ mock_repo = MagicMock ()
238+ mock_repo .full_name = "owner/repo"
239+ mock_repo .contributors .return_value = BoomIterable ()
240+
241+ with patch ("builtins.print" ) as mock_print :
242+ result = contributors_module .get_contributors (
243+ mock_repo , "2022-01-01" , "2022-12-31" , ""
244+ )
245+
246+ self .assertIsNone (result )
247+ mock_print .assert_any_call (
248+ "Error getting contributors for repository: owner/repo"
249+ )
250+
251+ def test_main_runs_under_main_guard (self ):
252+ """Test running contributors as a script executes main."""
253+ mock_env = MagicMock ()
254+ mock_env .get_env_vars .return_value = (
255+ "org" ,
256+ [],
257+ 123 ,
258+ 456 ,
259+ b"key" ,
260+ False ,
261+ "" ,
262+ "" ,
263+ "2022-01-01" ,
264+ "2022-12-31" ,
265+ "true" ,
266+ False ,
267+ )
268+
269+ mock_auth = MagicMock ()
270+ mock_github = MagicMock ()
271+ mock_org = MagicMock ()
272+ mock_org .repositories .return_value = []
273+ mock_github .organization .return_value = mock_org
274+ mock_auth .auth_to_github .return_value = mock_github
275+ mock_auth .get_github_app_installation_token .return_value = "token"
276+
277+ mock_markdown = MagicMock ()
278+ mock_json_writer = MagicMock ()
279+
280+ with patch .dict (
281+ "sys.modules" ,
282+ {
283+ "env" : mock_env ,
284+ "auth" : mock_auth ,
285+ "markdown" : mock_markdown ,
286+ "json_writer" : mock_json_writer ,
287+ },
288+ clear = False ,
289+ ):
290+ runpy .run_module ("contributors" , run_name = "__main__" )
291+
292+ mock_env .get_env_vars .assert_called_once ()
293+ mock_auth .auth_to_github .assert_called_once ()
294+ mock_auth .get_github_app_installation_token .assert_called_once_with (
295+ "" , 123 , b"key" , 456
296+ )
297+ mock_markdown .write_to_markdown .assert_called_once ()
298+ mock_json_writer .write_to_json .assert_called_once ()
299+
300+ def test_main_sets_new_contributor_flag (self ):
301+ """Test main sets new_contributor when start/end dates are provided."""
302+ contributor = ContributorStats (
303+ "user1" ,
304+ False ,
305+ "https://avatars.githubusercontent.com/u/1" ,
306+ 10 ,
307+ "commit_url" ,
308+ "" ,
309+ )
310+
311+ with patch .object (
312+ contributors_module .env , "get_env_vars"
313+ ) as mock_get_env_vars , patch .object (
314+ contributors_module .auth , "auth_to_github"
315+ ) as mock_auth_to_github , patch .object (
316+ contributors_module , "get_all_contributors"
317+ ) as mock_get_all_contributors , patch .object (
318+ contributors_module .contributor_stats ,
319+ "is_new_contributor" ,
320+ return_value = True ,
321+ ) as mock_is_new , patch .object (
322+ contributors_module .markdown , "write_to_markdown"
323+ ), patch .object (
324+ contributors_module .json_writer , "write_to_json"
325+ ):
326+ mock_get_env_vars .return_value = (
327+ "org" ,
328+ [],
329+ None ,
330+ None ,
331+ b"" ,
332+ False ,
333+ "token" ,
334+ "" ,
335+ "2022-01-01" ,
336+ "2022-12-31" ,
337+ False ,
338+ False ,
339+ )
340+ mock_auth_to_github .return_value = MagicMock ()
341+ mock_get_all_contributors .side_effect = [[contributor ], []]
342+
343+ contributors_module .main ()
344+
345+ mock_is_new .assert_called_once_with ("user1" , [])
346+ self .assertTrue (contributor .new_contributor )
347+
202348
203349if __name__ == "__main__" :
204350 unittest .main ()
0 commit comments