Files
community.crypto/tests/unit/plugins/module_utils/acme/test_errors.py
2025-05-02 15:39:03 +02:00

359 lines
9.6 KiB
Python

# Copyright (c) Ansible Project
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
from __future__ import annotations
from unittest.mock import (
MagicMock,
)
import pytest
from ansible_collections.community.crypto.plugins.module_utils.acme.errors import (
ACMEProtocolException,
format_error_problem,
)
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 Created.",
{
"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 Created. 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 Created. 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 Created. 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 Created.",
{
"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 Created. 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 Created. 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 Bad Request. 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 Created. 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 Bad Request. 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 Bad Request. 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