vlastic / test / rom_test.py

"""Unit tests for ROM(Resource-Object Mapping) module."""

import unittest
from vlastic.rom import *
from vlastic.rom.decorator import *
from vlastic.http import *

def test_view(context):
    return Response(
        ("HTTP/1.1", 200, "OK"),
        {"Content-Type": "text/plain"},
        context["message"]
    )


class TestResource(Resource):
    """A resource class for unit tests."""

    def __init__(self, value):
        self.value = str(value)

    @get(test_view)
    def get_method_test(self):
        return {"message": "GET method test"}

    @post(test_view)
    @put(lambda context, req: "PUT: " + context["message"] + " " +str(req.path))
    def post_method_test(self):
        return {"message": self.value}

    custom_method = method("Custom")(test_view)

    @custom_method
    def custom_method_test(self):
        return {"message": "CUSTOM method test"}


class ResourceTest(unittest.TestCase):
    """Unit tests for Resource class with simple request specified by
    HTTP 0.9.

    """

    def setUp(self):
        self.rsc = TestResource("root")

    def test_dispatch_method_handler(self):
        from vlastic.rom.resource import dispatch_method_handler as dispatch
        self.assertEquals(
            id(self.rsc.get_method_test),
            id(dispatch(self.rsc, "GET")[0])
        )
        self.assertEquals(
            id(test_view),
            id(dispatch(self.rsc, "GET")[1])
        )

    def test_root_get(self):
        self.assertEquals(
            {"message": "GET method test"},
            self.rsc.get_method_test()
        )
        self.assertEquals(
            "GET method test",
            self.rsc(Request("GET / HTTP/1.1\r\n\r\n")).body
        )

    def test_root_post(self):
        self.assertEquals(
            {"message": "root"},
            self.rsc.post_method_test()
        )
        self.assertEquals(
            "root",
            self.rsc(Request("POST / HTTP/1.1\r\n\r\n")).body
        )

    def test_root_put(self):
        self.assertEquals(
            "PUT: root /",
            self.rsc(Request("PUT / HTTP/1.1\r\n\r\n")).body
        )

    def test_root_custom(self):
        self.assertEquals(
            {"message": "CUSTOM method test"},
            self.rsc.custom_method_test()
        )
        self.assertEquals(
            "CUSTOM method test",
            self.rsc(Request("CUSTOM / HTTP/1.1\r\n\r\n")).body
        )

    def test_root_error(self):
        for method in ["DELETE", "UNDef"]:
            response = self.rsc(Request(method + " / HTTP/1.1\r\n\r\n"))
            self.assertTrue(
                isinstance(response, MethodNotAllowedError),
                "Resource should return MethodNotAllowedError when the method"
                " is unsupported."
            )


class TestObjectResource(ObjectResource):
    """A object resource class for unit tests."""

    def __init__(self, value):
        self.abc = TestResource("abc")
        self.value = value

    get_method_test = TestResource.get_method_test
    post_method_test = TestResource.post_method_test
    custom_method_test = TestResource.custom_method_test


class ObjectResourceTest(ResourceTest):
    """Unit tests for ObjectResource."""

    def setUp(self):
        self.rsc = TestObjectResource("root")

    def test_child(self):
        self.assertEquals(
            "GET method test",
            self.rsc(Request("GET /abc HTTP/1.1\r\n\r\n")).body
        )
        self.assertEquals(
            "abc",
            self.rsc(Request("POST /abc HTTP/1.1\r\n\r\n")).body
        )

    def test_404(self):
        res = self.rsc(Request("GET /no HTTP/1.1\r\n\r\n"))
        self.assertTrue(
            isinstance(res, NotFoundError),
            "Resource should return NotFoundError when there isn't the child."
        )


class TestDictionaryResource(DictionaryResource):
    """A simple class for unit testing DictionaryResource."""

    def __init__(self, name):
        self.name = name

    @get(test_view)
    def get_method_test(self):
        return {"message": "{0} can accept GET message.".format(self.name)}


class DictionaryResourceTest(unittest.TestCase):
    """Unit tests for DictionaryResource class with simple request
    specified by HTTP 0.9.

    """

    def setUp(self):
        self.root = TestDictionaryResource("Test Dictionary Resource")
        
    def test_root(self):
        self.assertEquals(
            "Test Dictionary Resource can accept GET message.",
            self.root(Request("GET / HTTP/1.1\r\n\r\n")).body
        )
        response = self.root(Request("POST / HTTP/1.1\r\n\r\n"))
        self.assertTrue(isinstance(response, MethodNotAllowedError))

    def test_child(self):
        self.root["abc"] = TestDictionaryResource("Test Child Resource")
        self.assertEquals(
            "Test Child Resource can accept GET message.",
            self.root(Request("GET /abc HTTP/1.1\r\n\r\n")).body
        )
        from functools import partial
        response = self.root(Request("GET /a HTTP/1.1\r\n\r\n"))
        self.assertTrue(isinstance(response, NotFoundError))
        response = partial(self.root.__getitem__, 1)
        self.assertRaises(KeyError, response)


