11import tempfile
22import os
3+ import json
34
4- from unittest .mock import call , patch
5+ from unittest .mock import call , patch , Mock , MagicMock
56
67os .environ ["AWS_DEFAULT_REGION" ] = "ca-central-1"
78os .environ ["ATHENA_OUTPUT_BUCKET" ] = "test_bucket"
@@ -31,6 +32,7 @@ def test_handler_with_ips_to_block(mock_waf_client, mock_athena_client, capsys):
3132 {"Data" : [{"VarCharValue" : "header" }]},
3233 {"Data" : [{"VarCharValue" : "192.168.1.1" }]},
3334 {"Data" : [{"VarCharValue" : "192.168.1.2" }]},
35+ {"Data" : [{"VarCharValue" : "198.103.1.2" }]},
3436 ]
3537 }
3638 },
@@ -40,6 +42,7 @@ def test_handler_with_ips_to_block(mock_waf_client, mock_athena_client, capsys):
4042 {"Data" : [{"VarCharValue" : "header" }]},
4143 {"Data" : [{"VarCharValue" : "192.168.1.1" }]},
4244 {"Data" : [{"VarCharValue" : "192.168.1.3" }]},
45+ {"Data" : [{"VarCharValue" : "205.193.1.2" }]},
4346 ]
4447 }
4548 },
@@ -282,3 +285,213 @@ def test_get_query_from_file_with_single_rule_id():
282285
283286 # Cleanup
284287 os .remove (temp_file_path )
288+
289+
290+ def test_recursive_entity_search_with_list_and_goc_handle ():
291+ """Test recursive_entity_search with a list containing GoC handle"""
292+ test_data = [
293+ {"roles" : ["registrant" ], "handle" : "SSC-299" },
294+ {"roles" : ["admin" ], "handle" : "OTHER-123" },
295+ ]
296+
297+ result = blocklist .recursive_entity_search (test_data )
298+ assert result is True
299+
300+
301+ def test_recursive_entity_search_with_list_without_goc_handle ():
302+ """Test recursive_entity_search with a list not containing GoC handle"""
303+ test_data = [
304+ {"roles" : ["registrant" ], "handle" : "OTHER-123" },
305+ {"roles" : ["admin" ], "handle" : "ANOTHER-456" },
306+ ]
307+
308+ result = blocklist .recursive_entity_search (test_data )
309+ assert result is False
310+
311+
312+ def test_recursive_entity_search_with_nested_entities ():
313+ """Test recursive_entity_search with nested entities containing GoC handle"""
314+ test_data = [
315+ {
316+ "roles" : ["registrant" ],
317+ "handle" : "OTHER-123" ,
318+ "entities" : [{"roles" : ["registrant" ], "handle" : "SSC-299" }],
319+ }
320+ ]
321+
322+ result = blocklist .recursive_entity_search (test_data )
323+ assert result is True
324+
325+
326+ def test_recursive_entity_search_with_dict_containing_entities ():
327+ """Test recursive_entity_search with dict containing entities key"""
328+ test_data = {"entities" : [{"roles" : ["registrant" ], "handle" : "SSC-299" }]}
329+
330+ result = blocklist .recursive_entity_search (test_data )
331+ assert result is True
332+
333+
334+ def test_recursive_entity_search_with_empty_list ():
335+ """Test recursive_entity_search with empty list"""
336+ test_data = []
337+
338+ result = blocklist .recursive_entity_search (test_data )
339+ assert result is False
340+
341+
342+ def test_recursive_entity_search_with_none ():
343+ """Test recursive_entity_search with None input"""
344+ result = blocklist .recursive_entity_search (None )
345+ assert result is False
346+
347+
348+ def test_recursive_entity_search_with_no_registrants ():
349+ """Test recursive_entity_search with list containing no registrants"""
350+ test_data = [
351+ {"roles" : ["admin" ], "handle" : "SSC-299" },
352+ {"roles" : ["tech" ], "handle" : "OTHER-123" },
353+ ]
354+
355+ result = blocklist .recursive_entity_search (test_data )
356+ assert result is False
357+
358+
359+ def test_recursive_entity_search_with_missing_roles ():
360+ """Test recursive_entity_search with entities missing roles field"""
361+ test_data = [
362+ {"handle" : "SSC-299" }, # Missing roles field
363+ {"roles" : ["admin" ], "handle" : "OTHER-123" },
364+ ]
365+
366+ result = blocklist .recursive_entity_search (test_data )
367+ assert result is False
368+
369+
370+ @patch ("blocklist.create_retrying_request" )
371+ def test_gc_ip_success_with_goc_ip (mock_create_session ):
372+ """Test gc_ip with successful response indicating GoC IP"""
373+ # Setup mock response
374+ mock_response = Mock ()
375+ mock_response .ok = True
376+ mock_response .json .return_value = {
377+ "entities" : [{"roles" : ["registrant" ], "handle" : "SSC-299" }]
378+ }
379+
380+ # Setup mock session
381+ mock_session = Mock ()
382+ mock_session .get .return_value = mock_response
383+ mock_create_session .return_value = mock_session
384+
385+ # Test
386+ result = blocklist .gc_ip ("192.168.1.1" )
387+
388+ # Verify
389+ assert result is True
390+ mock_create_session .assert_called_once_with (total_retries = 5 , backoff_factor = 1 )
391+ mock_session .get .assert_called_once_with (
392+ "https://rdap.arin.net/registry/ip/192.168.1.1" , timeout = 5
393+ )
394+
395+
396+ @patch ("blocklist.create_retrying_request" )
397+ def test_gc_ip_success_with_non_goc_ip (mock_create_session ):
398+ """Test gc_ip with successful response indicating non-GoC IP"""
399+ # Setup mock response
400+ mock_response = Mock ()
401+ mock_response .ok = True
402+ mock_response .json .return_value = {
403+ "entities" : [{"roles" : ["registrant" ], "handle" : "OTHER-123" }]
404+ }
405+
406+ # Setup mock session
407+ mock_session = Mock ()
408+ mock_session .get .return_value = mock_response
409+ mock_create_session .return_value = mock_session
410+
411+ # Test
412+ result = blocklist .gc_ip ("8.8.8.8" )
413+
414+ # Verify
415+ assert result is False
416+
417+
418+ @patch ("blocklist.create_retrying_request" )
419+ def test_gc_ip_http_error (mock_create_session ):
420+ """Test gc_ip with HTTP error response"""
421+ # Setup mock response
422+ mock_response = Mock ()
423+ mock_response .ok = False
424+ mock_response .status_code = 404
425+
426+ # Setup mock session
427+ mock_session = Mock ()
428+ mock_session .get .return_value = mock_response
429+ mock_create_session .return_value = mock_session
430+
431+ # Test
432+ result = blocklist .gc_ip ("192.168.1.1" )
433+
434+ # Verify
435+ assert result is False
436+
437+
438+ @patch ("blocklist.create_retrying_request" )
439+ def test_gc_ip_request_exception (mock_create_session , capsys ):
440+ """Test gc_ip with request exception"""
441+ # Setup mock session to raise exception
442+ mock_session = Mock ()
443+ mock_session .get .side_effect = blocklist .requests .exceptions .RequestException (
444+ "Connection error"
445+ )
446+ mock_create_session .return_value = mock_session
447+
448+ # Test
449+ result = blocklist .gc_ip ("192.168.1.1" )
450+
451+ # Verify
452+ assert result is False
453+ captured = capsys .readouterr ()
454+ assert (
455+ "Could not successfully retrieve information about IP 192.168.1.1"
456+ in captured .out
457+ )
458+
459+
460+ @patch ("blocklist.create_retrying_request" )
461+ def test_gc_ip_no_entities_in_response (mock_create_session ):
462+ """Test gc_ip when response has no entities"""
463+ # Setup mock response
464+ mock_response = Mock ()
465+ mock_response .ok = True
466+ mock_response .json .return_value = {"other_field" : "value" }
467+
468+ # Setup mock session
469+ mock_session = Mock ()
470+ mock_session .get .return_value = mock_response
471+ mock_create_session .return_value = mock_session
472+
473+ # Test
474+ result = blocklist .gc_ip ("192.168.1.1" )
475+
476+ # Verify
477+ assert result is False
478+
479+
480+ @patch ("blocklist.create_retrying_request" )
481+ def test_gc_ip_entities_is_none (mock_create_session ):
482+ """Test gc_ip when entities field is None"""
483+ # Setup mock response
484+ mock_response = Mock ()
485+ mock_response .ok = True
486+ mock_response .json .return_value = {"entities" : None }
487+
488+ # Setup mock session
489+ mock_session = Mock ()
490+ mock_session .get .return_value = mock_response
491+ mock_create_session .return_value = mock_session
492+
493+ # Test
494+ result = blocklist .gc_ip ("192.168.1.1" )
495+
496+ # Verify
497+ assert result is False
0 commit comments