Ejemplo n.º 1
0
Archivo: test.py Proyecto: wujm2007/hug
def call(method, api_or_module, url, body='', headers=None, params=None, query_string='', scheme='http', **kwargs):
    """Simulates a round-trip call against the given API / URL"""
    api = API(api_or_module).http.server()
    response = StartResponseMock()
    headers = {} if headers is None else headers
    if not isinstance(body, str) and 'json' in headers.get('content-type', 'application/json'):
        body = output_format.json(body)
        headers.setdefault('content-type', 'application/json')

    params = params if params else {}
    params.update(kwargs)
    if params:
        query_string = '{}{}{}'.format(query_string, '&' if query_string else '', urlencode(params, True))
    result = api(create_environ(path=url, method=method, headers=headers, query_string=query_string,
                                body=body, scheme=scheme), response)
    if result:
        try:
            response.data = result[0].decode('utf8')
        except TypeError:
            response.data = []
            for chunk in result:
                response.data.append(chunk.decode('utf8'))
            response.data = "".join(response.data)
        except UnicodeDecodeError:
            response.data = result[0]
        response.content_type = response.headers_dict['content-type']
        if response.content_type == 'application/json':
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 2
0
def call(method, api_module, url, body='', headers=None, **params):
    '''Simulates a round-trip call against the given api_module / url'''
    api = server(api_module)
    response = StartResponseMock()
    if not isinstance(body, str):
        body = output_format.json(body)
        headers = {} if headers is None else headers
        headers['content-type'] = 'application/json'

    result = api(
        create_environ(path=url,
                       method=method,
                       headers=headers,
                       query_string=urlencode(params),
                       body=body), response)
    if result:
        try:
            response.data = result[0].decode('utf8')
        except TypeError:
            response.data = []
            for chunk in result:
                response.data.append(chunk.decode('utf8'))
            response.data = "".join(response.data)
        except UnicodeDecodeError:
            response.data = result[0]
        response.content_type = response.headers_dict['content-type']
        if response.content_type == 'application/json':
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 3
0
    def test_update_password_with_code(self, api, user1, mocker):
        code = ''

        def sendmail_trap(*args):
            nonlocal code
            code = args[1]  # pylint: disable=unused-variable

        # Given
        response = StartResponseMock()
        mocker.patch.object(api, 'sendmail', sendmail_trap)
        api.password_reset_code(user1.email)
        # When
        result = api.update_password_with_code(code, "newpassword", response)
        # Then
        user = TESTSESSION.query(User).filter_by(user_id=user1.user_id).one()
        assert check_password(user, "newpassword") is True
        # When
        result = api.update_password_with_code(
            '12345678-1234-5678-1234-567812345678', "newpassword", response)
        # Then
        assert response.status == HTTP_409
        assert result == 'Invalid password reset code'
        # When
        result = api.update_password_with_code('Invalid UUID string',
                                               "newpassword", response)
        # Then
        assert response.status == HTTP_409
        assert result == 'Invalid password reset code'
Ejemplo n.º 4
0
    def request(self, method, path, params=None, body=''):
        if not path:
            path = '/'

        if not params:
            params = []

        env = create_environ(path=path, method=method.upper(), query_string=urlencode(params), body=body)

        resp = StartResponseMock()
        result = self.app(env, resp)

        resp.content = ''
        if result:
            resp.content = result[0].decode('utf-8')

        return resp
