From 2d3f131d59a3c9e9e53e2d131861a7f2c461bb4c Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 27 Oct 2020 21:25:38 +0900 Subject: [PATCH 1/5] Refactor test_api.py --- tests/test_api.py | 59 ++++++++++++++++++++------------------------- tests/wsgi_utils.py | 17 +++++++++---- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index e24ee7dc..8a296922 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1,23 +1,9 @@ import json -from typing import Dict, Optional, Any from unittest import TestCase import optuna -from .wsgi_utils import create_wsgi_env, send_request, WSGIEnv from optuna_dashboard.app import create_app - - -def create_json_api_wsgi_env( - path: str, - method: str, - json_body: Optional[Dict[str, Any]] = None, - content_type: str = "application/json", - headers: Optional[Dict[str, str]] = None, -) -> WSGIEnv: - body = json.dumps(json_body) if json_body else "" - return create_wsgi_env( - path, method, body, content_type=content_type, headers=headers - ) +from .wsgi_utils import send_request class APITestCase(TestCase): @@ -27,11 +13,12 @@ class APITestCase(TestCase): storage.create_new_study("foo2") app = create_app(storage) - env = create_json_api_wsgi_env( + status, _, body = send_request( + app, "/api/studies/", "GET", + content_type="application/json", ) - status, _, body = send_request(app, env) self.assertEqual(status, "200 OK") study_summaries = json.loads(body)["study_summaries"] self.assertEqual(len(study_summaries), 2) @@ -41,15 +28,17 @@ class APITestCase(TestCase): self.assertEqual(len(storage.get_all_study_summaries()), 0) app = create_app(storage) - env = create_json_api_wsgi_env( + request_body = { + "study_name": "foo", + "direction": "minimize", + } + status, _, _ = send_request( + app, "/api/studies", "POST", - json_body={ - "study_name": "foo", - "direction": "minimize", - }, + content_type="application/json", + body=json.dumps(request_body), ) - status, _, _ = send_request(app, env) self.assertEqual(status, "201 Created") self.assertEqual(len(storage.get_all_study_summaries()), 1) @@ -59,15 +48,17 @@ class APITestCase(TestCase): self.assertEqual(len(storage.get_all_study_summaries()), 1) app = create_app(storage) - env = create_json_api_wsgi_env( + request_body = { + "study_name": "foo", + "direction": "minimize", + } + status, _, _ = send_request( + app, "/api/studies", "POST", - json_body={ - "study_name": "foo", - "direction": "minimize", - }, + content_type="application/json", + body=json.dumps(request_body), ) - status, _, _ = send_request(app, env) self.assertEqual(status, "400 Bad Request") self.assertEqual(len(storage.get_all_study_summaries()), 1) @@ -78,20 +69,22 @@ class APITestCase(TestCase): self.assertEqual(len(storage.get_all_study_summaries()), 2) app = create_app(storage) - env = create_json_api_wsgi_env( + status, _, _ = send_request( + app, "/api/studies/1", "DELETE", + content_type="application/json", ) - status, _, _ = send_request(app, env) self.assertEqual(status, "204 No Content") self.assertEqual(len(storage.get_all_study_summaries()), 1) def test_delete_study_not_found(self) -> None: storage = optuna.storages.InMemoryStorage() app = create_app(storage) - env = create_json_api_wsgi_env( + status, _, _ = send_request( + app, "/api/studies/1", "DELETE", + content_type="application/json", ) - status, _, _ = send_request(app, env) self.assertEqual(status, "404 Not Found") diff --git a/tests/wsgi_utils.py b/tests/wsgi_utils.py index 37227f0a..9b5f4b28 100644 --- a/tests/wsgi_utils.py +++ b/tests/wsgi_utils.py @@ -46,19 +46,26 @@ def create_wsgi_env( def send_request( - app: WSGIApp, env: WSGIEnv + app: WSGIApp, + path: str, + method: str, + body: Union[str, bytes] = b"", + queries: Optional[Dict[str, str]] = None, + headers: Optional[Dict[str, str]] = None, + content_type: str = "text/plain; charset=utf-8", ) -> Tuple[str, List[Tuple[str, str]], bytes]: status: str = "" - headers: List[Tuple[str, str]] = [] + response_headers: List[Tuple[str, str]] = [] def start_response(status_: str, headers_: List[Tuple[str, str]]) -> None: - nonlocal status, headers + nonlocal status, response_headers status = status_ - headers = headers_ + response_headers = headers_ + env = create_wsgi_env(path, method, body=body, queries=queries, headers=headers, content_type=content_type) body = b"" iterable_body = app(env, start_response) for b in iterable_body: body += b - return status, headers, body + return status, response_headers, body From 4248d04e26fa63fa775e6641b753f27033e03703 Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 27 Oct 2020 21:30:25 +0900 Subject: [PATCH 2/5] Refactor wsgi_utils.py --- tests/wsgi_utils.py | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/tests/wsgi_utils.py b/tests/wsgi_utils.py index 9b5f4b28..4a88b9c3 100644 --- a/tests/wsgi_utils.py +++ b/tests/wsgi_utils.py @@ -9,37 +9,31 @@ WSGIApp = Callable[[WSGIEnv, StartResponse], Iterable[bytes]] def create_wsgi_env( path: str, method: str, - body: Union[str, bytes] = b"", - queries: Optional[Dict[str, str]] = None, - headers: Optional[Dict[str, str]] = None, - content_type: str = "text/plain; charset=utf-8", + content_type: str, + body: bytes, + queries: Dict[str, str], + headers: Dict[str, str], ) -> WSGIEnv: - request_method = method.upper() - bytes_body = body if isinstance(body, bytes) else body.encode("utf-8") - wsgi_input = io.BytesIO(bytes_body) - content_length = len(body) - # 'key1=value1&key2=value2' - query_string = "&".join([f"{k}={v}" for k, v in queries.items()]) if queries else "" + query_string = "&".join([f"{k}={v}" for k, v in queries.items()]) # See https://www.python.org/dev/peps/pep-3333/#environ-variables env = { "PATH_INFO": path, - "REQUEST_METHOD": request_method, + "REQUEST_METHOD": method.upper(), "SCRIPT_NAME": "", "QUERY_STRING": query_string, "CONTENT_TYPE": content_type, - "CONTENT_LENGTH": content_length, + "CONTENT_LENGTH": len(body), "SERVER_PROTOCOL": "http", "SERVER_NAME": "localhost", - "wsgi.input": wsgi_input, + "wsgi.input": io.BytesIO(body), "wsgi.version": (1, 0), "wsgi.errors": io.StringIO(""), "wsgi.multithread": True, "wsgi.multitprocess": True, "wsgi.run_once": False, } - headers = headers or {} for k, v in headers.items(): env[f"HTTP_{k.upper()}"] = v return env @@ -62,7 +56,10 @@ def send_request( status = status_ response_headers = headers_ - env = create_wsgi_env(path, method, body=body, queries=queries, headers=headers, content_type=content_type) + bytes_body = body if isinstance(body, bytes) else body.encode("utf-8") + headers = headers or {} + queries = queries or {} + env = create_wsgi_env(path, method, content_type, bytes_body, queries, headers) body = b"" iterable_body = app(env, start_response) for b in iterable_body: From 86a8563f933263cfa8555b697ec344aecf088b32 Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 27 Oct 2020 21:35:23 +0900 Subject: [PATCH 3/5] Add tests for ignore trailing slashes --- tests/test_api.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_api.py b/tests/test_api.py index 8a296922..07b31b90 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -88,3 +88,20 @@ class APITestCase(TestCase): content_type="application/json", ) self.assertEqual(status, "404 Not Found") + + +class BottleRequestHookTestCase(TestCase): + def test_ignore_trailing_slashes(self) -> None: + storage = optuna.storages.InMemoryStorage() + app = create_app(storage) + + endpoints = ["/api/studies", "/api/studies/"] + for endpoint in endpoints: + with self.subTest(msg=endpoint): + status, _, body = send_request( + app, + endpoint, + "GET", + content_type="application/json", + ) + self.assertEqual(status, "200 OK") From 317efb0e2945e590ff8f05ac7ec108ce6fa93d42 Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 27 Oct 2020 21:37:03 +0900 Subject: [PATCH 4/5] Assert status code number --- tests/test_api.py | 12 ++++++------ tests/wsgi_utils.py | 5 +++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/tests/test_api.py b/tests/test_api.py index 07b31b90..2e49037f 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -19,7 +19,7 @@ class APITestCase(TestCase): "GET", content_type="application/json", ) - self.assertEqual(status, "200 OK") + self.assertEqual(status, 200) study_summaries = json.loads(body)["study_summaries"] self.assertEqual(len(study_summaries), 2) @@ -39,7 +39,7 @@ class APITestCase(TestCase): content_type="application/json", body=json.dumps(request_body), ) - self.assertEqual(status, "201 Created") + self.assertEqual(status, 201) self.assertEqual(len(storage.get_all_study_summaries()), 1) def test_create_study_duplicated(self) -> None: @@ -59,7 +59,7 @@ class APITestCase(TestCase): content_type="application/json", body=json.dumps(request_body), ) - self.assertEqual(status, "400 Bad Request") + self.assertEqual(status, 400) self.assertEqual(len(storage.get_all_study_summaries()), 1) def test_delete_study(self) -> None: @@ -75,7 +75,7 @@ class APITestCase(TestCase): "DELETE", content_type="application/json", ) - self.assertEqual(status, "204 No Content") + self.assertEqual(status, 204) self.assertEqual(len(storage.get_all_study_summaries()), 1) def test_delete_study_not_found(self) -> None: @@ -87,7 +87,7 @@ class APITestCase(TestCase): "DELETE", content_type="application/json", ) - self.assertEqual(status, "404 Not Found") + self.assertEqual(status, 404) class BottleRequestHookTestCase(TestCase): @@ -104,4 +104,4 @@ class BottleRequestHookTestCase(TestCase): "GET", content_type="application/json", ) - self.assertEqual(status, "200 OK") + self.assertEqual(status, 200) diff --git a/tests/wsgi_utils.py b/tests/wsgi_utils.py index 4a88b9c3..d6975951 100644 --- a/tests/wsgi_utils.py +++ b/tests/wsgi_utils.py @@ -47,7 +47,7 @@ def send_request( queries: Optional[Dict[str, str]] = None, headers: Optional[Dict[str, str]] = None, content_type: str = "text/plain; charset=utf-8", -) -> Tuple[str, List[Tuple[str, str]], bytes]: +) -> Tuple[int, List[Tuple[str, str]], bytes]: status: str = "" response_headers: List[Tuple[str, str]] = [] @@ -65,4 +65,5 @@ def send_request( for b in iterable_body: body += b - return status, response_headers, body + status_code = int(status.split()[0]) + return status_code, response_headers, body From cd02661cf5f6a02ea5ea1d7e33c95afeb58d7dfa Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 27 Oct 2020 21:38:00 +0900 Subject: [PATCH 5/5] Rename to wsgi_client --- tests/test_api.py | 2 +- tests/{wsgi_utils.py => wsgi_client.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/{wsgi_utils.py => wsgi_client.py} (100%) diff --git a/tests/test_api.py b/tests/test_api.py index 2e49037f..de5d97d7 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -3,7 +3,7 @@ from unittest import TestCase import optuna from optuna_dashboard.app import create_app -from .wsgi_utils import send_request +from .wsgi_client import send_request class APITestCase(TestCase): diff --git a/tests/wsgi_utils.py b/tests/wsgi_client.py similarity index 100% rename from tests/wsgi_utils.py rename to tests/wsgi_client.py