class MethodDecoratorTest(unittest.TestCase):
    """Unit tests for MethodDecorator class and predefined method decorators
    e.g. get, post, put, delete.

    """

    def setUp(self):
        self.method = MethodDecorator("GET")

    def assert_handler(self, expected, resource, type):
        from vlastic.rom.resource import METHOD_HANDLER_ATTRIBUTE_NAME as name
        view = getattr(resource, name)["GET"]
        self.assertTrue(isinstance(view, NegotiativeView))
        self.assertEquals(id(expected), id(view[type]))

    def assert_default_view(self, expected, resource):
        from vlastic.rom.resource import METHOD_HANDLER_ATTRIBUTE_NAME as name
        view = getattr(resource, name)["GET"]
        self.assertTrue(isinstance(view, NegotiativeView))
        self.assertEquals(id(expected), id(view.default_view))

    def test_negotiation(self):
        decorator = self.method({
            "text/plain": test_view,
            "text/html": NegotiativeViewTest._html_view
        })
        resource = lambda: None
        decorator(resource)
        self.assert_handler(test_view, resource, "text/plain")
        self.assert_handler(
            NegotiativeViewTest._html_view,
            resource, "text/html"
        )

    def test_kwargs(self):
        decorator = self.method(
            {"image/png": test_view},
            html=NegotiativeViewTest._html_view,
            text=test_view
        )
        resource = lambda: None
        decorator(resource)
        self.assert_handler(
            NegotiativeViewTest._html_view,
            resource, "text/html"
        )
        self.assert_handler(
            NegotiativeViewTest._html_view,
            resource, "application/xhtml+xml"
        )
        self.assert_handler(test_view, resource, "text/plain")
        self.assert_handler(test_view, resource, "image/png")


    def test_kwargs_with_default(self):
        decorator = self.method(test_view, html=NegotiativeViewTest._html_view)
        resource = lambda: None
        decorator(resource)
        self.assert_handler(
            NegotiativeViewTest._html_view,
            resource, "text/html"
        )
        self.assert_handler(
            NegotiativeViewTest._html_view,
            resource, "application/xhtml+xml"
        )
        self.assert_default_view(test_view, resource)


class NegotiativeViewTest(unittest.TestCase):
    """Unit tests for NegotiativeView class."""

    @staticmethod
    def _html_view(context):
        return Response(
            ("HTTP/1.1", 200, "OK"),
            {"Content-Type": "text/html"},
            "<pre>{0[message]}</pre>".format(context)
        )

    @staticmethod
    def _json_view(context):
        import json
        return Response(
            ("HTTP/1.1", 200, "OK"),
            {"Content-Type": "application/json"},
            json.dumps(context)
        )

    def setUp(self):
        self.view = NegotiativeView({
            "text/plain": test_view,
            "text/html": self._html_view,
            "application/json": self._json_view
        })

    def assert_view(self, expected_body, mime_type, expected_mime_type=None):
        request = Request(
            ("GET", "/", "HTTP/1.1"),
            {"Accept": mime_type} if mime_type else {}
        )
        response = self.view({"message": "Content negotiation test"}, request)
        self.assertEquals(
            expected_mime_type or mime_type,
            response["Content-Type"]
        )
        self.assertEquals(expected_body, response.body)

    def test_negotiation(self):
        self.assert_view("Content negotiation test", "text/plain")
        self.assert_view("<pre>Content negotiation test</pre>", "text/html")
        self.assert_view(
            '{"message": "Content negotiation test"}',
            "application/json"
        )

    def test_negotiation_best_match(self):
        self.assert_view(
            '{"message": "Content negotiation test"}',
            "text/plain; q=0.5, application/json; q=1.0",
            "application/json"
        )

    def test_default(self):
        self.assert_view(
            "<pre>Content negotiation test</pre>",
            None, "text/html"
        )

    def test_not_acceptable(self):
        error = self.view(
            {},
            Request(("GET", "/", "HTTP/1.1"), {"Accept": "image/png"})
        )
        self.assertEquals(406, error.status_code)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.