Ejemplo n.º 5
0
def call(method, api_or_module, url, body='', headers=None, **params):
    """Simulates a round-trip call against the given API / URL"""
    api = API(api_or_module).http.server()
    response = StartResponseMock()
    if not isinstance(body, str):
        body = output_format.json(body)
        headers = {} if headers is None else headers
        headers.setdefault('content-type', 'application/json')

    result = api(create_environ(path=url, method=method, headers=headers, query_string=urlencode(params), body=body),
                 response)
    if result:
        try:
            response.data = result[0].decode('utf8')
        except TypeError:
            response.data = []
            for chunk in result:
                response.data.append(chunk.decode('utf8'))
            response.data = "".join(response.data)
        except UnicodeDecodeError:
            response.data = result[0]
        response.content_type = response.headers_dict['content-type']
        if response.content_type == 'application/json':
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 6
0
Archivo: test.py Proyecto: zxy-zxy/hug
def call(method, api_or_module, url, body='', headers=None, params=None, query_string='', scheme='http', **kwargs):
    """Simulates a round-trip call against the given API / URL"""
    api = API(api_or_module).http.server()
    response = StartResponseMock()
    headers = {} if headers is None else headers
    if not isinstance(body, str) and 'json' in headers.get('content-type', 'application/json'):
        body = output_format.json(body)
        headers.setdefault('content-type', 'application/json')

    params = params if params else {}
    params.update(kwargs)
    if params:
        query_string = '{}{}{}'.format(query_string, '&' if query_string else '', urlencode(params, True))
    result = api(create_environ(path=url, method=method, headers=headers, query_string=query_string,
                                body=body, scheme=scheme), response)
    if result:
        try:
            response.data = result[0].decode('utf8')
        except TypeError:
            data = BytesIO()
            for chunk in result:
                data.write(chunk)
            data = data.getvalue()
            try:
                response.data = data.decode('utf8')
            except UnicodeDecodeError:   # pragma: no cover
                response.data = data
        except (UnicodeDecodeError, AttributeError):
            response.data = result[0]
        response.content_type = response.headers_dict['content-type']
        if 'application/json' in response.content_type:
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 7
0
Archivo: test.py Proyecto: jean/hug
def call(method, api_module, url, body='', headers=None, **params):
    '''Simulates a round-trip call against the given api_module / url'''
    api = server(api_module)
    response = StartResponseMock()
    if not isinstance(body, str):
        body = output_format.json(body)
        headers = {} if headers is None else headers
        headers['content-type'] = 'application/json'

    result = api(create_environ(path=url, method=method, headers=headers, query_string=urlencode(params), body=body),
                 response)
    if result:
        response.data = result[0].decode('utf8')
        response.content_type = response.headers_dict['content-type']
        if response.content_type == 'application/json':
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 8
0
 def test_login_bad_password(self, api, user1):
     # Given
     response = StartResponseMock()
     # When
     login = api.login(user1.email, 'wrongpassword', response)
     # Then
     assert login == ('Invalid email and/or password for email: '
                      '[email protected] [None]')
     assert response.status == HTTP_401
Ejemplo n.º 9
0
 def test_login_bad_user(self, api):
     # Given
     response = StartResponseMock()
     # When
     login = api.login('*****@*****.**', 'whocares', response)
     # Then
     assert login == ('Invalid email and/or password for email: '
                      '[email protected] [No row was found for one()]')
     assert response.status == HTTP_401
Ejemplo n.º 10
0
def call(method,
         api_or_module,
         url,
         body="",
         headers=None,
         params=None,
         query_string="",
         scheme="http",
         host=DEFAULT_HOST,
         **kwargs):
    """Simulates a round-trip call against the given API / URL"""
    api = API(api_or_module).http.server()
    response = StartResponseMock()
    headers = {} if headers is None else headers
    if not isinstance(body, str) and "json" in headers.get(
            "content-type", "application/json"):
        body = output_format.json(body)
        headers.setdefault("content-type", "application/json")

    params = params if params else {}
    params.update(kwargs)
    if params:
        query_string = "{}{}{}".format(query_string,
                                       "&" if query_string else "",
                                       urlencode(params, True))
    result = api(
        create_environ(
            path=url,
            method=method,
            headers=headers,
            query_string=query_string,
            body=body,
            scheme=scheme,
            host=host,
        ),
        response,
    )
    if result:
        response.data = _internal_result(result)
        response.content_type = response.headers_dict["content-type"]
        if "application/json" in response.content_type:
            response.data = json.loads(response.data)
    return response
Ejemplo n.º 11
0
def call(method, api_module, url, body="", headers=None, **params):
    """Simulates a round-trip call against the given api_module / url"""
    api = server(api_module)
    response = StartResponseMock()
    if not isinstance(body, str):
        body = output_format.json(body)
        headers = {} if headers is None else headers
        headers["content-type"] = "application/json"

    result = api(
        create_environ(path=url, method=method, headers=headers, query_string=urlencode(params), body=body), response
    )
    if result:
        try:
            response.data = result[0].decode("utf8")
        except TypeError:
            response.data = []
            for chunk in result:
                response.data.append(chunk.decode("utf8"))
            response.data = "".join(response.data)
        response.content_type = response.headers_dict["content-type"]
        if response.content_type == "application/json":
            response.data = json.loads(response.data)

    return response
