def test_chain_validators():
    request = {
        "num": "42",
        "num2": "-10",
        "num3": "3.14",
        "power": "16",
        "power2": "25",
        "new_param": "2.79",
    }
    # fmt: off
    factories = {
        "num": int,
        "num2": int,
        "num3": float,
        "power": int,
        "power2": lambda x: float(x) ** 0.5
    }
    # fmt: on

    qval = (
        QueryParamValidator(make_request(request), factories)
        .check("power", lambda x: (x ** 0.5).is_integer())
        .check("power2", float.is_integer)
        .eq("num", 42)
        .gt("num", 41)
        .lt("num2", 0)
        .nonzero("num3")
        .positive("power")
    )
    qval.add_predicate(
        "new_param", lambda x: float(x) not in (float("inf"), float("-inf"))
    )
    for r in builder.iterbuild(request):
        params = qval.apply_to_request(r)
        with params as p:
            assert p.num == 42
            assert p.num2 == -10
            assert p.num3 == 3.14
            assert p.power == 16
            assert p.power2 == 5
            assert p.new_param == "2.79"

    bad_examples = {
        "num": "0",  # is not equal to 42
        "num2": "1",  # is greater than zero
        "num3": "0",  # is equal to zero
        "power": "-64",  # is not positive
        "power2": "24",  # is not a perfect square
        "new_param": "inf",  # is an infinity
    }

    for r in builder.iterbuild(bad_examples):
        params = qval.apply_to_request(r)
        with pytest.raises(InvalidQueryParamException) as e, params:
            pass
        assert e.type is InvalidQueryParamException
예제 #2
0
def test_validator_factory():
    currency2f = lambda x: float(x[:-1])
    qparams = {
        "price": "43.5$",
        "n_items": "1",
        "meta": "info",
        "num": "10",
        "num2": "5",
        "token": "0123456789",
    }
    for r in builder.iterbuild(qparams):
        params = (
            validate(r, price=currency2f, n_items=int, num=int,
                     num2=int).positive(  # `n_items` must be greater than zero
                         "n_items")
            # `num` must be equal to 10
            .eq("num", 10)
            # `num2` must be less than 10
            .lt("num2", 10)
            # Len of `token` must be equal to 10
            .check("token", lambda x: len(x) == 10)
            # The same check as above, but using `transform`
            .eq("token", 10, transform=len))
        with params as p:
            assert {43.5, 1, "info", 10, 5,
                    "0123456789"} == set(p.__dct__.values())
예제 #3
0
def test_validation_fails():
    currency2f = lambda x: float(x[:-1])
    qparams = {
        "price": "43.5$",
        "n_items": "0",
        "meta": "info",
        "num": "-10",
        "num2": "20",
        "token": "012345678",
    }
    for r in builder.iterbuild(qparams):
        params = (
            validate(r, price=currency2f, n_items=int, num=int,
                     num2=int).positive(  # `n_items` must be greater than zero
                         "n_items")
            # `num` must be equal to 10
            .eq("num", 10)
            # `num2` must be less than 10
            .lt("num2", 10)
            # Len of `token` must be equal to 10
            .check("token", lambda x: len(x) == 10)
            # The same check as above, but using `transform`
            .eq("token", 10, transform=len))
        with pytest.raises(InvalidQueryParamException) as e, params:
            pass
        assert e.value.status_code == HTTP_400_BAD_REQUEST
예제 #4
0
def test_missing_param_throws_error():
    dct = {"param1": "whatever", "param2": "6.66"}
    for request in builder.iterbuild(dct):
        params = validate(request, param1=None, param2=float, param3=int)
        with pytest.raises(InvalidQueryParamException) as e, params:
            pass
        assert e.value.status_code == HTTP_400_BAD_REQUEST
예제 #5
0
def test_params_processed():
    dct = {"num": "42", "double": "2.79", "string": "some string"}
    for request in builder.iterbuild(dct):
        with validate(request, num=int, double=float) as p:
            assert p.num == 42
            assert p.double == 2.79
            assert p.string == "some string"
예제 #6
0
def test_params_omitted():
    dct = {"num": "42", "string": "some string"}
    for request in builder.iterbuild(dct):
        # Disable auto-detection of parameters (box_all)
        params = validate(request, num=int, box_all=False)
        with pytest.raises(APIException) as e, params as p:
            assert p.num == 42
            assert p.string == "some string"
        assert e.value.status_code == HTTP_500_INTERNAL_SERVER_ERROR
예제 #7
0
def test_unsupported_errors_handled():
    supported_exceptions = (TypeError, ValueError, KeyError)
    random_exceptions = (IOError, BrokenPipeError, ConnectionError,
                         BufferError)
    params = {"param": "value"}
    for r in builder.iterbuild(params):
        for exc in supported_exceptions + random_exceptions:
            with pytest.raises(APIException) as e, validate(r):
                raise exc
            assert e.value.status_code == HTTP_500_INTERNAL_SERVER_ERROR
예제 #8
0
def test_exception_handled_in_outside_context():
    """
    See `QueryParamValidator._validate()` and `test_supported_errors_handled()` for more info.
    """

    # Random exception.
    def f(_):
        raise IOError

    params = {"param": "value"}
    for r in builder.iterbuild(params):
        with pytest.raises(APIException) as e, validate(r, param=f):
            pass
        assert e.value.status_code == HTTP_500_INTERNAL_SERVER_ERROR
예제 #9
0
def test_params_validated():
    params = {
        "double": "3.14",
        "num": "10",
        "price": "0",
        "hashme": "s$cret_t0k3n",
        "observable": "important metric",
    }
    for request in builder.iterbuild(params):
        with pytest.raises(InvalidQueryParamException) as e:
            simple_view(request)
        assert stats[-1] == params["observable"]
        assert e.value.status_code == HTTP_400_BAD_REQUEST

        with pytest.raises(InvalidQueryParamException) as e:
            ViewClass().complex_view(request, 1, 2)
        assert stats[-1] == params["observable"]
        assert e.value.status_code == HTTP_400_BAD_REQUEST
예제 #10
0
def test_params_provided():
    params = {
        "double": "3.14",
        "num": "10",
        "price": "2.79",
        "hashme": "s$cret_t0k3n",
        "observable": "important metric",
    }
    for request in builder.iterbuild(params):
        # Test simple view
        r, box = simple_view(request)
        assert stats[-1] == params["observable"]
        assert set(r.query_params.keys()) == set(box.__dct__.keys())

        # Test complex view
        r, p1, p2, box = ViewClass().complex_view(request, 1, 2)
        assert (p1, p2) == (1, 2)
        assert stats[-1] == params["observable"]
        assert set(r.query_params.keys()) == set(box.__dct__.keys())
예제 #11
0
def test_supported_errors_handled():
    """
    Only TypeError, ValueError and KeyError occurred during the validation
    are handled as expected. Any error thrown inside of the context will raise APIError.
    See `test_unsupported_errors_handled()`.
    """
    def exc_factory(exc):
        def f(_):
            raise exc

        return f

    params = {"param": "value"}
    for r in builder.iterbuild(params):
        for exc in (TypeError, ValueError, KeyError):
            with pytest.raises(InvalidQueryParamException) as e, validate(
                    r, param=exc_factory(exc)):
                pass
            assert e.value.status_code == HTTP_400_BAD_REQUEST