File: test_validators.py

package info (click to toggle)
python-falcon 4.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,172 kB
  • sloc: python: 33,608; javascript: 92; sh: 50; makefile: 50
file content (190 lines) | stat: -rw-r--r-- 5,399 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
import typing  # NOQA: F401

import pytest

import falcon
from falcon import testing
from falcon.media import validators

try:
    import jsonschema
except ImportError:
    jsonschema = None  # type: ignore

_VALID_MEDIA = {'message': 'something'}
_INVALID_MEDIA = {}  # type: typing.Dict[str, str]


_TEST_SCHEMA = {
    'type': 'object',
    'properies': {
        'message': {
            'type': 'string',
        },
    },
    'required': ['message'],
}


skip_missing_dep = pytest.mark.skipif(
    jsonschema is None, reason='jsonschema dependency not found'
)


class Resource:
    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA)
    def request_validated(self, req, resp):
        assert req.media is not None
        return resp

    @validators.jsonschema.validate(resp_schema=_TEST_SCHEMA)
    def response_validated(self, req, resp):
        assert resp.media is not None
        return resp

    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA, resp_schema=_TEST_SCHEMA)
    def both_validated(self, req, resp):
        assert req.media is not None
        assert resp.media is not None
        return req, resp

    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA, resp_schema=_TEST_SCHEMA)
    def on_put(self, req, resp):
        assert req.media is not None
        resp.media = _VALID_MEDIA


class ResourceAsync:
    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA)
    async def request_validated(self, req, resp):
        # NOTE(kgriffs): Verify that we can await req.get_media() multiple times
        for i in range(3):
            m = await req.get_media()
            assert m == _VALID_MEDIA

        assert m is not None
        return resp

    @validators.jsonschema.validate(resp_schema=_TEST_SCHEMA)
    async def response_validated(self, req, resp):
        assert resp.media is not None
        return resp

    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA, resp_schema=_TEST_SCHEMA)
    async def both_validated(self, req, resp):
        m = await req.get_media()
        assert m is not None

        assert resp.media is not None

        return req, resp

    @validators.jsonschema.validate(req_schema=_TEST_SCHEMA, resp_schema=_TEST_SCHEMA)
    async def on_put(self, req, resp):
        m = await req.get_media()
        assert m is not None
        resp.media = _VALID_MEDIA


class _MockReq:
    def __init__(self, valid=True):
        self.media = _VALID_MEDIA if valid else {}


class _MockReqAsync:
    def __init__(self, valid=True):
        self._media = _VALID_MEDIA if valid else {}

    async def get_media(self):
        return self._media


def MockReq(asgi, valid=True):
    return _MockReqAsync(valid) if asgi else _MockReq(valid)


class MockResp:
    def __init__(self, valid=True):
        self.media = _VALID_MEDIA if valid else {}


def call_method(asgi, method_name, *args):
    resource = ResourceAsync() if asgi else Resource()

    if asgi:
        return falcon.async_to_sync(getattr(resource, method_name), *args)

    return getattr(resource, method_name)(*args)


@skip_missing_dep
def test_req_schema_validation_success(asgi):
    data = MockResp()
    assert call_method(asgi, 'request_validated', MockReq(asgi), data) is data


@skip_missing_dep
@pytest.mark.parametrize(
    'exception_cls', [falcon.HTTPBadRequest, falcon.MediaValidationError]
)
def test_req_schema_validation_failure(asgi, exception_cls):
    with pytest.raises(exception_cls) as excinfo:
        call_method(asgi, 'request_validated', MockReq(asgi, False), None)

    assert excinfo.value.description == "'message' is a required property"


@skip_missing_dep
def test_resp_schema_validation_success(asgi):
    data = MockResp()
    assert call_method(asgi, 'response_validated', MockReq(asgi), data) is data


@skip_missing_dep
def test_resp_schema_validation_failure(asgi):
    with pytest.raises(falcon.HTTPInternalServerError) as excinfo:
        call_method(asgi, 'response_validated', MockReq(asgi), MockResp(False))

    assert excinfo.value.title == 'Response data failed validation'


@skip_missing_dep
def test_both_schemas_validation_success(asgi, util):
    req = MockReq(asgi)
    resp = MockResp()

    result = call_method(asgi, 'both_validated', req, resp)

    assert result[0] is req
    assert result[1] is resp

    client = testing.TestClient(util.create_app(asgi))
    resource = ResourceAsync() if asgi else Resource()
    client.app.add_route('/test', resource)

    result = client.simulate_put('/test', json=_VALID_MEDIA)
    assert result.json == resp.media


@skip_missing_dep
def test_both_schemas_validation_failure(asgi, util):
    bad_resp = MockResp(False)

    with pytest.raises(falcon.HTTPInternalServerError) as excinfo:
        call_method(asgi, 'both_validated', MockReq(asgi), bad_resp)

    assert excinfo.value.title == 'Response data failed validation'

    with pytest.raises(falcon.HTTPBadRequest) as excinfo:
        call_method(asgi, 'both_validated', MockReq(asgi, False), MockResp())

    assert excinfo.value.title == 'Request data failed validation'

    client = testing.TestClient(util.create_app(asgi))
    resource = ResourceAsync() if asgi else Resource()

    with util.disable_asgi_non_coroutine_wrapping():
        client.app.add_route('/test', resource)

    result = client.simulate_put('/test', json=_INVALID_MEDIA)
    assert result.status_code == 400