Ejemplo n.º 12
0
 def test_get_specific_person(self, person1, api):
     # Given
     response = StartResponseMock()
     # When
     result = api.get_person(person1.person_id, response)
     # Then
     assert (result == {
         "person_id": person1.person_id,
         "label": person1.label
     })
     # When
     result = api.get_person(-1, response)
     # Then
     assert response.status == HTTP_404
     assert result is None
Ejemplo n.º 13
0
 def test_get_specific_user_profile(self, api, user1):
     # Given
     response = StartResponseMock()
     # When
     result = api.get_user_profile(user1.user_id, response, None)
     # Then
     assert (result == {
         "user_id": user1.user_id,
         "name": user1.name,
         "email": user1.email
     })
     # When
     result = api.get_user_profile(-1, response, None)
     # Then
     assert response.status == HTTP_404
     assert result is None
Ejemplo n.º 14
0
def test_basic_documentation_output_type_accept():
    """Ensure API documentation works with selectable output types"""
    accept_output = hug.output_format.accept(
        {
            "application/json": hug.output_format.json,
            "application/pretty-json": hug.output_format.pretty_json,
        },
        default=hug.output_format.json,
    )
    with mock.patch.object(api.http, "_output_format", accept_output, create=True):
        handler = api.http.documentation_404()
        response = StartResponseMock()

        handler(Request(create_environ(path="v1/doc")), response)

    documentation = json.loads(response.data.decode("utf8"))["documentation"]
    assert "handlers" in documentation and "overview" in documentation
Ejemplo n.º 15
0
 def test_get_specific_user(self, api, user1):
     # Given
     response = StartResponseMock()
     # When
     result = api.get_user(user1.user_id, response, None)
     # Then
     assert (result == {
         "user_id": user1.user_id,
         "name": user1.name,
         "email": user1.email,
         "person": user1.person,
         "boards": []
     })
     # When
     result = api.get_user(-1, response, None)
     # Then
     assert response.status == HTTP_404
     assert result is None
Ejemplo n.º 16
0
def test_basic_documentation_output_type_accept():
    """Ensure API documentation works with selectable output types"""
    accept_output = hug.output_format.accept(
        {
            'application/json': hug.output_format.json,
            'application/pretty-json': hug.output_format.pretty_json
        },
        default=hug.output_format.json)
    with mock.patch.object(api.http,
                           '_output_format',
                           accept_output,
                           create=True):
        handler = api.http.documentation_404()
        response = StartResponseMock()

        handler(Request(create_environ(path='v1/doc')), response)

    documentation = json.loads(response.data.decode('utf8'))['documentation']
    assert 'handlers' in documentation and 'overview' in documentation
Ejemplo n.º 17
0
 def test_get_reportedfeeling(self, api, reportedfeeling1):
     # Given
     response = StartResponseMock()
     # When
     result = api.get_reported_feeling(reportedfeeling1.board_id,
                                       reportedfeeling1.person_id,
                                       '2017-11-27', response)
     # Then
     assert result == {
         "board_id": reportedfeeling1.board_id,
         "person_id": reportedfeeling1.person_id,
         "date": "2017-11-27",
         "feeling": "a-feeling"
     }
     # When
     result = api.get_reported_feeling(reportedfeeling1.board_id,
                                       reportedfeeling1.person_id,
                                       '2038-01-01', response)
     # Then
     assert response.status == HTTP_404
     assert result is None
Ejemplo n.º 18
0
 def test_get_specific_board(self, api, board1, person1):
     # Given
     response = StartResponseMock()
     # When
     result = api.board(board1.board_id, response)
     # Then
     assert (result == {
         "board_id":
         board1.board_id,
         "label":
         board1.label,
         "people": [{
             "person_id": person1.person_id,
             "label": person1.label,
             "reportedfeelings": []
         }]
     })
     # When
     result = api.board(-1, response)
     # Then
     assert response.status == HTTP_404
     assert result is None
Ejemplo n.º 19
0
 def test_update_password(self, api, user1, user2, authenticated_user):
     # Given
     response = StartResponseMock()
     request = Request(create_environ(headers={'AUTHORIZATION': 'XXXX'}))
     # When
     result = api.update_password(user1.user_id, "newpassword", request,
                                  response, authenticated_user)
     # Then
     user = TESTSESSION.query(User).filter_by(user_id=user1.user_id).one()
     assert check_password(user, "newpassword") is True
     # When
     result = api.update_password(-1, "newpassword", request, response,
                                  authenticated_user)
     # Then
     assert response.status == HTTP_404
     assert result is None
     # When
     result = api.update_password(user2.user_id, "newpassword", request,
                                  response, authenticated_user)
     # Then
     assert response.status == HTTP_401
     assert result == ("Authenticated user isn't allowed to update the"
                       " password for requested user")
