@@ -674,3 +674,40 @@ async def test_dns_resolver_manager_missing_loop_data() -> None:
674674
675675 # Verify no exception was raised
676676 assert loop not in manager ._loop_data
677+
678+
679+ @pytest .mark .skipif (not getaddrinfo , reason = "aiodns >=3.2.0 required" )
680+ @pytest .mark .usefixtures ("check_no_lingering_resolvers" )
681+ async def test_async_resolver_close_multiple_times () -> None :
682+ """Test that AsyncResolver.close() can be called multiple times without error."""
683+ with patch ("aiodns.DNSResolver" ) as mock_dns_resolver :
684+ mock_resolver = Mock ()
685+ mock_resolver .cancel = Mock ()
686+ mock_dns_resolver .return_value = mock_resolver
687+
688+ # Create a resolver with custom args (dedicated resolver)
689+ resolver = AsyncResolver (nameservers = ["8.8.8.8" ])
690+
691+ # Close it once
692+ await resolver .close ()
693+ mock_resolver .cancel .assert_called_once ()
694+
695+ # Close it again - should not raise AttributeError
696+ await resolver .close ()
697+ # cancel should still only be called once
698+ mock_resolver .cancel .assert_called_once ()
699+
700+
701+ @pytest .mark .skipif (not getaddrinfo , reason = "aiodns >=3.2.0 required" )
702+ @pytest .mark .usefixtures ("check_no_lingering_resolvers" )
703+ async def test_async_resolver_close_with_none_resolver () -> None :
704+ """Test that AsyncResolver.close() handles None resolver gracefully."""
705+ with patch ("aiodns.DNSResolver" ):
706+ # Create a resolver with custom args (dedicated resolver)
707+ resolver = AsyncResolver (nameservers = ["8.8.8.8" ])
708+
709+ # Manually set resolver to None to simulate edge case
710+ resolver ._resolver = None # type: ignore[assignment]
711+
712+ # This should not raise AttributeError
713+ await resolver .close ()
0 commit comments