diff --git a/acme/src/acme/_internal/tests/errors_test.py b/acme/src/acme/_internal/tests/errors_test.py index 97f739d40..531f16a2f 100644 --- a/acme/src/acme/_internal/tests/errors_test.py +++ b/acme/src/acme/_internal/tests/errors_test.py @@ -51,5 +51,36 @@ class PollErrorTest(unittest.TestCase): 'sentinel.AR2})' % repr(set()) == repr(self.invalid) +class ValidationErrorTest(unittest.TestCase): + """Tests for acme.errors.ValidationError""" + + def setUp(self): + from acme.errors import ValidationError + from acme.challenges import DNS01 + from acme.messages import Error + from acme.messages import Authorization + from acme.messages import AuthorizationResource + from acme.messages import IDENTIFIER_FQDN + from acme.messages import ChallengeBody + from acme.messages import Identifier + self.challenge_error = Error(typ='custom', detail='bar') + failed_authzr = AuthorizationResource( + body=Authorization( + identifier=Identifier(typ=IDENTIFIER_FQDN, value="example.com"), + challenges=[ChallengeBody( + chall=DNS01(), + error=self.challenge_error, + )] + ) + ) + self.error = ValidationError([failed_authzr]) + + def test_repr(self): + err_message = str(self.error) + assert 'Authorization for example.com failed' in err_message + assert 'Challenge dns-01 failed' in err_message + assert str(self.challenge_error) in err_message + + if __name__ == "__main__": sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover diff --git a/acme/src/acme/errors.py b/acme/src/acme/errors.py index b8407da7e..5e0a5379d 100644 --- a/acme/src/acme/errors.py +++ b/acme/src/acme/errors.py @@ -107,6 +107,16 @@ class ValidationError(Error): self.failed_authzrs = failed_authzrs super().__init__() + def __str__(self) -> str: + msg = [] + for authzr in self.failed_authzrs: + msg.append(f'Authorization for {authzr.body.identifier.value} ' \ + 'failed due to one or more failed challenges:') + for challenge in authzr.body.challenges: + msg.append(f' Challenge {challenge.chall.typ} failed ' \ + f'with error {str(challenge.error)}') + return '\n'.join(msg) + class TimeoutError(Error): # pylint: disable=redefined-builtin """Error for when polling an authorization or an order times out."""