Ejemplo n.º 20
0
 def test_patch_user_profile(  # pylint: disable=too-many-arguments
         self, api, user1, user2, authenticated_user, mocker):
     # Given
     response = StartResponseMock()
     request = Request(create_environ(headers={'AUTHORIZATION': 'XXXX'}))
     # When
     result = api.patch_user_profile(user1.user_id, "newname",
                                     "newpassword", request, response,
                                     authenticated_user)
     # Then
     assert result == {
         "user_id": user1.user_id,
         "name": "newname",
         "email": "*****@*****.**"
     }
     # When
     result = api.patch_user_profile(-1, "newname", "newpassword", request,
                                     response, authenticated_user)
     # Then
     assert response.status == HTTP_404
     assert result is None
     # When
     result = api.patch_user_profile(user2.user_id, "newname",
                                     "newpassword", request, response,
                                     authenticated_user)
     # Then
     assert response.status == HTTP_401
     assert result == ("Authenticated user isn't allowed to update the"
                       " profile for requested user")
     # When
     mocker.patch.object(api.session, 'commit')
     api.session.commit.side_effect = InvalidRequestError()
     result = api.patch_user_profile(user1.user_id, "newname",
                                     "newpassword", request, response,
                                     authenticated_user)
     # Then
     assert result == 'User profile not updated'
Ejemplo n.º 21
0
def test_api_auto_initiate():
    """Test to ensure that Hug automatically exposes a wsgi server method"""
    assert isinstance(__hug_wsgi__(create_environ('/non_existant'), StartResponseMock()), (list, tuple))
Ejemplo n.º 22
0
def test_basic_documentation():
    """Ensure creating and then documenting APIs with Hug works as intuitively as expected"""

    @hug.get()
    def hello_world():
        """Returns hello world"""
        return "Hello World!"

    @hug.post()
    def echo(text):
        """Returns back whatever data it is given in the text parameter"""
        return text

    @hug.post("/happy_birthday", examples="name=HUG&age=1")
    def birthday(name, age: hug.types.number = 1):
        """Says happy birthday to a user"""
        return "Happy {age} Birthday {name}!".format(**locals())

    @hug.post()
    def noop(request, response):
        """Performs no action"""
        pass

    @hug.get()
    def string_docs(data: "Takes data", ignore_directive: hug.directives.Timer) -> "Returns data":
        """Annotations defined with strings should be documentation only"""
        pass

    @hug.get(private=True)
    def private():
        """Hidden from documentation"""
        pass

    documentation = api.http.documentation()
    assert "test_documentation" in documentation["overview"]

    assert "/hello_world" in documentation["handlers"]
    assert "/echo" in documentation["handlers"]
    assert "/happy_birthday" in documentation["handlers"]
    assert "/birthday" not in documentation["handlers"]
    assert "/noop" in documentation["handlers"]
    assert "/string_docs" in documentation["handlers"]
    assert "/private" not in documentation["handlers"]

    assert documentation["handlers"]["/hello_world"]["GET"]["usage"] == "Returns hello world"
    assert documentation["handlers"]["/hello_world"]["GET"]["examples"] == ["/hello_world"]
    assert documentation["handlers"]["/hello_world"]["GET"]["outputs"]["content_type"] in [
        "application/json",
        "application/json; charset=utf-8",
    ]
    assert "inputs" not in documentation["handlers"]["/hello_world"]["GET"]

    assert "text" in documentation["handlers"]["/echo"]["POST"]["inputs"]["text"]["type"]
    assert "default" not in documentation["handlers"]["/echo"]["POST"]["inputs"]["text"]

    assert "number" in documentation["handlers"]["/happy_birthday"]["POST"]["inputs"]["age"]["type"]
    assert documentation["handlers"]["/happy_birthday"]["POST"]["inputs"]["age"]["default"] == 1

    assert "inputs" not in documentation["handlers"]["/noop"]["POST"]

    assert (
        documentation["handlers"]["/string_docs"]["GET"]["inputs"]["data"]["type"] == "Takes data"
    )
    assert documentation["handlers"]["/string_docs"]["GET"]["outputs"]["type"] == "Returns data"
    assert "ignore_directive" not in documentation["handlers"]["/string_docs"]["GET"]["inputs"]

    @hug.post(versions=1)  # noqa
    def echo(text):
        """V1 Docs"""
        return "V1"

    @hug.post(versions=2)  # noqa
    def echo(text):
        """V1 Docs"""
        return "V2"

    @hug.post(versions=2)
    def test(text):
        """V1 Docs"""
        return True

    @hug.get(requires=test)
    def unversioned():
        return "Hello"

    @hug.get(versions=False)
    def noversions():
        pass

    @hug.extend_api("/fake", base_url="/api")
    def extend_with():
        import tests.module_fake_simple

        return (tests.module_fake_simple,)

    versioned_doc = api.http.documentation()
    assert "versions" in versioned_doc
    assert 1 in versioned_doc["versions"]
    assert 2 in versioned_doc["versions"]
    assert False not in versioned_doc["versions"]
    assert "/unversioned" in versioned_doc["handlers"]
    assert "/echo" in versioned_doc["handlers"]
    assert "/test" in versioned_doc["handlers"]

    specific_version_doc = api.http.documentation(api_version=1)
    assert "versions" in specific_version_doc
    assert "/echo" in specific_version_doc["handlers"]
    assert "/unversioned" in specific_version_doc["handlers"]
    assert specific_version_doc["handlers"]["/unversioned"]["GET"]["requires"] == ["V1 Docs"]
    assert "/test" not in specific_version_doc["handlers"]

    specific_base_doc = api.http.documentation(base_url="/api")
    assert "/echo" not in specific_base_doc["handlers"]
    assert "/fake/made_up_hello" in specific_base_doc["handlers"]

    handler = api.http.documentation_404()
    response = StartResponseMock()
    handler(Request(create_environ(path="v1/doc")), response)
    documentation = json.loads(response.data.decode("utf8"))["documentation"]
    assert "versions" in documentation
    assert "/echo" in documentation["handlers"]
    assert "/test" not in documentation["handlers"]
