from __future__ import absolute_import, division, print_function __metaclass__ = type import pytest from mock import MagicMock from ansible_collections.community.crypto.plugins.module_utils.acme.errors import ( format_error_problem, ACMEProtocolException, ) TEST_FORMAT_ERROR_PROBLEM = [ ( { 'type': 'foo', }, '', 'Error foo' ), ( { 'type': 'foo', 'title': 'bar' }, '', 'Error "bar" (foo)' ), ( { 'type': 'foo', 'detail': 'bar baz' }, '', 'Error foo: "bar baz"' ), ( { 'type': 'foo', 'subproblems': [] }, '', 'Error foo Subproblems:' ), ( { 'type': 'foo', 'subproblems': [ { 'type': 'bar', }, ] }, '', 'Error foo Subproblems:\n(0) Error bar' ), ( { 'type': 'foo', 'subproblems': [ { 'type': 'bar', 'subproblems': [ { 'type': 'baz', }, ] }, ] }, '', 'Error foo Subproblems:\n(0) Error bar Subproblems:\n(0.0) Error baz' ), ( { 'type': 'foo', 'title': 'Foo Error', 'detail': 'Foo went wrong', 'subproblems': [ { 'type': 'bar', 'detail': 'Bar went wrong', 'subproblems': [ { 'type': 'baz', 'title': 'Baz Error', }, ] }, { 'type': 'bar2', 'title': 'Bar 2 Error', 'detail': 'Bar really went wrong' }, ] }, 'X.', 'Error "Foo Error" (foo): "Foo went wrong" Subproblems:\n' '(X.0) Error bar: "Bar went wrong" Subproblems:\n' '(X.0.0) Error "Baz Error" (baz)\n' '(X.1) Error "Bar 2 Error" (bar2): "Bar really went wrong"' ), ] @pytest.mark.parametrize("problem, subproblem_prefix, result", TEST_FORMAT_ERROR_PROBLEM) def test_format_error_problem(problem, subproblem_prefix, result): res = format_error_problem(problem, subproblem_prefix) assert res == result def create_regular_response(response_text): response = MagicMock() response.read = MagicMock(return_value=response_text.encode('utf-8')) response.closed = False return response def create_error_response(): response = MagicMock() response.read = MagicMock(side_effect=AttributeError('read')) response.closed = True return response def create_decode_error(msg): def f(content): raise Exception(msg) return f TEST_ACME_PROTOCOL_EXCEPTION = [ ( {}, None, 'ACME request failed.', { }, ), ( { 'msg': 'Foo', 'extras': { 'foo': 'bar', }, }, None, 'Foo.', { 'foo': 'bar', }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, }, None, 'ACME request failed for https://ca.example.com/foo with HTTP status 201.', { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'response': create_regular_response('xxx'), }, None, 'ACME request failed for https://ca.example.com/foo with HTTP status 201. The raw error result: xxx', { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'response': create_regular_response('xxx'), }, create_decode_error('yyy'), 'ACME request failed for https://ca.example.com/foo with HTTP status 201. The raw error result: xxx', { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'response': create_regular_response('xxx'), }, lambda content: dict(foo='bar'), "ACME request failed for https://ca.example.com/foo with HTTP status 201. The JSON error result: {'foo': 'bar'}", { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'response': create_error_response(), }, None, 'ACME request failed for https://ca.example.com/foo with HTTP status 201.', { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, 'body': 'xxx', }, 'response': create_error_response(), }, lambda content: dict(foo='bar'), "ACME request failed for https://ca.example.com/foo with HTTP status 201. The JSON error result: {'foo': 'bar'}", { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'content': 'xxx', }, None, "ACME request failed for https://ca.example.com/foo with HTTP status 201. The raw error result: xxx", { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 400, }, 'content_json': { 'foo': 'bar', }, 'extras': { 'bar': 'baz', } }, None, "ACME request failed for https://ca.example.com/foo with HTTP status 400. The JSON error result: {'foo': 'bar'}", { 'http_url': 'https://ca.example.com/foo', 'http_status': 400, 'bar': 'baz', }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 201, }, 'content_json': { 'type': 'foo', }, }, None, "ACME request failed for https://ca.example.com/foo with HTTP status 201. The JSON error result: {'type': 'foo'}", { 'http_url': 'https://ca.example.com/foo', 'http_status': 201, }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 400, }, 'content_json': { 'type': 'foo', }, }, None, "ACME request failed for https://ca.example.com/foo with status 400. Error foo.", { 'http_url': 'https://ca.example.com/foo', 'http_status': 400, 'problem': { 'type': 'foo', }, 'subproblems': [], }, ), ( { 'info': { 'url': 'https://ca.example.com/foo', 'status': 400, }, 'content_json': { 'type': 'foo', 'title': 'Foo Error', 'subproblems': [ { 'type': 'bar', 'detail': 'This is a bar error', 'details': 'Details.', }, ], }, }, None, "ACME request failed for https://ca.example.com/foo with status 400. Error \"Foo Error\" (foo). Subproblems:\n" "(0) Error bar: \"This is a bar error\".", { 'http_url': 'https://ca.example.com/foo', 'http_status': 400, 'problem': { 'type': 'foo', 'title': 'Foo Error', }, 'subproblems': [ { 'type': 'bar', 'detail': 'This is a bar error', 'details': 'Details.', }, ], }, ), ] @pytest.mark.parametrize("input, from_json, msg, args", TEST_ACME_PROTOCOL_EXCEPTION) def test_acme_protocol_exception(input, from_json, msg, args): if from_json is None: module = None else: module = MagicMock() module.from_json = from_json with pytest.raises(ACMEProtocolException) as exc: raise ACMEProtocolException(module, **input) print(exc.value.msg) print(exc.value.module_fail_args) print(msg) print(args) assert exc.value.msg == msg assert exc.value.module_fail_args == args