Merge pull request #416 from subyraman/more-view-tests

Add CompositionView tests, simplify checks for invalid/duplicate methods
This commit is contained in:
Eli Uriegas 2017-02-13 11:09:56 -06:00 committed by GitHub
commit 1783df883e
3 changed files with 84 additions and 14 deletions

View File

@ -17,6 +17,7 @@ from .response import HTTPResponse
from .router import Router from .router import Router
from .server import serve, serve_multiple, HttpProtocol from .server import serve, serve_multiple, HttpProtocol
from .static import register as static_register from .static import register as static_register
from .views import CompositionView
class Sanic: class Sanic:
@ -120,7 +121,16 @@ class Sanic:
""" """
# Handle HTTPMethodView differently # Handle HTTPMethodView differently
if hasattr(handler, 'view_class'): if hasattr(handler, 'view_class'):
methods = frozenset(HTTP_METHODS) methods = set()
for method in HTTP_METHODS:
if getattr(handler.view_class, method.lower(), None):
methods.add(method)
# handle composition view differently
if isinstance(handler, CompositionView):
methods = handler.handlers.keys()
self.route(uri=uri, methods=methods, host=host)(handler) self.route(uri=uri, methods=methods, host=host)(handler)
return handler return handler

View File

@ -1,4 +1,5 @@
from .exceptions import InvalidUsage from .exceptions import InvalidUsage
from .constants import HTTP_METHODS
class HTTPMethodView: class HTTPMethodView:
@ -40,11 +41,7 @@ class HTTPMethodView:
def dispatch_request(self, request, *args, **kwargs): def dispatch_request(self, request, *args, **kwargs):
handler = getattr(self, request.method.lower(), None) handler = getattr(self, request.method.lower(), None)
if handler: return handler(request, *args, **kwargs)
return handler(request, *args, **kwargs)
raise InvalidUsage(
'Method {} not allowed for URL {}'.format(
request.method, request.url), status_code=405)
@classmethod @classmethod
def as_view(cls, *class_args, **class_kwargs): def as_view(cls, *class_args, **class_kwargs):
@ -89,15 +86,15 @@ class CompositionView:
def add(self, methods, handler): def add(self, methods, handler):
for method in methods: for method in methods:
if method not in HTTP_METHODS:
raise InvalidUsage(
'{} is not a valid HTTP method.'.format(method))
if method in self.handlers: if method in self.handlers:
raise KeyError( raise InvalidUsage(
'Method {} already is registered.'.format(method)) 'Method {} is already registered.'.format(method))
self.handlers[method] = handler self.handlers[method] = handler
def __call__(self, request, *args, **kwargs): def __call__(self, request, *args, **kwargs):
handler = self.handlers.get(request.method.upper(), None) handler = self.handlers[request.method.upper()]
if handler is None:
raise InvalidUsage(
'Method {} not allowed for URL {}'.format(
request.method, request.url), status_code=405)
return handler(request, *args, **kwargs) return handler(request, *args, **kwargs)

View File

@ -1,8 +1,9 @@
import pytest as pytest import pytest as pytest
from sanic import Sanic from sanic import Sanic
from sanic.exceptions import InvalidUsage
from sanic.response import text, HTTPResponse from sanic.response import text, HTTPResponse
from sanic.views import HTTPMethodView from sanic.views import HTTPMethodView, CompositionView
from sanic.blueprints import Blueprint from sanic.blueprints import Blueprint
from sanic.request import Request from sanic.request import Request
from sanic.utils import sanic_endpoint_test from sanic.utils import sanic_endpoint_test
@ -196,3 +197,65 @@ def test_with_decorator():
request, response = sanic_endpoint_test(app, method="get") request, response = sanic_endpoint_test(app, method="get")
assert response.text == 'I am get method' assert response.text == 'I am get method'
assert results[0] == 1 assert results[0] == 1
def test_composition_view_rejects_incorrect_methods():
def foo(request):
return text('Foo')
view = CompositionView()
with pytest.raises(InvalidUsage) as e:
view.add(['GET', 'FOO'], foo)
assert str(e.value) == 'FOO is not a valid HTTP method.'
def test_composition_view_rejects_duplicate_methods():
def foo(request):
return text('Foo')
view = CompositionView()
with pytest.raises(InvalidUsage) as e:
view.add(['GET', 'POST', 'GET'], foo)
assert str(e.value) == 'Method GET is already registered.'
@pytest.mark.parametrize('method', HTTP_METHODS)
def test_composition_view_runs_methods_as_expected(method):
app = Sanic('test_composition_view')
view = CompositionView()
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
view.add(['DELETE', 'PATCH'], lambda x: text('second method'))
app.add_route(view, '/')
if method in ['GET', 'POST', 'PUT']:
request, response = sanic_endpoint_test(app, uri='/', method=method)
assert response.text == 'first method'
if method in ['DELETE', 'PATCH']:
request, response = sanic_endpoint_test(app, uri='/', method=method)
assert response.text == 'second method'
@pytest.mark.parametrize('method', HTTP_METHODS)
def test_composition_view_rejects_invalid_methods(method):
app = Sanic('test_composition_view')
view = CompositionView()
view.add(['GET', 'POST', 'PUT'], lambda x: text('first method'))
app.add_route(view, '/')
if method in ['GET', 'POST', 'PUT']:
request, response = sanic_endpoint_test(app, uri='/', method=method)
assert response.status == 200
assert response.text == 'first method'
if method in ['DELETE', 'PATCH']:
request, response = sanic_endpoint_test(app, uri='/', method=method)
assert response.status == 405