Ejemplo n.º 23
0
def test_basic_documentation():
    '''Ensure creating and then documenting APIs with Hug works as intuitively as expected'''
    @hug.get()
    def hello_world():
        '''Returns hello world'''
        return "Hello World!"

    @hug.post()
    def echo(text):
        '''Returns back whatever data it is given in the text parameter'''
        return text

    @hug.post('/happy_birthday', examples="name=HUG&age=1")
    def birthday(name, age: hug.types.number = 1):
        '''Says happy birthday to a user'''
        return "Happy {age} Birthday {name}!".format(**locals())

    @hug.post()
    def noop(request, response):
        '''Performs no action'''
        pass

    @hug.get()
    def string_docs(data: 'Takes data') -> 'Returns data':
        '''Annotations defined with strings should be documentation only'''
        pass

    documentation = hug.documentation.generate(api)
    assert 'test_documentation' in documentation['overview']

    assert '/hello_world' in documentation
    assert '/echo' in documentation
    assert '/happy_birthday' in documentation
    assert not '/birthday' in documentation
    assert '/noop' in documentation
    assert '/string_docs' in documentation

    assert documentation['/hello_world']['GET'][
        'usage'] == "Returns hello world"
    assert documentation['/hello_world']['GET']['examples'] == ["/hello_world"]
    assert documentation['/hello_world']['GET']['outputs'][
        'content_type'] == "application/json"
    assert not 'inputs' in documentation['/hello_world']['GET']

    assert 'text' in documentation['/echo']['POST']['inputs']['text']['type']
    assert not 'default' in documentation['/echo']['POST']['inputs']['text']

    assert 'number' in documentation['/happy_birthday']['POST']['inputs'][
        'age']['type']
    assert documentation['/happy_birthday']['POST']['inputs']['age'][
        'default'] == 1

    assert not 'inputs' in documentation['/noop']['POST']

    assert documentation['/string_docs']['GET']['inputs']['data'][
        'type'] == 'Takes data'
    assert documentation['/string_docs']['GET']['outputs'][
        'type'] == 'Returns data'

    @hug.post(versions=1)
    def echo(text):
        """V1 Docs"""
        return 'V1'

    @hug.get()
    def unversioned():
        return 'Hello'

    versioned_doc = hug.documentation.generate(api)
    assert 'versions' in versioned_doc
    assert 1 in versioned_doc['versions']
    assert '/unversioned' in versioned_doc['versions'][1]

    specific_version_doc = hug.documentation.generate(api, api_version=1)
    assert not 'versions' in specific_version_doc
    assert '/echo' in specific_version_doc
    assert '/unversioned' in specific_version_doc

    handler = hug.run.documentation_404(api)
    response = StartResponseMock()
    handler(Request(create_environ(path='v1/doc')), response)
    documentation = json.loads(response.data.decode('utf8'))['documentation']
    assert not 'versions' in documentation
    assert '/echo' in documentation
