Codebase list python-webargs / bcd95e2 tests / test_flaskparser.py
bcd95e2

Tree @bcd95e2 (Download .tar.gz)

test_flaskparser.py @bcd95e2raw · history · blame

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import threading

from werkzeug.exceptions import HTTPException
import mock
import pytest

from flask import Flask
from webargs import fields, ValidationError, missing
from webargs.flaskparser import parser, abort
from webargs.core import MARSHMALLOW_VERSION_INFO, json

from .apps.flask_app import app
from webargs.testing import CommonTestCase


class TestFlaskParser(CommonTestCase):
    def create_app(self):
        return app

    def test_parsing_view_args(self, testapp):
        res = testapp.get("/echo_view_arg/42")
        assert res.json == {"view_arg": 42}

    def test_parsing_invalid_view_arg(self, testapp):
        res = testapp.get("/echo_view_arg/foo", expect_errors=True)
        assert res.status_code == 422
        assert res.json == {"view_arg": ["Not a valid integer."]}

    def test_use_args_with_view_args_parsing(self, testapp):
        res = testapp.get("/echo_view_arg_use_args/42")
        assert res.json == {"view_arg": 42}

    def test_use_args_on_a_method_view(self, testapp):
        res = testapp.post("/echo_method_view_use_args", {"val": 42})
        assert res.json == {"val": 42}

    def test_use_kwargs_on_a_method_view(self, testapp):
        res = testapp.post("/echo_method_view_use_kwargs", {"val": 42})
        assert res.json == {"val": 42}

    def test_use_kwargs_with_missing_data(self, testapp):
        res = testapp.post("/echo_use_kwargs_missing", {"username": "foo"})
        assert res.json == {"username": "foo"}

    # regression test for https://github.com/marshmallow-code/webargs/issues/145
    def test_nested_many_with_data_key(self, testapp):
        res = testapp.post_json("/echo_nested_many_data_key", {"x_field": [{"id": 42}]})
        # https://github.com/marshmallow-code/marshmallow/pull/714
        if MARSHMALLOW_VERSION_INFO[0] < 3:
            assert res.json == {"x_field": [{"id": 42}]}

        res = testapp.post_json("/echo_nested_many_data_key", {"X-Field": [{"id": 24}]})
        assert res.json == {"x_field": [{"id": 24}]}

        res = testapp.post_json("/echo_nested_many_data_key", {})
        assert res.json == {}


@mock.patch("webargs.flaskparser.abort")
def test_abort_called_on_validation_error(mock_abort):
    app = Flask("testapp")

    def validate(x):
        return x == 42

    argmap = {"value": fields.Field(validate=validate)}
    with app.test_request_context(
        "/foo",
        method="post",
        data=json.dumps({"value": 41}),
        content_type="application/json",
    ):
        parser.parse(argmap)
    mock_abort.assert_called()
    abort_args, abort_kwargs = mock_abort.call_args
    assert abort_args[0] == 422
    expected_msg = "Invalid value."
    assert abort_kwargs["messages"]["value"] == [expected_msg]
    assert type(abort_kwargs["exc"]) == ValidationError


def test_parse_form_returns_missing_if_no_form():
    req = mock.Mock()
    req.form.get.side_effect = AttributeError("no form")
    assert parser.parse_form(req, "foo", fields.Field()) is missing


def test_abort_with_message():
    with pytest.raises(HTTPException) as excinfo:
        abort(400, message="custom error message")
    assert excinfo.value.data["message"] == "custom error message"


def test_abort_has_serializable_data():
    with pytest.raises(HTTPException) as excinfo:
        abort(400, message="custom error message")
    serialized_error = json.dumps(excinfo.value.data)
    error = json.loads(serialized_error)
    assert isinstance(error, dict)
    assert error["message"] == "custom error message"

    with pytest.raises(HTTPException) as excinfo:
        abort(
            400,
            message="custom error message",
            exc=ValidationError("custom error message"),
        )
    serialized_error = json.dumps(excinfo.value.data)
    error = json.loads(serialized_error)
    assert isinstance(error, dict)
    assert error["message"] == "custom error message"


def test_json_cache_race_condition():
    app = Flask("testapp")
    lock = threading.Lock()
    lock.acquire()

    class MyField(fields.Field):
        def _deserialize(self, value, attr, data, **kwargs):
            with lock:
                return value

    argmap = {"value": MyField()}
    results = {}

    def thread_fn(value):
        with app.test_request_context(
            "/foo",
            method="post",
            data=json.dumps({"value": value}),
            content_type="application/json",
        ):
            results[value] = parser.parse(argmap)["value"]

    t1 = threading.Thread(target=thread_fn, args=(42,))
    t2 = threading.Thread(target=thread_fn, args=(23,))
    t1.start()
    t2.start()
    lock.release()
    t1.join()
    t2.join()
    # ensure we didn't get contaminated by a parallel request
    assert results[42] == 42
    assert results[23] == 23