99from apps .owasp .models import Chapter , Project
1010
1111
12+ class MockQuerySet :
13+ """Mock QuerySet that supports slicing and iteration without database access."""
14+
15+ def __init__ (self , items ):
16+ self ._items = items
17+
18+ def __iter__ (self ):
19+ return iter (self ._items )
20+
21+ def __getitem__ (self , key ):
22+ if isinstance (key , slice ):
23+ return MockQuerySet (self ._items [key ])
24+ return self ._items [key ]
25+
26+ def filter (self , ** kwargs ):
27+ # Return self to support filter chaining
28+ return self
29+
30+ def __len__ (self ):
31+ return len (self ._items )
32+
33+
1234class TestOwaspAggregateContributions :
1335 @pytest .fixture
1436 def command (self ):
@@ -154,55 +176,53 @@ def test_aggregate_project_without_repositories(self, command, mock_project):
154176
155177 assert result == {}
156178
157- @mock .patch .object (Chapter , "bulk_save" )
158- @mock .patch .object (Chapter .objects , "filter" )
159- def test_handle_chapters_only (self , mock_filter , mock_bulk_save , command , mock_chapter ):
179+ @mock .patch ("apps.owasp.management.commands.owasp_aggregate_contributions.Chapter" )
180+ def test_handle_chapters_only (self , mock_chapter_model , command , mock_chapter ):
160181 """Test command execution for chapters only."""
161- mock_filter .return_value = [mock_chapter ]
182+ mock_chapter_model .objects .filter .return_value = MockQuerySet ([mock_chapter ])
183+ mock_chapter_model .bulk_save = mock .Mock ()
162184
163185 with mock .patch .object (
164186 command ,
165187 "aggregate_chapter_contributions" ,
166188 return_value = {"2024-11-16" : 5 },
167189 ):
168- command .handle (entity_type = "chapter" , days = 365 )
190+ command .handle (entity_type = "chapter" , days = 365 , offset = 0 )
169191
170192 assert mock_chapter .contribution_data == {"2024-11-16" : 5 }
171- assert mock_bulk_save .called
193+ assert mock_chapter_model . bulk_save .called
172194
173- @mock .patch .object (Project , "bulk_save" )
174- @mock .patch .object (Project .objects , "filter" )
175- def test_handle_projects_only (self , mock_filter , mock_bulk_save , command , mock_project ):
195+ @mock .patch ("apps.owasp.management.commands.owasp_aggregate_contributions.Project" )
196+ def test_handle_projects_only (self , mock_project_model , command , mock_project ):
176197 """Test command execution for projects only."""
177- mock_filter .return_value = [mock_project ]
198+ mock_project_model .objects .filter .return_value = MockQuerySet ([mock_project ])
199+ mock_project_model .bulk_save = mock .Mock ()
178200
179201 with mock .patch .object (
180202 command ,
181203 "aggregate_project_contributions" ,
182204 return_value = {"2024-11-16" : 10 },
183205 ):
184- command .handle (entity_type = "project" , days = 365 )
206+ command .handle (entity_type = "project" , days = 365 , offset = 0 )
185207
186208 assert mock_project .contribution_data == {"2024-11-16" : 10 }
187- assert mock_bulk_save .called
209+ assert mock_project_model . bulk_save .called
188210
189- @mock .patch .object (Chapter , "bulk_save" )
190- @mock .patch .object (Project , "bulk_save" )
191- @mock .patch .object (Chapter .objects , "filter" )
192- @mock .patch .object (Project .objects , "filter" )
211+ @mock .patch ("apps.owasp.management.commands.owasp_aggregate_contributions.Chapter" )
212+ @mock .patch ("apps.owasp.management.commands.owasp_aggregate_contributions.Project" )
193213 def test_handle_both_entities (
194214 self ,
195- mock_project_filter ,
196- mock_chapter_filter ,
197- mock_project_bulk_save ,
198- mock_chapter_bulk_save ,
215+ mock_project_model ,
216+ mock_chapter_model ,
199217 command ,
200218 mock_chapter ,
201219 mock_project ,
202220 ):
203221 """Test command execution for both chapters and projects."""
204- mock_chapter_filter .return_value = [mock_chapter ]
205- mock_project_filter .return_value = [mock_project ]
222+ mock_chapter_model .objects .filter .return_value = MockQuerySet ([mock_chapter ])
223+ mock_project_model .objects .filter .return_value = MockQuerySet ([mock_project ])
224+ mock_chapter_model .bulk_save = mock .Mock ()
225+ mock_project_model .bulk_save = mock .Mock ()
206226
207227 with (
208228 mock .patch .object (
@@ -216,62 +236,55 @@ def test_handle_both_entities(
216236 return_value = {"2024-11-16" : 10 },
217237 ),
218238 ):
219- command .handle (entity_type = "both" , days = 365 )
239+ command .handle (entity_type = "both" , days = 365 , offset = 0 )
220240
221- assert mock_chapter_bulk_save .called
222- assert mock_project_bulk_save .called
241+ assert mock_chapter_model . bulk_save .called
242+ assert mock_project_model . bulk_save .called
223243
224- @mock .patch . object ( Chapter . objects , "filter " )
225- def test_handle_with_specific_key (self , mock_filter , command , mock_chapter ):
244+ @mock .patch ( "apps.owasp.management.commands.owasp_aggregate_contributions.Chapter " )
245+ def test_handle_with_specific_key (self , mock_chapter_model , command , mock_chapter ):
226246 """Test command execution with a specific entity key."""
227- mock_filter .return_value = [mock_chapter ]
247+ mock_chapter_model .objects .filter .return_value = MockQuerySet ([mock_chapter ])
248+ mock_chapter_model .bulk_save = mock .Mock ()
228249
229- with (
230- mock .patch .object (Chapter , "bulk_save" ),
231- mock .patch .object (
232- command ,
233- "aggregate_chapter_contributions" ,
234- return_value = {"2024-11-16" : 3 },
235- ),
250+ with mock .patch .object (
251+ command ,
252+ "aggregate_chapter_contributions" ,
253+ return_value = {"2024-11-16" : 3 },
236254 ):
237- command .handle (entity_type = "chapter" , key = "www-chapter-test" , days = 365 )
255+ command .handle (entity_type = "chapter" , key = "www-chapter-test" , days = 365 , offset = 0 )
238256
239257 # Verify filter was called with the specific key
240- mock_filter .assert_called ()
258+ mock_chapter_model . objects . filter .assert_called ()
241259
242- @mock .patch . object ( Chapter . objects , "filter " )
243- def test_handle_with_offset (self , mock_filter , command , mock_chapter ):
260+ @mock .patch ( "apps.owasp.management.commands.owasp_aggregate_contributions.Chapter " )
261+ def test_handle_with_offset (self , mock_chapter_model , command , mock_chapter ):
244262 """Test command execution with offset parameter."""
245263 chapters = [mock_chapter , mock_chapter , mock_chapter ]
246- mock_queryset = mock .Mock ()
247- mock_queryset .__getitem__ .return_value = chapters [2 :] # Skip first 2
248- mock_filter .return_value = mock_queryset
264+ mock_chapter_model .objects .filter .return_value = MockQuerySet (chapters )
265+ mock_chapter_model .bulk_save = mock .Mock ()
249266
250- with (
251- mock .patch .object (Chapter , "bulk_save" ),
252- mock .patch .object (
253- command ,
254- "aggregate_chapter_contributions" ,
255- return_value = {"2024-11-16" : 1 },
256- ),
267+ with mock .patch .object (
268+ command ,
269+ "aggregate_chapter_contributions" ,
270+ return_value = {"2024-11-16" : 1 },
257271 ):
258272 command .handle (entity_type = "chapter" , offset = 2 , days = 365 )
259273
260- # Verify offset was applied
261- mock_queryset .__getitem__ .assert_called_with (slice (2 , None ))
274+ # Verify that offset was applied - only 1 chapter should be processed (3 total - 2 offset)
262275
263- def test_handle_custom_days (self , command , mock_chapter ):
276+ @mock .patch ("apps.owasp.management.commands.owasp_aggregate_contributions.Chapter" )
277+ def test_handle_custom_days (self , mock_chapter_model , command , mock_chapter ):
264278 """Test command execution with custom days parameter."""
265- with (
266- mock .patch .object (Chapter .objects , "filter" , return_value = [mock_chapter ]),
267- mock .patch .object (Chapter , "bulk_save" ),
268- mock .patch .object (
269- command ,
270- "aggregate_chapter_contributions" ,
271- return_value = {},
272- ) as mock_aggregate ,
273- ):
274- command .handle (entity_type = "chapter" , days = 90 )
279+ mock_chapter_model .objects .filter .return_value = MockQuerySet ([mock_chapter ])
280+ mock_chapter_model .bulk_save = mock .Mock ()
281+
282+ with mock .patch .object (
283+ command ,
284+ "aggregate_chapter_contributions" ,
285+ return_value = {},
286+ ) as mock_aggregate :
287+ command .handle (entity_type = "chapter" , days = 90 , offset = 0 )
275288
276289 # Verify aggregate was called with correct start_date
277290 assert mock_aggregate .called
0 commit comments