Module pedantic.tests.validate.test_flask_parameters

Expand source code
import json
from dataclasses import dataclass
from typing import List, Dict, Any
from unittest import TestCase

from flask import Flask, Response, jsonify

from pedantic import Email, overrides
from pedantic.decorators.fn_deco_validate.exceptions import ValidateException, TooManyArguments, \
    ParameterException, ExceptionDictKey, InvalidHeader
from pedantic.decorators.fn_deco_validate.fn_deco_validate import validate, ReturnAs
from pedantic.decorators.fn_deco_validate.parameters.flask_parameters import FlaskJsonParameter, FlaskFormParameter, \
    FlaskHeaderParameter, FlaskGetParameter, FlaskPathParameter, Deserializable, GenericFlaskDeserializer
from pedantic.decorators.fn_deco_validate.validators import NotEmpty, Min

JSON = 'application/json'
OK = 200
INVALID = 422
TOO_MANY_ARGS = 400


class TestFlaskParameters(TestCase):
    def test_validator_flask_json(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(
            FlaskJsonParameter(name='key', validators=[NotEmpty()]),
        )
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.route('/required')
        @validate(
            FlaskJsonParameter(name='required', required=True),
            FlaskJsonParameter(name='not_required', required=False),
            FlaskJsonParameter(name='not_required_with_default', required=False, default=42),
        )
        def required_params(required, not_required, not_required_with_default) -> Response:
            return jsonify({
                'required': required,
                'not_required': not_required,
                'not_required_with_default': not_required_with_default,
            })

        @app.route('/types')
        @validate(
            FlaskJsonParameter(name='bool_param', value_type=bool),
            FlaskJsonParameter(name='int_param', value_type=int),
            FlaskJsonParameter(name='float_param', value_type=float),
            FlaskJsonParameter(name='str_param', value_type=str),
            FlaskJsonParameter(name='list_param', value_type=list),
            FlaskJsonParameter(name='dict_param', value_type=dict),
        )
        def different_types(
                bool_param,
                int_param,
                float_param,
                str_param,
                list_param,
                dict_param,
        ) -> Response:
            return jsonify({
                'bool_param': bool_param,
                'int_param': int_param,
                'float_param': float_param,
                'str_param': str_param,
                'list_param': list_param,
                'dict_param': dict_param,
            })

        @app.route('/args')
        @validate(
            FlaskJsonParameter(name='a', validators=[NotEmpty()]),
            FlaskJsonParameter(name='b', validators=[NotEmpty()]),
            return_as=ReturnAs.ARGS,
        )
        def names_do_not_need_to_match(my_key: str, another: str) -> Response:
            return jsonify({
                'my_key': my_key,
                'another': another,
            })

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        @app.errorhandler(TooManyArguments)
        def handle_validation_error(exception: TooManyArguments) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = TOO_MANY_ARGS
            return response

        with app.test_client() as client:
            res = client.get('/', data=json.dumps({'key': '  hello world  '}), content_type=JSON)
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', data=json.dumps({'key': '  '}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

            data = {
                'key': '  hello world  ',
                'required': '1',
            }
            res = client.get('/', data=json.dumps(data), content_type=JSON)
            self.assertEqual(TOO_MANY_ARGS, res.status_code)
            self.assertEqual("Got unexpected arguments: ['required']", res.json)

    def test_validator_flask_form_data(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', data={'key': '  hello world  '})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', data={'key': '  '})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_header(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(InvalidHeader)
        def handle_validation_error(exception: InvalidHeader) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', headers={'key': '  hello world  '}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', headers={'key': '  '}, data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_header_optional_parameter(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()], required=False))
        def hello_world(key: str = None) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/', headers={'key': '  hello world  '}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', headers={}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(None, res.json)

    def test_validator_flask_get(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskGetParameter(name='key', value_type=str, validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/?key=hello_world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello_world', res.json)

            res = client.get('/?key=hello world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/?key= ', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: ' ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_get_multiple_values_for_same_key(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskGetParameter(name='key', value_type=list, validators=[NotEmpty()]))
        def hello_world(key: List[str]) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/?key=hello&key=world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(['hello', 'world'], res.json)

    def test_validator_flask_path(self) -> None:
        app = Flask(__name__)

        @app.route('/<string:key>')
        @validate(FlaskPathParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/hello_world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello_world', res.json)

            res = client.get('/hello world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/   ', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '   ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_invalid_value_type(self) -> None:
        app = Flask(__name__)

        with self.assertRaises(expected_exception=AssertionError):
            @app.route('/')
            @validate(FlaskFormParameter(name='key', value_type=tuple))
            def hello_world(key: str) -> Response:
                return jsonify(key)

    def test_wrong_name(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='k', value_type=str))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ValidateException)
        def handle_validation_error(exception: ValidateException) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data={'key': 'k'})
            self.assertEqual(INVALID, res.status_code)
            self.assertIn('Value for parameter k is required.', res.json)

    def test_default_value(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_not_required_allows_none_kwargs_without_none(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False),
                  return_as=ReturnAs.KWARGS_WITHOUT_NONE)
        def hello_world(key: str = 'it works') -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('it works', res.json)

    def test_not_required_allows_none_kwargs_with_none(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False, default=None),
                  return_as=ReturnAs.KWARGS_WITH_NONE)
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={'key': None})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(None, res.json)

    def test_not_required_with_default(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False, default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_validator_flask_path_type_conversion(self) -> None:
        app = Flask(__name__)

        @app.route('/<string:key>')
        @validate(FlaskPathParameter(name='key', value_type=int, validators=[Min(42)]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/42', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(42, res.json)

            res = client.get('/42f', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: None,
                ExceptionDictKey.VALUE: '42f',
                ExceptionDictKey.MESSAGE: "Value 42f cannot be converted to <class 'int'>.",
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_json_parameter_does_not_get_json(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='key'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: None,
                ExceptionDictKey.VALUE: 'None',
                ExceptionDictKey.MESSAGE: 'Value for parameter key is required.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_json_parameter_does_not_get_json_but_default(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='key', default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_too_many_arguments(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='k', value_type=str))
        def hello_world(**kwargs) -> Response:
            return jsonify(str(kwargs))

        @app.errorhandler(TooManyArguments)
        def handle_validation_error(exception: TooManyArguments) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data=json.dumps({'k': 'k', 'a': 1}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            self.assertEqual("Got unexpected arguments: ['a']", res.json)

    def test_exception_for_required_parameter(self) -> None:
        app = Flask(__name__)
        key = 'PASSWORD'

        @app.route('/')
        @validate(FlaskJsonParameter(name=key, value_type=str))
        def hello_world(**kwargs) -> Response:
            return jsonify(str(kwargs))

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            reason = 'required' if 'required' in exception.message else 'invalid'
            response = jsonify({exception.parameter_name: [{'KEY': reason}]})
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data=json.dumps({}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            self.assertEqual({key: [{'KEY': 'required'}]}, res.json)

    def test_async_endpoints(self) -> None:
        app = Flask(__name__)

        @app.route('/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]))
        async def hello_world(k) -> Response:
            return jsonify(str(k))

        @app.route('/foo/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]), return_as=ReturnAs.ARGS)
        async def return_args(k) -> Response:
            return jsonify(str(k))

        @app.route('/bar/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]),
                  return_as=ReturnAs.KWARGS_WITH_NONE)
        async def return_kwargs_with_none(k) -> Response:
            return jsonify(str(k))

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(path=f'/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/foo/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/bar/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/41')
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.MESSAGE: 'smaller then allowed: 41 is not >= 42',
                ExceptionDictKey.PARAMETER: 'k',
                ExceptionDictKey.VALIDATOR: 'Min',
                ExceptionDictKey.VALUE: '41',
            }
            self.assertEqual(expected, res.json)

    def test_json_parameter_with_default_value(self) -> None:
        app = Flask(__name__)

        @app.route('/testing/message/<string:email>', methods=['POST'])
        @validate(
            FlaskPathParameter(name='email', value_type=str, validators=[Email()]),
            FlaskJsonParameter(name='content', value_type=str, default='this is a fake message', required=False),
        )
        def testing_send_system_message(email: str, content: str) -> Response:
            return jsonify({'email': email, 'content': content})

        with app.test_client() as client:
            res = client.post(path=f'/testing/message/adam@eva.de')
            self.assertEqual(OK, res.status_code)
            expected = {
                'email': 'adam@eva.de',
                'content': 'this is a fake message',
            }
            self.assertEqual(expected, res.json)

            res = client.post(path=f'/testing/message/adam@eva.de', json={'content': 'hello world'})
            self.assertEqual(OK, res.status_code)
            expected = {
                'email': 'adam@eva.de',
                'content': 'hello world',
            }
            self.assertEqual(expected, res.json)

    def test_generic_deserializer(self) -> None:
        @dataclass(frozen=True)
        class User(Deserializable):
            firstname: str
            lastname: str
            age: int

            @staticmethod
            @overrides(Deserializable)
            def from_json(data: Dict[str, Any]) -> 'User':
                return User(
                    firstname=data['firstname'],
                    lastname=data['lastname'],
                    age=Min(value=18).validate_param(value=int(data['age']), parameter_name='age'),
                )

        app = Flask(__name__)

        @app.route('/foo', methods=['POST'])
        @validate(
            GenericFlaskDeserializer(name='user', cls=User, catch_exception=True),
        )
        def the_generic_approach(user: User) -> Response:
            return jsonify({'name': user.firstname})

        @app.route('/bar', methods=['POST'])
        @validate(
            GenericFlaskDeserializer(name='user', cls=User, catch_exception=False),
        )
        def do_not_catch(user: User) -> Response:
            return jsonify({'name': user.firstname})

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            data = {
                'firstname': 'Albert',
                'lastname': 'Einstein',
                'age': '56',
            }
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 200
            assert res.json == {'name': 'Albert'}

            data.pop('age')
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 422

            res = client.post(path=f'/bar', json=data)
            assert res.status_code == 500

            data['age'] = '16'
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 422
            expected = {
                ExceptionDictKey.MESSAGE: 'smaller then allowed: 16 is not >= 18',
                ExceptionDictKey.PARAMETER: 'age',
                ExceptionDictKey.VALIDATOR: 'Min',
                ExceptionDictKey.VALUE: '16',
            }
            assert res.json == expected

Classes

class TestFlaskParameters (methodName='runTest')

A class whose instances are single test cases.

By default, the test code itself should be placed in a method named 'runTest'.

If the fixture may be used for many test cases, create as many test methods as are needed. When instantiating such a TestCase subclass, specify in the constructor arguments the name of the test method that the instance is to execute.

Test authors should subclass TestCase for their own tests. Construction and deconstruction of the test's environment ('fixture') can be implemented by overriding the 'setUp' and 'tearDown' methods respectively.

If it is necessary to override the init method, the base class init method must always be called. It is important that subclasses should not change the signature of their init method, since instances of the classes are instantiated automatically by parts of the framework in order to be run.

When subclassing TestCase, you can set these attributes: * failureException: determines which exception will be raised when the instance's assertion methods fail; test methods raising this exception will be deemed to have 'failed' rather than 'errored'. * longMessage: determines whether long messages (including repr of objects used in assert methods) will be printed on failure in addition to any explicit message passed. * maxDiff: sets the maximum length of a diff in failure messages by assert methods using difflib. It is looked up as an instance attribute so can be configured by individual tests if required.

Create an instance of the class that will use the named test method when executed. Raises a ValueError if the instance does not have a method with the specified name.

Expand source code
class TestFlaskParameters(TestCase):
    def test_validator_flask_json(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(
            FlaskJsonParameter(name='key', validators=[NotEmpty()]),
        )
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.route('/required')
        @validate(
            FlaskJsonParameter(name='required', required=True),
            FlaskJsonParameter(name='not_required', required=False),
            FlaskJsonParameter(name='not_required_with_default', required=False, default=42),
        )
        def required_params(required, not_required, not_required_with_default) -> Response:
            return jsonify({
                'required': required,
                'not_required': not_required,
                'not_required_with_default': not_required_with_default,
            })

        @app.route('/types')
        @validate(
            FlaskJsonParameter(name='bool_param', value_type=bool),
            FlaskJsonParameter(name='int_param', value_type=int),
            FlaskJsonParameter(name='float_param', value_type=float),
            FlaskJsonParameter(name='str_param', value_type=str),
            FlaskJsonParameter(name='list_param', value_type=list),
            FlaskJsonParameter(name='dict_param', value_type=dict),
        )
        def different_types(
                bool_param,
                int_param,
                float_param,
                str_param,
                list_param,
                dict_param,
        ) -> Response:
            return jsonify({
                'bool_param': bool_param,
                'int_param': int_param,
                'float_param': float_param,
                'str_param': str_param,
                'list_param': list_param,
                'dict_param': dict_param,
            })

        @app.route('/args')
        @validate(
            FlaskJsonParameter(name='a', validators=[NotEmpty()]),
            FlaskJsonParameter(name='b', validators=[NotEmpty()]),
            return_as=ReturnAs.ARGS,
        )
        def names_do_not_need_to_match(my_key: str, another: str) -> Response:
            return jsonify({
                'my_key': my_key,
                'another': another,
            })

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        @app.errorhandler(TooManyArguments)
        def handle_validation_error(exception: TooManyArguments) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = TOO_MANY_ARGS
            return response

        with app.test_client() as client:
            res = client.get('/', data=json.dumps({'key': '  hello world  '}), content_type=JSON)
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', data=json.dumps({'key': '  '}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

            data = {
                'key': '  hello world  ',
                'required': '1',
            }
            res = client.get('/', data=json.dumps(data), content_type=JSON)
            self.assertEqual(TOO_MANY_ARGS, res.status_code)
            self.assertEqual("Got unexpected arguments: ['required']", res.json)

    def test_validator_flask_form_data(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', data={'key': '  hello world  '})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', data={'key': '  '})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_header(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(InvalidHeader)
        def handle_validation_error(exception: InvalidHeader) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', headers={'key': '  hello world  '}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', headers={'key': '  '}, data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALUE: '  ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_header_optional_parameter(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()], required=False))
        def hello_world(key: str = None) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/', headers={'key': '  hello world  '}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/', headers={}, data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(None, res.json)

    def test_validator_flask_get(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskGetParameter(name='key', value_type=str, validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/?key=hello_world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello_world', res.json)

            res = client.get('/?key=hello world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/?key= ', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: ' ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_get_multiple_values_for_same_key(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskGetParameter(name='key', value_type=list, validators=[NotEmpty()]))
        def hello_world(key: List[str]) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/?key=hello&key=world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(['hello', 'world'], res.json)

    def test_validator_flask_path(self) -> None:
        app = Flask(__name__)

        @app.route('/<string:key>')
        @validate(FlaskPathParameter(name='key', validators=[NotEmpty()]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/hello_world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello_world', res.json)

            res = client.get('/hello world', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('hello world', res.json)

            res = client.get('/   ', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: 'NotEmpty',
                ExceptionDictKey.VALUE: '   ',
                ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_invalid_value_type(self) -> None:
        app = Flask(__name__)

        with self.assertRaises(expected_exception=AssertionError):
            @app.route('/')
            @validate(FlaskFormParameter(name='key', value_type=tuple))
            def hello_world(key: str) -> Response:
                return jsonify(key)

    def test_wrong_name(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='k', value_type=str))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ValidateException)
        def handle_validation_error(exception: ValidateException) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data={'key': 'k'})
            self.assertEqual(INVALID, res.status_code)
            self.assertIn('Value for parameter k is required.', res.json)

    def test_default_value(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_not_required_allows_none_kwargs_without_none(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False),
                  return_as=ReturnAs.KWARGS_WITHOUT_NONE)
        def hello_world(key: str = 'it works') -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('it works', res.json)

    def test_not_required_allows_none_kwargs_with_none(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False, default=None),
                  return_as=ReturnAs.KWARGS_WITH_NONE)
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={'key': None})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(None, res.json)

    def test_not_required_with_default(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=str, required=False, default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get(data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_validator_flask_path_type_conversion(self) -> None:
        app = Flask(__name__)

        @app.route('/<string:key>')
        @validate(FlaskPathParameter(name='key', value_type=int, validators=[Min(42)]))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/42', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual(42, res.json)

            res = client.get('/42f', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: None,
                ExceptionDictKey.VALUE: '42f',
                ExceptionDictKey.MESSAGE: "Value 42f cannot be converted to <class 'int'>.",
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_json_parameter_does_not_get_json(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='key'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            print(str(exception))
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get('/', data={})
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.VALIDATOR: None,
                ExceptionDictKey.VALUE: 'None',
                ExceptionDictKey.MESSAGE: 'Value for parameter key is required.',
                ExceptionDictKey.PARAMETER: 'key',
            }
            self.assertEqual(expected, res.json)

    def test_validator_flask_json_parameter_does_not_get_json_but_default(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='key', default='42'))
        def hello_world(key: str) -> Response:
            return jsonify(key)

        with app.test_client() as client:
            res = client.get('/', data={})
            self.assertEqual(OK, res.status_code)
            self.assertEqual('42', res.json)

    def test_too_many_arguments(self) -> None:
        app = Flask(__name__)

        @app.route('/')
        @validate(FlaskJsonParameter(name='k', value_type=str))
        def hello_world(**kwargs) -> Response:
            return jsonify(str(kwargs))

        @app.errorhandler(TooManyArguments)
        def handle_validation_error(exception: TooManyArguments) -> Response:
            print(str(exception))
            response = jsonify(str(exception))
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data=json.dumps({'k': 'k', 'a': 1}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            self.assertEqual("Got unexpected arguments: ['a']", res.json)

    def test_exception_for_required_parameter(self) -> None:
        app = Flask(__name__)
        key = 'PASSWORD'

        @app.route('/')
        @validate(FlaskJsonParameter(name=key, value_type=str))
        def hello_world(**kwargs) -> Response:
            return jsonify(str(kwargs))

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            reason = 'required' if 'required' in exception.message else 'invalid'
            response = jsonify({exception.parameter_name: [{'KEY': reason}]})
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(data=json.dumps({}), content_type=JSON)
            self.assertEqual(INVALID, res.status_code)
            self.assertEqual({key: [{'KEY': 'required'}]}, res.json)

    def test_async_endpoints(self) -> None:
        app = Flask(__name__)

        @app.route('/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]))
        async def hello_world(k) -> Response:
            return jsonify(str(k))

        @app.route('/foo/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]), return_as=ReturnAs.ARGS)
        async def return_args(k) -> Response:
            return jsonify(str(k))

        @app.route('/bar/<int:k>')
        @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]),
                  return_as=ReturnAs.KWARGS_WITH_NONE)
        async def return_kwargs_with_none(k) -> Response:
            return jsonify(str(k))

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            res = client.get(path=f'/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/foo/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/bar/45')
            self.assertEqual(OK, res.status_code)

            res = client.get(path=f'/41')
            self.assertEqual(INVALID, res.status_code)
            expected = {
                ExceptionDictKey.MESSAGE: 'smaller then allowed: 41 is not >= 42',
                ExceptionDictKey.PARAMETER: 'k',
                ExceptionDictKey.VALIDATOR: 'Min',
                ExceptionDictKey.VALUE: '41',
            }
            self.assertEqual(expected, res.json)

    def test_json_parameter_with_default_value(self) -> None:
        app = Flask(__name__)

        @app.route('/testing/message/<string:email>', methods=['POST'])
        @validate(
            FlaskPathParameter(name='email', value_type=str, validators=[Email()]),
            FlaskJsonParameter(name='content', value_type=str, default='this is a fake message', required=False),
        )
        def testing_send_system_message(email: str, content: str) -> Response:
            return jsonify({'email': email, 'content': content})

        with app.test_client() as client:
            res = client.post(path=f'/testing/message/adam@eva.de')
            self.assertEqual(OK, res.status_code)
            expected = {
                'email': 'adam@eva.de',
                'content': 'this is a fake message',
            }
            self.assertEqual(expected, res.json)

            res = client.post(path=f'/testing/message/adam@eva.de', json={'content': 'hello world'})
            self.assertEqual(OK, res.status_code)
            expected = {
                'email': 'adam@eva.de',
                'content': 'hello world',
            }
            self.assertEqual(expected, res.json)

    def test_generic_deserializer(self) -> None:
        @dataclass(frozen=True)
        class User(Deserializable):
            firstname: str
            lastname: str
            age: int

            @staticmethod
            @overrides(Deserializable)
            def from_json(data: Dict[str, Any]) -> 'User':
                return User(
                    firstname=data['firstname'],
                    lastname=data['lastname'],
                    age=Min(value=18).validate_param(value=int(data['age']), parameter_name='age'),
                )

        app = Flask(__name__)

        @app.route('/foo', methods=['POST'])
        @validate(
            GenericFlaskDeserializer(name='user', cls=User, catch_exception=True),
        )
        def the_generic_approach(user: User) -> Response:
            return jsonify({'name': user.firstname})

        @app.route('/bar', methods=['POST'])
        @validate(
            GenericFlaskDeserializer(name='user', cls=User, catch_exception=False),
        )
        def do_not_catch(user: User) -> Response:
            return jsonify({'name': user.firstname})

        @app.errorhandler(ParameterException)
        def handle_validation_error(exception: ParameterException) -> Response:
            response = jsonify(exception.to_dict)
            response.status_code = INVALID
            return response

        with app.test_client() as client:
            data = {
                'firstname': 'Albert',
                'lastname': 'Einstein',
                'age': '56',
            }
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 200
            assert res.json == {'name': 'Albert'}

            data.pop('age')
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 422

            res = client.post(path=f'/bar', json=data)
            assert res.status_code == 500

            data['age'] = '16'
            res = client.post(path=f'/foo', json=data)
            assert res.status_code == 422
            expected = {
                ExceptionDictKey.MESSAGE: 'smaller then allowed: 16 is not >= 18',
                ExceptionDictKey.PARAMETER: 'age',
                ExceptionDictKey.VALIDATOR: 'Min',
                ExceptionDictKey.VALUE: '16',
            }
            assert res.json == expected

Ancestors

  • unittest.case.TestCase

Methods

def test_async_endpoints(self) ‑> None
Expand source code
def test_async_endpoints(self) -> None:
    app = Flask(__name__)

    @app.route('/<int:k>')
    @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]))
    async def hello_world(k) -> Response:
        return jsonify(str(k))

    @app.route('/foo/<int:k>')
    @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]), return_as=ReturnAs.ARGS)
    async def return_args(k) -> Response:
        return jsonify(str(k))

    @app.route('/bar/<int:k>')
    @validate(FlaskPathParameter(name='k', value_type=int, validators=[Min(42)]),
              return_as=ReturnAs.KWARGS_WITH_NONE)
    async def return_kwargs_with_none(k) -> Response:
        return jsonify(str(k))

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get(path=f'/45')
        self.assertEqual(OK, res.status_code)

        res = client.get(path=f'/foo/45')
        self.assertEqual(OK, res.status_code)

        res = client.get(path=f'/bar/45')
        self.assertEqual(OK, res.status_code)

        res = client.get(path=f'/41')
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.MESSAGE: 'smaller then allowed: 41 is not >= 42',
            ExceptionDictKey.PARAMETER: 'k',
            ExceptionDictKey.VALIDATOR: 'Min',
            ExceptionDictKey.VALUE: '41',
        }
        self.assertEqual(expected, res.json)
def test_default_value(self) ‑> None
Expand source code
def test_default_value(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='key', value_type=str, default='42'))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get(data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('42', res.json)
def test_exception_for_required_parameter(self) ‑> None
Expand source code
def test_exception_for_required_parameter(self) -> None:
    app = Flask(__name__)
    key = 'PASSWORD'

    @app.route('/')
    @validate(FlaskJsonParameter(name=key, value_type=str))
    def hello_world(**kwargs) -> Response:
        return jsonify(str(kwargs))

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        reason = 'required' if 'required' in exception.message else 'invalid'
        response = jsonify({exception.parameter_name: [{'KEY': reason}]})
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get(data=json.dumps({}), content_type=JSON)
        self.assertEqual(INVALID, res.status_code)
        self.assertEqual({key: [{'KEY': 'required'}]}, res.json)
def test_generic_deserializer(self) ‑> None
Expand source code
def test_generic_deserializer(self) -> None:
    @dataclass(frozen=True)
    class User(Deserializable):
        firstname: str
        lastname: str
        age: int

        @staticmethod
        @overrides(Deserializable)
        def from_json(data: Dict[str, Any]) -> 'User':
            return User(
                firstname=data['firstname'],
                lastname=data['lastname'],
                age=Min(value=18).validate_param(value=int(data['age']), parameter_name='age'),
            )

    app = Flask(__name__)

    @app.route('/foo', methods=['POST'])
    @validate(
        GenericFlaskDeserializer(name='user', cls=User, catch_exception=True),
    )
    def the_generic_approach(user: User) -> Response:
        return jsonify({'name': user.firstname})

    @app.route('/bar', methods=['POST'])
    @validate(
        GenericFlaskDeserializer(name='user', cls=User, catch_exception=False),
    )
    def do_not_catch(user: User) -> Response:
        return jsonify({'name': user.firstname})

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        data = {
            'firstname': 'Albert',
            'lastname': 'Einstein',
            'age': '56',
        }
        res = client.post(path=f'/foo', json=data)
        assert res.status_code == 200
        assert res.json == {'name': 'Albert'}

        data.pop('age')
        res = client.post(path=f'/foo', json=data)
        assert res.status_code == 422

        res = client.post(path=f'/bar', json=data)
        assert res.status_code == 500

        data['age'] = '16'
        res = client.post(path=f'/foo', json=data)
        assert res.status_code == 422
        expected = {
            ExceptionDictKey.MESSAGE: 'smaller then allowed: 16 is not >= 18',
            ExceptionDictKey.PARAMETER: 'age',
            ExceptionDictKey.VALIDATOR: 'Min',
            ExceptionDictKey.VALUE: '16',
        }
        assert res.json == expected
def test_invalid_value_type(self) ‑> None
Expand source code
def test_invalid_value_type(self) -> None:
    app = Flask(__name__)

    with self.assertRaises(expected_exception=AssertionError):
        @app.route('/')
        @validate(FlaskFormParameter(name='key', value_type=tuple))
        def hello_world(key: str) -> Response:
            return jsonify(key)
def test_json_parameter_with_default_value(self) ‑> None
Expand source code
def test_json_parameter_with_default_value(self) -> None:
    app = Flask(__name__)

    @app.route('/testing/message/<string:email>', methods=['POST'])
    @validate(
        FlaskPathParameter(name='email', value_type=str, validators=[Email()]),
        FlaskJsonParameter(name='content', value_type=str, default='this is a fake message', required=False),
    )
    def testing_send_system_message(email: str, content: str) -> Response:
        return jsonify({'email': email, 'content': content})

    with app.test_client() as client:
        res = client.post(path=f'/testing/message/adam@eva.de')
        self.assertEqual(OK, res.status_code)
        expected = {
            'email': 'adam@eva.de',
            'content': 'this is a fake message',
        }
        self.assertEqual(expected, res.json)

        res = client.post(path=f'/testing/message/adam@eva.de', json={'content': 'hello world'})
        self.assertEqual(OK, res.status_code)
        expected = {
            'email': 'adam@eva.de',
            'content': 'hello world',
        }
        self.assertEqual(expected, res.json)
def test_not_required_allows_none_kwargs_with_none(self) ‑> None
Expand source code
def test_not_required_allows_none_kwargs_with_none(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='key', value_type=str, required=False, default=None),
              return_as=ReturnAs.KWARGS_WITH_NONE)
    def hello_world(key: str) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get(data={'key': None})
        self.assertEqual(OK, res.status_code)
        self.assertEqual(None, res.json)
def test_not_required_allows_none_kwargs_without_none(self) ‑> None
Expand source code
def test_not_required_allows_none_kwargs_without_none(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='key', value_type=str, required=False),
              return_as=ReturnAs.KWARGS_WITHOUT_NONE)
    def hello_world(key: str = 'it works') -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get(data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('it works', res.json)
def test_not_required_with_default(self) ‑> None
Expand source code
def test_not_required_with_default(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='key', value_type=str, required=False, default='42'))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get(data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('42', res.json)
def test_too_many_arguments(self) ‑> None
Expand source code
def test_too_many_arguments(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskJsonParameter(name='k', value_type=str))
    def hello_world(**kwargs) -> Response:
        return jsonify(str(kwargs))

    @app.errorhandler(TooManyArguments)
    def handle_validation_error(exception: TooManyArguments) -> Response:
        print(str(exception))
        response = jsonify(str(exception))
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get(data=json.dumps({'k': 'k', 'a': 1}), content_type=JSON)
        self.assertEqual(INVALID, res.status_code)
        self.assertEqual("Got unexpected arguments: ['a']", res.json)
def test_validator_flask_form_data(self) ‑> None
Expand source code
def test_validator_flask_form_data(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='key', validators=[NotEmpty()]))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/', data={'key': '  hello world  '})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/', data={'key': '  '})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: 'NotEmpty',
            ExceptionDictKey.VALUE: '  ',
            ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)
def test_validator_flask_get(self) ‑> None
Expand source code
def test_validator_flask_get(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskGetParameter(name='key', value_type=str, validators=[NotEmpty()]))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/?key=hello_world', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello_world', res.json)

        res = client.get('/?key=hello world', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/?key= ', data={})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: 'NotEmpty',
            ExceptionDictKey.VALUE: ' ',
            ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)
def test_validator_flask_get_multiple_values_for_same_key(self) ‑> None
Expand source code
def test_validator_flask_get_multiple_values_for_same_key(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskGetParameter(name='key', value_type=list, validators=[NotEmpty()]))
    def hello_world(key: List[str]) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get('/?key=hello&key=world', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual(['hello', 'world'], res.json)
def test_validator_flask_header(self) ‑> None
Expand source code
def test_validator_flask_header(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()]))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(InvalidHeader)
    def handle_validation_error(exception: InvalidHeader) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/', headers={'key': '  hello world  '}, data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/', headers={'key': '  '}, data={})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALUE: '  ',
            ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
            ExceptionDictKey.PARAMETER: 'key',
            ExceptionDictKey.VALIDATOR: 'NotEmpty',
        }
        self.assertEqual(expected, res.json)
