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
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
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'
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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")
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'
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))
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"]
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
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']