From e54e869385b01c331aa406216afce88353aca13c Mon Sep 17 00:00:00 2001 From: Petr Skramovsky Date: Mon, 29 Feb 2016 17:34:13 +0100 Subject: [PATCH] wsgi test case added --- clearest/__init__.py | 5 +++-- clearest/core.py | 22 +++++++++++++++++++--- clearest/exceptions.py | 13 +++++++++++++ clearest/http.py | 18 ++++++++++++++++++ clearest/wsgi.py | 9 +++++++++ tests/test_application.py | 11 +++++++++++ tests/wsgi.py | 37 +++++++++++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 5 deletions(-) create mode 100644 clearest/http.py create mode 100644 clearest/wsgi.py create mode 100644 tests/test_application.py create mode 100644 tests/wsgi.py diff --git a/clearest/__init__.py b/clearest/__init__.py index 4f0afdb..bdfe61c 100644 --- a/clearest/__init__.py +++ b/clearest/__init__.py @@ -1,5 +1,6 @@ -from clearest.core import GET, unregister_all -from clearest.exceptions import MissingArgumentError, AlreadyRegisteredError, NotUniqueError +from clearest.core import GET, POST, application, unregister_all, application +from clearest.http import * +from clearest.exceptions import * __all__ = [ "MissingArgumentError", diff --git a/clearest/core.py b/clearest/core.py index c69e34b..261e7a9 100644 --- a/clearest/core.py +++ b/clearest/core.py @@ -3,8 +3,12 @@ import functools import inspect import re + import six -from clearest.exceptions import MissingArgumentError, AlreadyRegisteredError, NotUniqueError + +from clearest.exceptions import MissingArgumentError, AlreadyRegisteredError, NotUniqueError, HttpError, HttpBadRequest +from clearest.http import HTTP_GET, HTTP_POST, CONTENT_TYPE, MIME_TEXT_PLAIN +from clearest.wsgi import REQUEST_METHOD KEY_PATTERN = re.compile("\{(.*)\}") @@ -56,6 +60,18 @@ def all_registered(): def unregister_all(): BaseDecorator.registered.clear() +def application(environ, start_response): + try: + if environ[REQUEST_METHOD] not in all_registered(): + raise HttpBadRequest() + registered = BaseDecorator.registered[environ[REQUEST_METHOD]] + except HttpError as error: + start_response("{code} {msg}".format(code=error.code, msg=error.msg), [(CONTENT_TYPE, MIME_TEXT_PLAIN)]) + return [] + else: + pass + + @six.add_metaclass(ABCMeta) class BaseDecorator(object): registered = defaultdict(lambda: dict()) @@ -84,9 +100,9 @@ def type(self): class GET(BaseDecorator): def type(self): - return "GET" + return HTTP_GET class POST(BaseDecorator): def type(self): - return "POST" + return HTTP_POST diff --git a/clearest/exceptions.py b/clearest/exceptions.py index f3829fe..7a32885 100644 --- a/clearest/exceptions.py +++ b/clearest/exceptions.py @@ -1,3 +1,6 @@ +from clearest.http import HTTP_BAD_REQUEST + + class MissingArgumentError(Exception): def __init__(self, fn_name, arg): super(MissingArgumentError, self).__init__("function {name} is missing argument {arg}!".format(name=fn_name, arg=arg)) @@ -9,3 +12,13 @@ def __init__(self, path, old_fn_name): class NotUniqueError(Exception): def __init__(self, var_name): super(NotUniqueError, self).__init__("variable {var} is not unique".format(var=var_name)) + +class HttpError(Exception): + def __init__(self, code, msg): + super(HttpError, self).__init__() + self.code = code + self.msg = msg + +class HttpBadRequest(HttpError): + def __init__(self): + super(HttpBadRequest, self).__init__(*HTTP_BAD_REQUEST) diff --git a/clearest/http.py b/clearest/http.py new file mode 100644 index 0000000..416c692 --- /dev/null +++ b/clearest/http.py @@ -0,0 +1,18 @@ +from collections import namedtuple + +HTTP_GET = "GET" +HTTP_POST = "POST" + +HttpStatus = namedtuple("HttpStatus", ["code", "msg"]) +HTTP_OK = HttpStatus(200, "OK") +HTTP_BAD_REQUEST = HttpStatus(403, "Bad-request") + +HTTP_1_0 = "HTTP/1.0" +HTTP_1_1 = "HTTP/1.1" + +HTTP_METHODS = (HTTP_GET, HTTP_POST) +HTTP_PROTOCOLS = (HTTP_1_0, HTTP_1_1) + +MIME_TEXT_PLAIN = "text/plain" + +CONTENT_TYPE = "Content-type" diff --git a/clearest/wsgi.py b/clearest/wsgi.py new file mode 100644 index 0000000..842d358 --- /dev/null +++ b/clearest/wsgi.py @@ -0,0 +1,9 @@ +REQUEST_METHOD = "REQUEST_METHOD" +SCRIPT_NAME = "SCRIPT_NAME" +PATH_INFO = "PATH_INFO" +QUERY_STRING = "QUERY_STRING" +CONTENT_TYPE = "CONTENT_TYPE" +CONTENT_LENGTH = "CONTENT_LENGTH" +SERVER_NAME = "SERVER_NAME" +SERVER_PORT = "SERVER_PORT" +SERVER_PROTOCOL = "SERVER_PROTOCOL" diff --git a/tests/test_application.py b/tests/test_application.py new file mode 100644 index 0000000..d29b61a --- /dev/null +++ b/tests/test_application.py @@ -0,0 +1,11 @@ +from clearest import POST, HTTP_BAD_REQUEST +from tests.wsgi import WSGITestCase + + +class Test(WSGITestCase): + def test_application_bad_request(self): + @POST("/asd") + def asd(): + return {} + result = self.get("/asd") + self.assertEqual(HTTP_BAD_REQUEST, self.status) diff --git a/tests/wsgi.py b/tests/wsgi.py new file mode 100644 index 0000000..7bc90cf --- /dev/null +++ b/tests/wsgi.py @@ -0,0 +1,37 @@ +from unittest import TestCase +from clearest.http import * +from clearest.wsgi import * +from clearest.core import application + + +class WSGITestCase(TestCase): + def __init__(self, *args, **kwargs): + super(WSGITestCase, self).__init__(*args, **kwargs) + self.headers = [] + self.status = None + + def _start_response(self, status, headers): + code, msg = status.split(" ") + self.status = HttpStatus(code=int(code), msg=msg) + self.headers = headers + + def request(self, app, method, query): + assert method in HTTP_METHODS + env = {REQUEST_METHOD: method, + SERVER_PROTOCOL: HTTP_1_1, + SERVER_NAME: "wsgi_unit_test", + SERVER_PORT: 42, + SCRIPT_NAME: ""} + parts = query.split("?") + assert len(parts) <= 2 + if len(parts) is 2: + env[PATH_INFO], env[QUERY_STRING] = parts + else: + env[PATH_INFO] = query + result = [] + for data in app(env, self._start_response): + result.append(data) + return result + + def get(self, query, app=application): + return self.request(app, HTTP_GET, query)