def test_validator_flask_header_optional_parameter(self) ‑> None
Expand source code
def test_validator_flask_header_optional_parameter(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskHeaderParameter(name='key', validators=[NotEmpty()], required=False))
    def hello_world(key: str = None) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get('/', headers={'key': '  hello world  '}, data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/', headers={}, data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual(None, res.json)
def test_validator_flask_json(self) ‑> None
Expand source code
def test_validator_flask_json(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(
        FlaskJsonParameter(name='key', validators=[NotEmpty()]),
    )
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.route('/required')
    @validate(
        FlaskJsonParameter(name='required', required=True),
        FlaskJsonParameter(name='not_required', required=False),
        FlaskJsonParameter(name='not_required_with_default', required=False, default=42),
    )
    def required_params(required, not_required, not_required_with_default) -> Response:
        return jsonify({
            'required': required,
            'not_required': not_required,
            'not_required_with_default': not_required_with_default,
        })

    @app.route('/types')
    @validate(
        FlaskJsonParameter(name='bool_param', value_type=bool),
        FlaskJsonParameter(name='int_param', value_type=int),
        FlaskJsonParameter(name='float_param', value_type=float),
        FlaskJsonParameter(name='str_param', value_type=str),
        FlaskJsonParameter(name='list_param', value_type=list),
        FlaskJsonParameter(name='dict_param', value_type=dict),
    )
    def different_types(
            bool_param,
            int_param,
            float_param,
            str_param,
            list_param,
            dict_param,
    ) -> Response:
        return jsonify({
            'bool_param': bool_param,
            'int_param': int_param,
            'float_param': float_param,
            'str_param': str_param,
            'list_param': list_param,
            'dict_param': dict_param,
        })

    @app.route('/args')
    @validate(
        FlaskJsonParameter(name='a', validators=[NotEmpty()]),
        FlaskJsonParameter(name='b', validators=[NotEmpty()]),
        return_as=ReturnAs.ARGS,
    )
    def names_do_not_need_to_match(my_key: str, another: str) -> Response:
        return jsonify({
            'my_key': my_key,
            'another': another,
        })

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    @app.errorhandler(TooManyArguments)
    def handle_validation_error(exception: TooManyArguments) -> Response:
        print(str(exception))
        response = jsonify(str(exception))
        response.status_code = TOO_MANY_ARGS
        return response

    with app.test_client() as client:
        res = client.get('/', data=json.dumps({'key': '  hello world  '}), content_type=JSON)
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/', data=json.dumps({'key': '  '}), content_type=JSON)
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: 'NotEmpty',
            ExceptionDictKey.VALUE: '  ',
            ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)

        data = {
            'key': '  hello world  ',
            'required': '1',
        }
        res = client.get('/', data=json.dumps(data), content_type=JSON)
        self.assertEqual(TOO_MANY_ARGS, res.status_code)
        self.assertEqual("Got unexpected arguments: ['required']", res.json)
def test_validator_flask_json_parameter_does_not_get_json(self) ‑> None
Expand source code
def test_validator_flask_json_parameter_does_not_get_json(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskJsonParameter(name='key'))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/', data={})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: None,
            ExceptionDictKey.VALUE: 'None',
            ExceptionDictKey.MESSAGE: 'Value for parameter key is required.',
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)
def test_validator_flask_json_parameter_does_not_get_json_but_default(self) ‑> None
Expand source code
def test_validator_flask_json_parameter_does_not_get_json_but_default(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskJsonParameter(name='key', default='42'))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    with app.test_client() as client:
        res = client.get('/', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('42', res.json)
def test_validator_flask_path(self) ‑> None
Expand source code
def test_validator_flask_path(self) -> None:
    app = Flask(__name__)

    @app.route('/<string:key>')
    @validate(FlaskPathParameter(name='key', validators=[NotEmpty()]))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/hello_world', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello_world', res.json)

        res = client.get('/hello world', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual('hello world', res.json)

        res = client.get('/   ', data={})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: 'NotEmpty',
            ExceptionDictKey.VALUE: '   ',
            ExceptionDictKey.MESSAGE: 'Got empty String which is invalid.',
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)
def test_validator_flask_path_type_conversion(self) ‑> None
Expand source code
def test_validator_flask_path_type_conversion(self) -> None:
    app = Flask(__name__)

    @app.route('/<string:key>')
    @validate(FlaskPathParameter(name='key', value_type=int, validators=[Min(42)]))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ParameterException)
    def handle_validation_error(exception: ParameterException) -> Response:
        print(str(exception))
        response = jsonify(exception.to_dict)
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get('/42', data={})
        self.assertEqual(OK, res.status_code)
        self.assertEqual(42, res.json)

        res = client.get('/42f', data={})
        self.assertEqual(INVALID, res.status_code)
        expected = {
            ExceptionDictKey.VALIDATOR: None,
            ExceptionDictKey.VALUE: '42f',
            ExceptionDictKey.MESSAGE: "Value 42f cannot be converted to <class 'int'>.",
            ExceptionDictKey.PARAMETER: 'key',
        }
        self.assertEqual(expected, res.json)
def test_wrong_name(self) ‑> None
Expand source code
def test_wrong_name(self) -> None:
    app = Flask(__name__)

    @app.route('/')
    @validate(FlaskFormParameter(name='k', value_type=str))
    def hello_world(key: str) -> Response:
        return jsonify(key)

    @app.errorhandler(ValidateException)
    def handle_validation_error(exception: ValidateException) -> Response:
        print(str(exception))
        response = jsonify(str(exception))
        response.status_code = INVALID
        return response

    with app.test_client() as client:
        res = client.get(data={'key': 'k'})
        self.assertEqual(INVALID, res.status_code)
        self.assertIn('Value for parameter k is required.', res.json)