22
33import runpy
44import unittest
5- from unittest .mock import MagicMock , call , patch
5+ from unittest .mock import MagicMock , patch
66
77import contributors as contributors_module
88from contributor_stats import ContributorStats
@@ -19,24 +19,27 @@ def test_get_contributors(self, mock_contributor_stats):
1919 Test the get_contributors function.
2020 """
2121 mock_repo = MagicMock ()
22- mock_user = MagicMock ()
23- mock_user .login = "user"
24- mock_user . avatar_url = "https://avatars.githubusercontent.com/u/12345678?v=4"
25- mock_user . contributions_count = 100
26- mock_repo . contributors . return_value = [ mock_user ]
22+ mock_commit = MagicMock ()
23+ mock_commit . author .login = "user"
24+ mock_commit . author . avatar_url = (
25+ "https://avatars.githubusercontent.com/u/12345678?v=4"
26+ )
2727 mock_repo .full_name = "owner/repo"
28- mock_repo .commits .return_value = iter ([object () ])
28+ mock_repo .commits .return_value = iter ([mock_commit ])
2929
30- contributors_module .get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , "" )
30+ result = contributors_module .get_contributors (
31+ mock_repo , "2022-01-01" , "2022-12-31" , ""
32+ )
3133
34+ self .assertEqual (len (result ), 1 )
3235 mock_repo .commits .assert_called_once_with (
33- author = "user" , since = "2022-01-01" , until = "2022-12-31"
36+ since = "2022-01-01" , until = "2022-12-31"
3437 )
3538 mock_contributor_stats .assert_called_once_with (
3639 "user" ,
3740 False ,
3841 "https://avatars.githubusercontent.com/u/12345678?v=4" ,
39- 100 ,
42+ 1 ,
4043 "https://github.com/owner/repo/commits?author=user&since=2022-01-01&until=2022-12-31" ,
4144 "" ,
4245 )
@@ -124,41 +127,34 @@ def test_get_all_contributors_with_repository(self, mock_get_contributors):
124127 )
125128
126129 @patch ("contributors.contributor_stats.ContributorStats" )
127- def test_get_contributors_skip_users_with_no_commits (self , mock_contributor_stats ):
130+ def test_get_contributors_with_single_commit (self , mock_contributor_stats ):
128131 """
129- Test the get_contributors function skips users with no commits in the date range.
132+ Test get_contributors returns a single contributor for one commit in the date range.
130133 """
131134 mock_repo = MagicMock ()
132- mock_user = MagicMock ()
133- mock_user .login = "user"
134- mock_user .avatar_url = "https://avatars.githubusercontent.com/u/12345678?v=4"
135- mock_user .contributions_count = 100
136- mock_user2 = MagicMock ()
137- mock_user2 .login = "user2"
138- mock_user2 .avatar_url = "https://avatars.githubusercontent.com/u/12345679?v=4"
139- mock_user2 .contributions_count = 102
135+ mock_commit = MagicMock ()
136+ mock_commit .author .login = "user"
137+ mock_commit .author .avatar_url = (
138+ "https://avatars.githubusercontent.com/u/12345678?v=4"
139+ )
140140
141- mock_repo .contributors .return_value = [mock_user , mock_user2 ]
142141 mock_repo .full_name = "owner/repo"
143- mock_repo .commits .side_effect = [
144- iter ([object ()]), # user has commits in range
145- iter ([]), # user2 has no commits in range and should be skipped
146- ]
142+ mock_repo .commits .return_value = iter ([mock_commit ])
147143 ghe = ""
148144
149- contributors_module .get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
145+ result = contributors_module .get_contributors (
146+ mock_repo , "2022-01-01" , "2022-12-31" , ghe
147+ )
150148
151- mock_repo .commits .assert_has_calls (
152- [
153- call (author = "user" , since = "2022-01-01" , until = "2022-12-31" ),
154- call (author = "user2" , since = "2022-01-01" , until = "2022-12-31" ),
155- ]
149+ self .assertEqual (len (result ), 1 )
150+ mock_repo .commits .assert_called_once_with (
151+ since = "2022-01-01" , until = "2022-12-31"
156152 )
157153 mock_contributor_stats .assert_called_once_with (
158154 "user" ,
159155 False ,
160156 "https://avatars.githubusercontent.com/u/12345678?v=4" ,
161- 100 ,
157+ 1 ,
162158 "https://github.com/owner/repo/commits?author=user&since=2022-01-01&until=2022-12-31" ,
163159 "" ,
164160 )
@@ -169,19 +165,22 @@ def test_get_contributors_skip_bot(self, mock_contributor_stats):
169165 Test if the get_contributors function skips the bot user.
170166 """
171167 mock_repo = MagicMock ()
172- mock_user = MagicMock ()
173- mock_user .login = "[bot]"
174- mock_user .avatar_url = "https://avatars.githubusercontent.com/u/12345678?v=4"
175- mock_user .contributions_count = 100
168+ mock_commit = MagicMock ()
169+ mock_commit .author .login = "[bot]"
170+ mock_commit .author .avatar_url = (
171+ "https://avatars.githubusercontent.com/u/12345678?v=4"
172+ )
176173
177- mock_repo .contributors .return_value = [mock_user ]
178174 mock_repo .full_name = "owner/repo"
175+ mock_repo .commits .return_value = iter ([mock_commit ])
179176 ghe = ""
180177
181- contributors_module .get_contributors (mock_repo , "2022-01-01" , "2022-12-31" , ghe )
178+ result = contributors_module .get_contributors (
179+ mock_repo , "2022-01-01" , "2022-12-31" , ghe
180+ )
182181
182+ self .assertEqual (result , [])
183183 # Ensure that the bot user is skipped and ContributorStats is never instantiated
184- mock_repo .commits .assert_not_called ()
185184 mock_contributor_stats .assert_not_called ()
186185
187186 @patch ("contributors.contributor_stats.ContributorStats" )
@@ -212,13 +211,8 @@ def test_get_contributors_no_commit_end_date(self, mock_contributor_stats):
212211 )
213212
214213 def test_get_contributors_skips_when_no_commits_in_range (self ):
215- """Test get_contributors skips users with no commits in the date range."""
214+ """Test get_contributors returns empty list when no commits in the date range."""
216215 mock_repo = MagicMock ()
217- mock_user = MagicMock ()
218- mock_user .login = "user"
219- mock_user .avatar_url = "https://avatars.githubusercontent.com/u/12345678?v=4"
220- mock_user .contributions_count = 100
221- mock_repo .contributors .return_value = [mock_user ]
222216 mock_repo .full_name = "owner/repo"
223217 mock_repo .commits .return_value = iter ([])
224218
@@ -228,6 +222,39 @@ def test_get_contributors_skips_when_no_commits_in_range(self):
228222
229223 self .assertEqual (result , [])
230224
225+ def test_get_contributors_skips_none_author (self ):
226+ """Test get_contributors skips commits with no linked GitHub author."""
227+ mock_repo = MagicMock ()
228+ mock_repo .full_name = "owner/repo"
229+ mock_commit = MagicMock ()
230+ mock_commit .author = None
231+ mock_repo .commits .return_value = iter ([mock_commit ])
232+
233+ result = contributors_module .get_contributors (
234+ mock_repo , "2022-01-01" , "2022-12-31" , ""
235+ )
236+
237+ self .assertEqual (result , [])
238+
239+ def test_get_contributors_aggregates_multiple_commits (self ):
240+ """Test get_contributors counts multiple commits per author correctly."""
241+ mock_repo = MagicMock ()
242+ mock_repo .full_name = "owner/repo"
243+ mock_commit1 = MagicMock ()
244+ mock_commit1 .author .login = "user"
245+ mock_commit1 .author .avatar_url = "https://avatars.githubusercontent.com/u/1"
246+ mock_commit2 = MagicMock ()
247+ mock_commit2 .author .login = "user"
248+ mock_commit2 .author .avatar_url = "https://avatars.githubusercontent.com/u/1"
249+ mock_repo .commits .return_value = iter ([mock_commit1 , mock_commit2 ])
250+
251+ result = contributors_module .get_contributors (
252+ mock_repo , "2022-01-01" , "2022-12-31" , ""
253+ )
254+
255+ self .assertEqual (len (result ), 1 )
256+ self .assertEqual (result [0 ].contribution_count , 2 )
257+
231258 def test_get_contributors_handles_exception (self ):
232259 """Test get_contributors returns None when an exception is raised."""
233260
@@ -239,7 +266,7 @@ def __iter__(self):
239266
240267 mock_repo = MagicMock ()
241268 mock_repo .full_name = "owner/repo"
242- mock_repo .contributors .return_value = BoomIterable ()
269+ mock_repo .commits .return_value = BoomIterable ()
243270
244271 with patch ("builtins.print" ) as mock_print :
245272 result = contributors_module .get_contributors (
0 commit comments