Passed
Push — master ( 99f654...da6e57 )
by Mingyu
01:48 queued 52s
created

{{cookiecutter.project_slug}}.tests.app.hooks.test_error   A

Complexity

Total Complexity 8

Size/Duplication

Total Lines 107
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 8
eloc 70
dl 0
loc 107
rs 10
c 0
b 0
f 0

5 Methods

Rating   Name   Duplication   Size   Complexity  
A TestBroadExceptionHandler.test_500() 0 9 1
A TestError.setUp() 0 4 1
A TestBroadExceptionHandler.test_http_exception() 0 18 4
A TestBroadExceptionHandler.test_pydantic_validation_error() 0 34 1
A TestError.add_route_raises_exception() 0 13 1
1
from dataclasses import dataclass
2
from typing import Union, Callable, Type
3
4
from flask import Flask
5
from pydantic import ValidationError, NoneIsNotAllowedError, BoolError, BaseModel
6
from pydantic.error_wrappers import ErrorWrapper
7
from werkzeug.exceptions import HTTPException
8
from werkzeug.routing import RequestRedirect
9
10
from app.hooks.error import broad_exception_handler
11
from tests import BaseTestCase
12
13
14
@dataclass
15
class ExceptionHandlingSpec:
16
    error_handler_exception_or_code: Union[Type[Exception], int]
17
    error_handler_func: Callable
18
    exception_instance_to_handler_raises: Exception
19
20
21
class TestError(BaseTestCase):
22
    def setUp(self):
23
        super(TestError, self).setUp()
24
25
        self.path = "/foo"
26
27
    def add_route_raises_exception(
28
        self, exception_handling_spec: ExceptionHandlingSpec
29
    ):
30
        self.app = Flask(__name__)
31
        self.app.register_error_handler(
32
            exception_handling_spec.error_handler_exception_or_code,
33
            exception_handling_spec.error_handler_func,
34
        )
35
        self.client = self.app.test_client()
36
37
        @self.app.route(self.path)
38
        def handler():
39
            raise exception_handling_spec.exception_instance_to_handler_raises
40
41
42
class TestBroadExceptionHandler(TestError):
43
    def test_http_exception(self):
44
        for exception_cls in HTTPException.__subclasses__():
45
            if exception_cls is RequestRedirect or exception_cls().code == 412:
46
                continue
47
48
            exception_instance = exception_cls()
49
50
            self.add_route_raises_exception(
51
                ExceptionHandlingSpec(
52
                    Exception, broad_exception_handler, exception_instance
53
                )
54
            )
55
56
            resp = self.request()
57
58
            self.assertEqual(exception_instance.code, resp.status_code)
59
            self.assertTrue(resp.is_json)
60
            self.assertDictEqual({"error": exception_instance.description}, resp.json)
61
62
    def test_pydantic_validation_error(self):
63
        self.add_route_raises_exception(
64
            ExceptionHandlingSpec(
65
                Exception,
66
                broad_exception_handler,
67
                ValidationError(
68
                    [
69
                        ErrorWrapper(NoneIsNotAllowedError(), "foo"),
70
                        ErrorWrapper(BoolError(), "bar"),
71
                    ],
72
                    BaseModel,
73
                ),
74
            )
75
        )
76
77
        resp = self.request()
78
79
        self.assertEqual(400, resp.status_code)
80
        self.assertDictEqual(
81
            {
82
                "error": [
83
                    {
84
                        "loc": ["foo"],
85
                        "msg": "none is not an allowed value",
86
                        "type": "type_error.none.not_allowed",
87
                    },
88
                    {
89
                        "loc": ["bar"],
90
                        "msg": "value could not be parsed to a boolean",
91
                        "type": "type_error.bool",
92
                    },
93
                ]
94
            },
95
            resp.json,
96
        )
97
98
    def test_500(self):
99
        self.add_route_raises_exception(
100
            ExceptionHandlingSpec(Exception, broad_exception_handler, KeyError())
101
        )
102
103
        resp = self.request()
104
105
        self.assertEqual(500, resp.status_code)
106
        self.assertDictEqual({"error": ""}, resp.json)
107