Esempio n. 1
0
    async def test_unit__global_exception__ok__nominal_case(self, aiohttp_client):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        @hapic.with_api_doc()
        async def divide_by_zero(request):
            raise ZeroDivisionError()

        app = web.Application(debug=True)
        context = AiohttpContext(app)
        hapic.set_context(context)
        context.handle_exception(ZeroDivisionError, http_code=HTTPStatus.BAD_REQUEST)
        app.router.add_get("/", divide_by_zero)
        client = await aiohttp_client(app)
        response = await client.get("/")

        assert 400 == response.status
Esempio n. 2
0
    def test_func_schema_in_doc__ok__additionals_fields__forms__string(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            category = marshmallow.fields.String(
                required=True,
                description="a description",
                example="00010",
                format="binary",
                enum=["01000", "11111"],
                maxLength=5,
                minLength=5,
                # Theses none string specific parameters should disappear
                # in query/path
                maximum=400,
                # exclusiveMaximun=False,
                # minimum=0,
                # exclusiveMinimum=True,
                # multipleOf=1,
            )

        @hapic.with_api_doc()
        @hapic.input_forms(MySchema())
        def my_controller():
            return

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()
        assert "multipart/form-data" in doc["paths"]["/paper"]["post"][
            "consumes"]
        assert doc.get("paths").get("/paper").get("post").get("parameters")[0]
        field = doc.get("paths").get("/paper").get("post").get("parameters")[0]
        assert field[
            "description"] == "a description\n\n*example value: 00010*"
        # INFO - G.M - 01-06-2018 - Field example not allowed here,
        # added in description instead
        assert "example" not in field
        assert field["format"] == "binary"
        assert field["in"] == "formData"
        assert field["type"] == "string"
        assert field["maxLength"] == 5
        assert field["minLength"] == 5
        assert field["required"] is True
        assert field["enum"] == ["01000", "11111"]
        assert "maximum" not in field
Esempio n. 3
0
def get_bottle_context():
    h = Hapic(processor_class=MarshmallowProcessor)

    bottle_app = Bottle()
    h.reset_context()
    h.set_context(BottleContext(bottle_app, default_error_builder=MarshmallowDefaultErrorBuilder()))

    class MySchema(marshmallow.Schema):
        name = marshmallow.fields.String(required=True)

    @h.with_api_doc()
    @h.input_body(MySchema())
    def my_controller():
        return {"name": "test"}

    bottle_app.route("/test", method="POST", callback=my_controller)
    return {"hapic": h, "app": bottle_app}
Esempio n. 4
0
def get_flask_context():
    h = Hapic(processor_class=MarshmallowProcessor)

    flask_app = Flask(__name__)
    h.reset_context()
    h.set_context(FlaskContext(flask_app, default_error_builder=MarshmallowDefaultErrorBuilder()))

    class MySchema(marshmallow.Schema):
        name = marshmallow.fields.String(required=True)

    @h.with_api_doc()
    @h.input_body(MySchema())
    def my_controller():
        return {"name": "test"}

    flask_app.add_url_rule("/test", view_func=my_controller, methods=["POST"])
    return {"hapic": h, "app": flask_app}
Esempio n. 5
0
    async def test_aiohttp_output_stream__error__interrupt(
            self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        class AsyncGenerator:
            def __init__(self):
                self._iterator = iter([
                    {
                        "name": "Hello, bob"
                    },
                    {
                        "nameZ": "Hello, Z"
                    },  # This line is incorrect
                    {
                        "name": "Hello, franck"
                    },  # This line must not be reached
                ])

            async def __aiter__(self):
                return self

            async def __anext__(self):
                return next(self._iterator)

        class OuputStreamItemSchema(marshmallow.Schema):
            name = marshmallow.fields.String(required=True)

        @hapic.output_stream(OuputStreamItemSchema(), ignore_on_error=False)
        async def hello(request):
            return AsyncGenerator()

        app = web.Application(debug=True)
        app.router.add_get("/", hello)
        hapic.set_context(
            AiohttpContext(
                app, default_error_builder=MarshmallowDefaultErrorBuilder()))
        client = await aiohttp_client(app)

        resp = await client.get("/")
        assert resp.status == 200

        line = await resp.content.readline()
        assert b'{"name": "Hello, bob"}\n' == line

        line = await resp.content.readline()
        assert b"" == line
Esempio n. 6
0
    def test_func__output_file_doc__ok__nominal_case(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        @hapic.with_api_doc()
        @hapic.output_file(["image/jpeg"])
        def my_controller():
            return b"101010100101"

        app.route("/avatar", method="GET", callback=my_controller)
        doc = hapic.generate_doc()

        assert doc
        assert "/avatar" in doc["paths"]
        assert "produces" in doc["paths"]["/avatar"]["get"]
        assert "image/jpeg" in doc["paths"]["/avatar"]["get"]["produces"]
        assert "200" in doc["paths"]["/avatar"]["get"]["responses"]
Esempio n. 7
0
    def test_func__output_file_doc__ok__nominal_case(self):
        hapic = Hapic()
        hapic.set_context(MyContext())
        app = bottle.Bottle()

        @hapic.with_api_doc()
        @hapic.output_file('image/jpeg')
        def my_controller():
            return b'101010100101'

        app.route('/avatar', method='GET', callback=my_controller)
        doc = hapic.generate_doc(app)

        assert doc
        assert '/avatar' in doc['paths']
        assert 'produce' in doc['paths']['/avatar']['get']
        assert 'image/jpeg' in doc['paths']['/avatar']['get']['produce']
        assert 200 in doc['paths']['/avatar']['get']['responses']
Esempio n. 8
0
    def test_func__tags__ok__nominal_case(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        @hapic.with_api_doc(tags=["foo", "bar"])
        def my_controller(hapic_data=None):
            assert hapic_data
            assert hapic_data.files

        app.route("/upload", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        assert doc.get("paths")
        assert "/upload" in doc["paths"]
        assert "post" in doc["paths"]["/upload"]
        assert "tags" in doc["paths"]["/upload"]["post"]
        assert ["foo", "bar"] == doc["paths"]["/upload"]["post"]["tags"]
Esempio n. 9
0
    async def test_unit__general_exception_handling__ok__exception_list(
            self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        @hapic.with_api_doc()
        async def zero(request):
            return 1 / 0

        @hapic.with_api_doc()
        async def key(request):
            return dict()["foo"]

        app = web.Application(debug=True)
        app.router.add_get("/a", zero)
        app.router.add_get("/b", key)
        context = AiohttpContext(
            app, default_error_builder=MarshmallowDefaultErrorBuilder())
        context.handle_exceptions([ZeroDivisionError, KeyError], 400)
        hapic.set_context(context)

        client = await aiohttp_client(app)

        resp = await client.get("/a")

        json = await resp.json()
        assert resp.status == 400
        assert {
            "details": {
                "error_detail": {}
            },
            "message": "division by zero",
            "code": None,
        } == json
        resp = await client.get("/a")

        json = await resp.json()
        assert resp.status == 400
        assert {
            "details": {
                "error_detail": {}
            },
            "message": "division by zero",
            "code": None,
        } == json
Esempio n. 10
0
    async def test_aiohttp_handle_excpetion__ok__nominal_case(self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        @hapic.handle_exception(ZeroDivisionError, http_code=400)
        async def hello(request):
            1 / 0

        app = web.Application(debug=True)
        app.router.add_get("/", hello)
        hapic.set_context(
            AiohttpContext(app, default_error_builder=MarshmallowDefaultErrorBuilder())
        )
        client = await aiohttp_client(app)

        resp = await client.get("/")
        assert resp.status == 400

        data = await resp.json()
        assert "division by zero" == data.get("message")
Esempio n. 11
0
    def test_unit__generate_doc_with_wildcard__ok__fixed_methods(self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        @hapic.with_api_doc()
        async def a_proxy(request):
            pass

        app = web.Application(debug=True)
        app.router.add_route(hdrs.METH_ANY, path="/", handler=a_proxy)
        hapic.set_context(
            AiohttpContext(app, default_error_builder=MarshmallowDefaultErrorBuilder())
        )

        doc = hapic.generate_doc("aiohttp", "testing", wildcard_method_replacement=["head"])
        # INFO BS 2019-04-15: Prevent keep of OrderedDict
        doc = json.loads(json.dumps(doc))

        assert 1 == len(doc["paths"]["/"])
        assert "head" in doc["paths"]["/"]
Esempio n. 12
0
    async def test_request_header__ok__lowercase_key(self, aiohttp_client):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        class HeadersSchema(marshmallow.Schema):
            foo = marshmallow.fields.String(required=True)

        @hapic.with_api_doc()
        @hapic.input_headers(HeadersSchema())
        async def hello(request, hapic_data: HapicData):
            return web.json_response(hapic_data.headers)

        app = web.Application(debug=True)
        hapic.set_context(AiohttpContext(app))
        app.router.add_get("/", hello)
        client = await aiohttp_client(app)
        response = await client.get("/", headers={"FOO": "bar"})
        assert 200 == response.status
        json_ = await response.json()
        assert {"foo": "bar"} == json_
Esempio n. 13
0
    def test_unit__input_files__ok__file_is_present(self):
        hapic = Hapic()
        hapic.set_context(
            MyContext(fake_files_parameters={
                'file_abc': '10101010101',
            }))

        class MySchema(marshmallow.Schema):
            file_abc = marshmallow.fields.Raw(required=True)

        @hapic.with_api_doc()
        @hapic.input_files(MySchema())
        def my_controller(hapic_data=None):
            assert hapic_data
            assert hapic_data.files
            return 'OK'

        result = my_controller()
        assert 'OK' == result
Esempio n. 14
0
    def test_func_schema_in_doc__ok__additionals_fields__body__number(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            category = marshmallow.fields.Integer(
                required=True,
                description="a number",
                example="12",
                format="int64",
                enum=[4, 6],
                # Theses none string specific parameters should disappear
                # in query/path
                maximum=14,
                exclusiveMaximun=False,
                minimum=0,
                exclusiveMinimum=True,
                multipleOf=2,
            )

        @hapic.with_api_doc()
        @hapic.input_body(MySchema())
        def my_controller():
            return

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        schema_field = (doc.get("definitions",
                                {}).get("MySchema",
                                        {}).get("properties",
                                                {}).get("category", {}))
        assert schema_field
        assert schema_field["description"] == "a number"
        assert schema_field["example"] == "12"
        assert schema_field["format"] == "int64"
        assert schema_field["type"] == "integer"
        assert schema_field["maximum"] == 14
        assert schema_field["minimum"] == 0
        assert schema_field["exclusiveMinimum"] is True
        assert schema_field["enum"] == [4, 6]
        assert schema_field["multipleOf"] == 2
Esempio n. 15
0
def get_aiohttp_context():
    h = Hapic(async_=True, processor_class=MarshmallowProcessor)
    aiohttp_app = web.Application(debug=True)
    h.reset_context()
    h.set_context(
        AiohttpContext(aiohttp_app, default_error_builder=MarshmallowDefaultErrorBuilder())
    )

    class MySchema(marshmallow.Schema):
        name = marshmallow.fields.String(required=True)

    @h.with_api_doc()
    @h.input_body(MySchema())
    def my_controller():
        return {"name": "test"}

    aiohttp_app.router.add_post("/", my_controller)

    return {"hapic": h, "app": aiohttp_app}
Esempio n. 16
0
    def test_func_schema_in_doc__ok__additionals_fields__path__number(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            category = marshmallow.fields.Integer(
                required=True,
                description="a number",
                example="12",
                format="int64",
                enum=[4, 6],
                # Theses none string specific parameters should disappear
                # in query/path
                maximum=14,
                exclusiveMaximun=False,
                minimum=0,
                exclusiveMinimum=True,
                multipleOf=2,
            )

        @hapic.with_api_doc()
        @hapic.input_path(MySchema())
        def my_controller():
            return

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()
        assert doc.get("paths").get("/paper").get("post").get("parameters")[0]
        field = doc.get("paths").get("/paper").get("post").get("parameters")[0]
        assert field["description"] == "a number\n\n*example value: 12*"
        # INFO - G.M - 01-06-2018 - Field example not allowed here,
        # added in description instead
        assert "example" not in field
        assert field["format"] == "int64"
        assert field["in"] == "path"
        assert field["type"] == "integer"
        assert field["maximum"] == 14
        assert field["minimum"] == 0
        assert field["exclusiveMinimum"] is True
        assert field["required"] is True
        assert field["enum"] == [4, 6]
        assert field["multipleOf"] == 2
Esempio n. 17
0
    def test_func__docstring__ok__simple_case(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        @hapic.with_api_doc()
        def my_controller(hapic_data=None):
            """
            Hello doc
            """
            assert hapic_data
            assert hapic_data.files

        app.route("/upload", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        assert doc.get("paths")
        assert "/upload" in doc["paths"]
        assert "post" in doc["paths"]["/upload"]
        assert "description" in doc["paths"]["/upload"]["post"]
        assert "Hello doc" == doc["paths"]["/upload"]["post"]["description"]
Esempio n. 18
0
    def test_func__schema_in_doc__ok__many_and_exclude_case(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            name = marshmallow.fields.String(required=True)
            name2 = marshmallow.fields.String(required=True)

        @hapic.with_api_doc()
        @hapic.input_body(MySchema(exclude=("name2", ), many=True))
        def my_controller():
            return {"name": "test"}

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        definitions = doc.get("definitions", {})
        # TODO - G-M - Find better way to find our new schema
        # Do Better test when we were able to set correctly schema name
        # according to content
        schema_name = None
        for elem in definitions.keys():
            if elem != "MySchema":
                schema_name = elem
                break
        assert schema_name
        schema_def = definitions[schema_name]
        assert schema_def.get("properties", {}).get("name",
                                                    {}).get("type") == "string"
        assert doc.get("paths").get("/paper").get("post").get("parameters")[0]
        schema_ref = doc.get("paths").get("/paper").get("post").get(
            "parameters")[0]
        assert schema_ref.get("in") == "body"
        assert schema_ref["schema"] == {
            "items": {
                "$ref": "#/definitions/{}".format(schema_name)
            },
            "type": "array",
        }
Esempio n. 19
0
def get_pyramid_context():
    h = Hapic(processor_class=MarshmallowProcessor)

    configurator = Configurator(autocommit=True)

    h.reset_context()
    h.set_context(
        PyramidContext(configurator, default_error_builder=MarshmallowDefaultErrorBuilder())
    )

    class MySchema(marshmallow.Schema):
        name = marshmallow.fields.String(required=True)

    @h.with_api_doc()
    @h.input_body(MySchema())
    def my_controller():
        return {"name": "test"}

    configurator.add_route("test", "/test", request_method="POST")
    configurator.add_view(my_controller, route_name="test")
    pyramid_app = configurator.make_wsgi_app()
    return {"hapic": h, "app": pyramid_app}
    def test_unit__input_files__ok__file_is_empty_string(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        hapic.set_context(AgnosticContext(app=None, files_parameters={"file_abc": ""}))

        class MySchema(marshmallow.Schema):
            file_abc = marshmallow.fields.Raw(required=True)

        @hapic.input_files(MySchema())
        def my_controller(hapic_data=None):
            assert hapic_data
            assert hapic_data.files
            return "OK"

        result = my_controller()
        assert HTTPStatus.BAD_REQUEST == result.status_code
        assert {
            "http_code": 400,
            "original_error": {
                "details": {"file_abc": ["Missing data for required field"]},
                "message": "Validation error of input data",
            },
        } == json.loads(result.body)
Esempio n. 21
0
    async def test_aiohttp_output_body__ok__nominal_case(self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        class OuputBodySchema(marshmallow.Schema):
            name = marshmallow.fields.String()

        @hapic.output_body(OuputBodySchema())
        async def hello(request):
            return {"name": "bob"}

        app = web.Application(debug=True)
        app.router.add_get("/", hello)
        hapic.set_context(
            AiohttpContext(app, default_error_builder=MarshmallowDefaultErrorBuilder())
        )
        client = await aiohttp_client(app)

        resp = await client.get("/")
        assert resp.status == 200

        data = await resp.json()
        assert "bob" == data.get("name")
Esempio n. 22
0
    def test_unit__input_files__ok__file_is_empty_string(self):
        hapic = Hapic()
        hapic.set_context(MyContext(fake_files_parameters={
            'file_abc': '',
        }))

        class MySchema(marshmallow.Schema):
            file_abc = marshmallow.fields.Raw(required=True)

        @hapic.with_api_doc()
        @hapic.input_files(MySchema())
        def my_controller(hapic_data=None):
            assert hapic_data
            assert hapic_data.files
            return 'OK'

        result = my_controller()
        assert 'http_code' in result
        assert HTTPStatus.BAD_REQUEST == result['http_code']
        assert {
            'file_abc': ['Missing data for required field']
        } == result['original_error'].details
Esempio n. 23
0
    async def test_aiohttp_input_body__ok_nominal_case(self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        class InputBodySchema(marshmallow.Schema):
            name = marshmallow.fields.String()

        @hapic.input_body(InputBodySchema())
        async def hello(request, hapic_data: HapicData):
            name = hapic_data.body.get("name")
            return web.Response(text="Hello, {}".format(name))

        app = web.Application(debug=True)
        app.router.add_post("/", hello)
        hapic.set_context(
            AiohttpContext(app, default_error_builder=MarshmallowDefaultErrorBuilder())
        )
        client = await aiohttp_client(app)

        resp = await client.post("/", data={"name": "bob"})
        assert resp.status == 200

        text = await resp.text()
        assert "Hello, bob" in text
Esempio n. 24
0
    async def test_unit__post_file__ok__put_success(self, aiohttp_client, loop):
        hapic = Hapic(async_=True, processor_class=MarshmallowProcessor)

        class InputFilesSchema(marshmallow.Schema):
            avatar = marshmallow.fields.Raw()

        @hapic.with_api_doc()
        @hapic.input_files(InputFilesSchema())
        async def update_avatar(request: Request, hapic_data: HapicData):
            avatar = hapic_data.files["avatar"]
            assert isinstance(avatar, FileField)
            assert b"text content of file" == avatar.file.read()
            return Response(body="ok")

        app = web.Application(debug=True)
        app.router.add_put("/avatar", update_avatar)
        hapic.set_context(
            AiohttpContext(app, default_error_builder=MarshmallowDefaultErrorBuilder())
        )
        client = await aiohttp_client(app)

        resp = await client.put("/avatar", data={"avatar": io.StringIO("text content of file")})
        assert resp.status == 200
Esempio n. 25
0
    def test_func__enum__nominal_case(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            category = marshmallow.fields.String(
                validate=OneOf(["foo", "bar"]))

        @hapic.with_api_doc()
        @hapic.input_body(MySchema())
        def my_controller():
            return

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        assert ["foo",
                "bar"] == doc.get("definitions",
                                  {}).get("MySchema",
                                          {}).get("properties",
                                                  {}).get("category",
                                                          {}).get("enum")
    def test_unit__handle_exception_with_default_error_builder__ok__marshmallow(
            self):
        app = AgnosticApp()
        hapic = Hapic()
        hapic.set_processor_class(MarshmallowProcessor)
        hapic.set_context(
            AgnosticContext(
                app, default_error_builder=MarshmallowDefaultErrorBuilder()))

        @hapic.with_api_doc()
        @hapic.handle_exception(ZeroDivisionError, http_code=400)
        def my_view():
            1 / 0

        response = my_view()
        json_ = json.loads(response.body)
        assert {
            "code": None,
            "details": {
                "error_detail": {}
            },
            "message": "division by zero",
        } == json_
    async def test_func__catch_one_exception__ok__aiohttp_case(
            self, test_client):
        from aiohttp import web

        app = web.Application()
        hapic = Hapic(processor_class=MarshmallowProcessor)
        context = AiohttpContext(app=app)
        hapic.set_context(context)

        async def my_view(request):
            raise ZeroDivisionError("An exception message")

        app.add_routes([web.get("/my-view", my_view)])
        # FIXME - G.M - 17-05-2018 - Verify if:
        # - other view that work/raise an other exception do not go
        # into this code for handle this exceptions.
        # - response come clearly from there, not from web framework:
        #  Check not only http code, but also body.
        # see  issue #158 (https://github.com/algoo/hapic/issues/158)
        context.handle_exception(ZeroDivisionError, http_code=400)
        test_app = await test_client(app)
        response = await test_app.get("/my-view")

        assert 400 == response.status
Esempio n. 28
0
    def test_func__input_files_doc__ok__two_file(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            file_abc = marshmallow.fields.Raw(required=True)
            file_def = marshmallow.fields.Raw(required=False)

        @hapic.with_api_doc()
        @hapic.input_files(MySchema())
        def my_controller(hapic_data=None):
            assert hapic_data
            assert hapic_data.files

        app.route("/upload", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        assert doc
        assert "/upload" in doc["paths"]
        assert "consumes" in doc["paths"]["/upload"]["post"]
        assert "multipart/form-data" in doc["paths"]["/upload"]["post"][
            "consumes"]
        assert "parameters" in doc["paths"]["/upload"]["post"]
        assert {
            "name": "file_abc",
            "required": True,
            "in": "formData",
            "type": "file"
        } in doc["paths"]["/upload"]["post"]["parameters"]
        assert {
            "name": "file_def",
            "required": False,
            "in": "formData",
            "type": "file"
        } in doc["paths"]["/upload"]["post"]["parameters"]
Esempio n. 29
0
    def test_func_schema_in_doc__ok__additionals_fields__body__string(self):
        hapic = Hapic(processor_class=MarshmallowProcessor)
        app = AgnosticApp()
        hapic.set_context(AgnosticContext(app=app))

        class MySchema(marshmallow.Schema):
            category = marshmallow.fields.String(
                required=True,
                description="a description",
                example="00010",
                format="binary",
                enum=["01000", "11111"],
                maxLength=5,
                minLength=5,
            )

        @hapic.with_api_doc()
        @hapic.input_body(MySchema())
        def my_controller():
            return

        app.route("/paper", method="POST", callback=my_controller)
        doc = hapic.generate_doc()

        schema_field = (doc.get("definitions",
                                {}).get("MySchema",
                                        {}).get("properties",
                                                {}).get("category", {}))
        assert schema_field
        assert schema_field["description"] == "a description"
        assert schema_field["example"] == "00010"
        assert schema_field["format"] == "binary"
        assert schema_field["type"] == "string"
        assert schema_field["maxLength"] == 5
        assert schema_field["minLength"] == 5
        assert schema_field["enum"] == ["01000", "11111"]
    def test_func__catch_one_exception__ok__bottle_case(self):
        import bottle

        app = bottle.Bottle()
        hapic = Hapic(processor_class=MarshmallowProcessor)
        context = BottleContext(app=app)
        hapic.set_context(context)

        def my_view():
            raise ZeroDivisionError("An exception message")

        app.route("/my-view", method="GET", callback=my_view)
        # FIXME - G.M - 17-05-2018 - Verify if:
        # - other view that work/raise an other exception do not go
        # into this code for handle this exceptions.
        # - response come clearly from there, not from web framework:
        #  Check not only http code, but also body.
        # see  issue #158 (https://github.com/algoo/hapic/issues/158)
        context.handle_exception(ZeroDivisionError, http_code=400)

        test_app = TestApp(app)
        response = test_app.get("/my-view", status="*")

        assert 400 == response.status_code