RAHHH
This commit is contained in:
@@ -0,0 +1,300 @@
|
||||
# Copyright (c) Twisted Matrix Laboratories.
|
||||
# See LICENSE for details.
|
||||
|
||||
"""
|
||||
Tests for L{twisted.web.resource}.
|
||||
"""
|
||||
|
||||
from twisted.trial.unittest import TestCase
|
||||
from twisted.web.error import UnsupportedMethod
|
||||
from twisted.web.http_headers import Headers
|
||||
from twisted.web.iweb import IRequest
|
||||
from twisted.web.resource import (
|
||||
FORBIDDEN,
|
||||
NOT_FOUND,
|
||||
Resource,
|
||||
_UnsafeErrorPage as ErrorPage,
|
||||
_UnsafeForbiddenResource as ForbiddenResource,
|
||||
_UnsafeNoResource as NoResource,
|
||||
getChildForRequest,
|
||||
)
|
||||
from twisted.web.test.requesthelper import DummyRequest
|
||||
|
||||
|
||||
class ErrorPageTests(TestCase):
|
||||
"""
|
||||
Tests for L{_UnafeErrorPage}, L{_UnsafeNoResource}, and
|
||||
L{_UnsafeForbiddenResource}.
|
||||
"""
|
||||
|
||||
def test_deprecatedErrorPage(self) -> None:
|
||||
"""
|
||||
The public C{twisted.web.resource.ErrorPage} alias for the
|
||||
corresponding C{_Unsafe} class produces a deprecation warning when
|
||||
called.
|
||||
"""
|
||||
_ = ErrorPage(123, "ono", "ono!")
|
||||
[warning] = self.flushWarnings()
|
||||
self.assertEqual(warning["category"], DeprecationWarning)
|
||||
self.assertIn("twisted.web.pages.errorPage", warning["message"])
|
||||
|
||||
def test_deprecatedNoResource(self) -> None:
|
||||
"""
|
||||
The public C{twisted.web.resource.NoResource} alias for the
|
||||
corresponding C{_Unsafe} class produces a deprecation warning when
|
||||
called.
|
||||
"""
|
||||
_ = NoResource()
|
||||
[warning] = self.flushWarnings()
|
||||
self.assertEqual(warning["category"], DeprecationWarning)
|
||||
self.assertIn("twisted.web.pages.notFound", warning["message"])
|
||||
|
||||
def test_deprecatedForbiddenResource(self) -> None:
|
||||
"""
|
||||
The public C{twisted.web.resource.ForbiddenResource} alias for the
|
||||
corresponding C{_Unsafe} class produce a deprecation warning when
|
||||
called.
|
||||
"""
|
||||
_ = ForbiddenResource()
|
||||
[warning] = self.flushWarnings()
|
||||
self.assertEqual(warning["category"], DeprecationWarning)
|
||||
self.assertIn("twisted.web.pages.forbidden", warning["message"])
|
||||
|
||||
def test_getChild(self) -> None:
|
||||
"""
|
||||
The C{getChild} method of L{ErrorPage} returns the L{ErrorPage} it is
|
||||
called on.
|
||||
"""
|
||||
page = ErrorPage(321, "foo", "bar")
|
||||
self.assertIdentical(page.getChild(b"name", object()), page)
|
||||
|
||||
def _pageRenderingTest(
|
||||
self, page: Resource, code: int, brief: str, detail: str
|
||||
) -> None:
|
||||
request = DummyRequest([b""])
|
||||
template = (
|
||||
"\n"
|
||||
"<html>\n"
|
||||
" <head><title>%s - %s</title></head>\n"
|
||||
" <body>\n"
|
||||
" <h1>%s</h1>\n"
|
||||
" <p>%s</p>\n"
|
||||
" </body>\n"
|
||||
"</html>\n"
|
||||
)
|
||||
expected = template % (code, brief, brief, detail)
|
||||
self.assertEqual(page.render(request), expected.encode("utf-8"))
|
||||
self.assertEqual(request.responseCode, code)
|
||||
self.assertEqual(
|
||||
request.responseHeaders,
|
||||
Headers({b"content-type": [b"text/html; charset=utf-8"]}),
|
||||
)
|
||||
|
||||
def test_errorPageRendering(self) -> None:
|
||||
"""
|
||||
L{ErrorPage.render} returns a C{bytes} describing the error defined by
|
||||
the response code and message passed to L{ErrorPage.__init__}. It also
|
||||
uses that response code to set the response code on the L{Request}
|
||||
passed in.
|
||||
"""
|
||||
code = 321
|
||||
brief = "brief description text"
|
||||
detail = "much longer text might go here"
|
||||
page = ErrorPage(code, brief, detail)
|
||||
self._pageRenderingTest(page, code, brief, detail)
|
||||
|
||||
def test_noResourceRendering(self) -> None:
|
||||
"""
|
||||
L{NoResource} sets the HTTP I{NOT FOUND} code.
|
||||
"""
|
||||
detail = "long message"
|
||||
page = NoResource(detail)
|
||||
self._pageRenderingTest(page, NOT_FOUND, "No Such Resource", detail)
|
||||
|
||||
def test_forbiddenResourceRendering(self) -> None:
|
||||
"""
|
||||
L{ForbiddenResource} sets the HTTP I{FORBIDDEN} code.
|
||||
"""
|
||||
detail = "longer message"
|
||||
page = ForbiddenResource(detail)
|
||||
self._pageRenderingTest(page, FORBIDDEN, "Forbidden Resource", detail)
|
||||
|
||||
|
||||
class DynamicChild(Resource):
|
||||
"""
|
||||
A L{Resource} to be created on the fly by L{DynamicChildren}.
|
||||
"""
|
||||
|
||||
def __init__(self, path: bytes, request: IRequest) -> None:
|
||||
Resource.__init__(self)
|
||||
self.path = path
|
||||
self.request = request
|
||||
|
||||
|
||||
class DynamicChildren(Resource):
|
||||
"""
|
||||
A L{Resource} with dynamic children.
|
||||
"""
|
||||
|
||||
def getChild(self, path: bytes, request: IRequest) -> DynamicChild:
|
||||
return DynamicChild(path, request)
|
||||
|
||||
|
||||
class BytesReturnedRenderable(Resource):
|
||||
"""
|
||||
A L{Resource} with minimal capabilities to render a response.
|
||||
"""
|
||||
|
||||
def __init__(self, response: bytes) -> None:
|
||||
"""
|
||||
@param response: A C{bytes} object giving the value to return from
|
||||
C{render_GET}.
|
||||
"""
|
||||
Resource.__init__(self)
|
||||
self._response = response
|
||||
|
||||
def render_GET(self, request: object) -> bytes:
|
||||
"""
|
||||
Render a response to a I{GET} request by returning a short byte string
|
||||
to be written by the server.
|
||||
"""
|
||||
return self._response
|
||||
|
||||
|
||||
class ImplicitAllowedMethods(Resource):
|
||||
"""
|
||||
A L{Resource} which implicitly defines its allowed methods by defining
|
||||
renderers to handle them.
|
||||
"""
|
||||
|
||||
def render_GET(self, request: object) -> None:
|
||||
pass
|
||||
|
||||
def render_PUT(self, request: object) -> None:
|
||||
pass
|
||||
|
||||
|
||||
class ResourceTests(TestCase):
|
||||
"""
|
||||
Tests for L{Resource}.
|
||||
"""
|
||||
|
||||
def test_staticChildren(self) -> None:
|
||||
"""
|
||||
L{Resource.putChild} adds a I{static} child to the resource. That child
|
||||
is returned from any call to L{Resource.getChildWithDefault} for the
|
||||
child's path.
|
||||
"""
|
||||
resource = Resource()
|
||||
child = Resource()
|
||||
sibling = Resource()
|
||||
resource.putChild(b"foo", child)
|
||||
resource.putChild(b"bar", sibling)
|
||||
self.assertIdentical(
|
||||
child, resource.getChildWithDefault(b"foo", DummyRequest([]))
|
||||
)
|
||||
|
||||
def test_dynamicChildren(self) -> None:
|
||||
"""
|
||||
L{Resource.getChildWithDefault} delegates to L{Resource.getChild} when
|
||||
the requested path is not associated with any static child.
|
||||
"""
|
||||
path = b"foo"
|
||||
request = DummyRequest([])
|
||||
resource = DynamicChildren()
|
||||
child = resource.getChildWithDefault(path, request)
|
||||
self.assertIsInstance(child, DynamicChild)
|
||||
self.assertEqual(child.path, path)
|
||||
self.assertIdentical(child.request, request)
|
||||
|
||||
def test_staticChildPathType(self) -> None:
|
||||
"""
|
||||
Test that passing the wrong type to putChild results in a warning,
|
||||
and a failure in Python 3
|
||||
"""
|
||||
resource = Resource()
|
||||
child = Resource()
|
||||
sibling = Resource()
|
||||
self.assertRaises(TypeError, resource.putChild, "foo", child)
|
||||
self.assertRaises(TypeError, resource.putChild, None, sibling)
|
||||
|
||||
def test_defaultHEAD(self) -> None:
|
||||
"""
|
||||
When not otherwise overridden, L{Resource.render} treats a I{HEAD}
|
||||
request as if it were a I{GET} request.
|
||||
"""
|
||||
expected = b"insert response here"
|
||||
request = DummyRequest([])
|
||||
request.method = b"HEAD"
|
||||
resource = BytesReturnedRenderable(expected)
|
||||
self.assertEqual(expected, resource.render(request))
|
||||
|
||||
def test_explicitAllowedMethods(self) -> None:
|
||||
"""
|
||||
The L{UnsupportedMethod} raised by L{Resource.render} for an unsupported
|
||||
request method has a C{allowedMethods} attribute set to the value of the
|
||||
C{allowedMethods} attribute of the L{Resource}, if it has one.
|
||||
"""
|
||||
expected = [b"GET", b"HEAD", b"PUT"]
|
||||
resource = Resource()
|
||||
resource.allowedMethods = expected
|
||||
request = DummyRequest([])
|
||||
request.method = b"FICTIONAL"
|
||||
exc = self.assertRaises(UnsupportedMethod, resource.render, request)
|
||||
self.assertEqual(set(expected), set(exc.allowedMethods))
|
||||
|
||||
def test_implicitAllowedMethods(self) -> None:
|
||||
"""
|
||||
The L{UnsupportedMethod} raised by L{Resource.render} for an unsupported
|
||||
request method has a C{allowedMethods} attribute set to a list of the
|
||||
methods supported by the L{Resource}, as determined by the
|
||||
I{render_}-prefixed methods which it defines, if C{allowedMethods} is
|
||||
not explicitly defined by the L{Resource}.
|
||||
"""
|
||||
expected = {b"GET", b"HEAD", b"PUT"}
|
||||
resource = ImplicitAllowedMethods()
|
||||
request = DummyRequest([])
|
||||
request.method = b"FICTIONAL"
|
||||
exc = self.assertRaises(UnsupportedMethod, resource.render, request)
|
||||
self.assertEqual(expected, set(exc.allowedMethods))
|
||||
|
||||
|
||||
class GetChildForRequestTests(TestCase):
|
||||
"""
|
||||
Tests for L{getChildForRequest}.
|
||||
"""
|
||||
|
||||
def test_exhaustedPostPath(self) -> None:
|
||||
"""
|
||||
L{getChildForRequest} returns whatever resource has been reached by the
|
||||
time the request's C{postpath} is empty.
|
||||
"""
|
||||
request = DummyRequest([])
|
||||
resource = Resource()
|
||||
result = getChildForRequest(resource, request)
|
||||
self.assertIdentical(resource, result)
|
||||
|
||||
def test_leafResource(self) -> None:
|
||||
"""
|
||||
L{getChildForRequest} returns the first resource it encounters with a
|
||||
C{isLeaf} attribute set to C{True}.
|
||||
"""
|
||||
request = DummyRequest([b"foo", b"bar"])
|
||||
resource = Resource()
|
||||
resource.isLeaf = True
|
||||
result = getChildForRequest(resource, request)
|
||||
self.assertIdentical(resource, result)
|
||||
|
||||
def test_postPathToPrePath(self) -> None:
|
||||
"""
|
||||
As path segments from the request are traversed, they are taken from
|
||||
C{postpath} and put into C{prepath}.
|
||||
"""
|
||||
request = DummyRequest([b"foo", b"bar"])
|
||||
root = Resource()
|
||||
child = Resource()
|
||||
child.isLeaf = True
|
||||
root.putChild(b"foo", child)
|
||||
self.assertIdentical(child, getChildForRequest(root, request))
|
||||
self.assertEqual(request.prepath, [b"foo"])
|
||||
self.assertEqual(request.postpath, [b"bar"])
|
||||
Reference in New Issue
Block a user