Ejemplo n.º 24
0
def test_basic_documentation():
    """Ensure creating and then documenting APIs with Hug works as intuitively as expected"""
    @hug.get()
    def hello_world():
        """Returns hello world"""
        return "Hello World!"

    @hug.post()
    def echo(text):
        """Returns back whatever data it is given in the text parameter"""
        return text

    @hug.post('/happy_birthday', examples="name=HUG&age=1")
    def birthday(name, age: hug.types.number = 1):
        """Says happy birthday to a user"""
        return "Happy {age} Birthday {name}!".format(**locals())

    @hug.post()
    def noop(request, response):
        """Performs no action"""
        pass

    @hug.get()
    def string_docs(data: 'Takes data',
                    ignore_directive: hug.directives.Timer) -> 'Returns data':
        """Annotations defined with strings should be documentation only"""
        pass

    @hug.get(private=True)
    def private():
        """Hidden from documentation"""
        pass

    documentation = api.http.documentation()
    assert 'test_documentation' in documentation['overview']

    assert '/hello_world' in documentation['handlers']
    assert '/echo' in documentation['handlers']
    assert '/happy_birthday' in documentation['handlers']
    assert not '/birthday' in documentation['handlers']
    assert '/noop' in documentation['handlers']
    assert '/string_docs' in documentation['handlers']
    assert not '/private' in documentation['handlers']

    assert documentation['handlers']['/hello_world']['GET'][
        'usage'] == "Returns hello world"
    assert documentation['handlers']['/hello_world']['GET']['examples'] == [
        "/hello_world"
    ]
    assert documentation['handlers']['/hello_world']['GET']['outputs'][
        'content_type'] == "application/json"
    assert not 'inputs' in documentation['handlers']['/hello_world']['GET']

    assert 'text' in documentation['handlers']['/echo']['POST']['inputs'][
        'text']['type']
    assert not 'default' in documentation['handlers']['/echo']['POST'][
        'inputs']['text']

    assert 'number' in documentation['handlers']['/happy_birthday']['POST'][
        'inputs']['age']['type']
    assert documentation['handlers']['/happy_birthday']['POST']['inputs'][
        'age']['default'] == 1

    assert not 'inputs' in documentation['handlers']['/noop']['POST']

    assert documentation['handlers']['/string_docs']['GET']['inputs']['data'][
        'type'] == 'Takes data'
    assert documentation['handlers']['/string_docs']['GET']['outputs'][
        'type'] == 'Returns data'
    assert not 'ignore_directive' in documentation['handlers']['/string_docs'][
        'GET']['inputs']

    @hug.post(versions=1)  # noqa
    def echo(text):
        """V1 Docs"""
        return 'V1'

    @hug.post(versions=2)  # noqa
    def echo(text):
        """V1 Docs"""
        return 'V2'

    @hug.post(versions=2)
    def test(text):
        """V1 Docs"""
        return True

    @hug.get(requires=test)
    def unversioned():
        return 'Hello'

    @hug.get(versions=False)
    def noversions():
        pass

    versioned_doc = api.http.documentation()
    assert 'versions' in versioned_doc
    assert 1 in versioned_doc['versions']
    assert 2 in versioned_doc['versions']
    assert False not in versioned_doc['versions']
    assert '/unversioned' in versioned_doc['handlers']
    assert '/echo' in versioned_doc['handlers']
    assert '/test' in versioned_doc['handlers']

    specific_version_doc = api.http.documentation(api_version=1)
    assert 'versions' in specific_version_doc
    assert '/echo' in specific_version_doc['handlers']
    assert '/unversioned' in specific_version_doc['handlers']
    assert specific_version_doc['handlers']['/unversioned']['GET'][
        'requires'] == ['V1 Docs']
    assert '/test' not in specific_version_doc['handlers']

    handler = api.http.documentation_404()
    response = StartResponseMock()
    handler(Request(create_environ(path='v1/doc')), response)
    documentation = json.loads(response.data.decode('utf8'))['documentation']
    assert 'versions' in documentation
    assert '/echo' in documentation['handlers']
    assert '/test' not in documentation['handlers']