from __future__ import unicode_literals from django.test import SimpleTestCase from channels.generic import BaseConsumer from channels.message import Message from channels.routing import Router, include, route, route_class from channels.utils import name_that_thing # Fake consumers and routing sets that can be imported by string def consumer_1(): pass def consumer_2(): pass def consumer_3(): pass class TestClassConsumer(BaseConsumer): method_mapping = { "test.channel": "some_method", } def some_method(self, message, **kwargs): pass chatroom_routing = [ route("websocket.connect", consumer_2, path=r"^/chat/(?P[^/]+)/$"), route("websocket.connect", consumer_3, path=r"^/mentions/$"), ] chatroom_routing_nolinestart = [ route("websocket.connect", consumer_2, path=r"/chat/(?P[^/]+)/$"), route("websocket.connect", consumer_3, path=r"/mentions/$"), ] class_routing = [ route_class(TestClassConsumer, path=r"^/foobar/$"), ] class RoutingTests(SimpleTestCase): """ Tests that the router's routing code works correctly. """ def assertRoute(self, router, channel, content, consumer, kwargs=None): """ Asserts that asking the `router` to route the `content` as a message from `channel` means it returns consumer `consumer`, optionally testing it also returns `kwargs` to be passed in Use `consumer` = None to assert that no route is found. """ message = Message(content, channel, channel_layer="fake channel layer") match = router.match(message) if match is None: if consumer is None: return else: self.fail("No route found for %s on %s; expecting %s" % ( content, channel, name_that_thing(consumer), )) else: mconsumer, mkwargs = match if consumer is None: self.fail("Route found for %s on %s; expecting no route." % ( content, channel, )) self.assertEqual(consumer, mconsumer, "Route found for %s on %s; but wrong consumer (%s not %s)." % ( content, channel, name_that_thing(mconsumer), name_that_thing(consumer), )) if kwargs is not None: self.assertEqual(kwargs, mkwargs, "Route found for %s on %s; but wrong kwargs (%s not %s)." % ( content, channel, mkwargs, kwargs, )) def test_assumption(self): """ Ensures the test consumers don't compare equal, as if this ever happens this test file will pass and miss most bugs. """ self.assertEqual(consumer_1, consumer_1) self.assertNotEqual(consumer_1, consumer_2) self.assertNotEqual(consumer_1, consumer_3) def test_dict(self): """ Tests dict expansion """ router = Router({ "http.request": consumer_1, "http.disconnect": consumer_2, }) self.assertRoute( router, channel="http.request", content={}, consumer=consumer_1, kwargs={}, ) self.assertRoute( router, channel="http.request", content={"path": "/chat/"}, consumer=consumer_1, kwargs={}, ) self.assertRoute( router, channel="http.disconnect", content={}, consumer=consumer_2, kwargs={}, ) def test_filters(self): """ Tests that filters catch things correctly. """ router = Router([ route("http.request", consumer_1, path=r"^/chat/$"), route("http.disconnect", consumer_2), route("http.request", consumer_3), ]) # Filter hit self.assertRoute( router, channel="http.request", content={"path": "/chat/"}, consumer=consumer_1, kwargs={}, ) # Fall-through self.assertRoute( router, channel="http.request", content={}, consumer=consumer_3, kwargs={}, ) self.assertRoute( router, channel="http.request", content={"path": "/liveblog/"}, consumer=consumer_3, kwargs={}, ) def test_include(self): """ Tests inclusion without a prefix """ router = Router([ include("channels.tests.test_routing.chatroom_routing"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": "/boom/"}, consumer=None, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/chat/django/"}, consumer=consumer_2, kwargs={"room": "django"}, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/mentions/"}, consumer=consumer_3, kwargs={}, ) def test_route_class(self): """ Tests route_class with/without prefix """ router = Router([ include("channels.tests.test_routing.class_routing"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": "/foobar/"}, consumer=None, ) self.assertRoute( router, channel="test.channel", content={"path": "/foobar/"}, consumer=TestClassConsumer, ) self.assertRoute( router, channel="test.channel", content={"path": "/"}, consumer=None, ) def test_include_prefix(self): """ Tests inclusion with a prefix """ router = Router([ include("channels.tests.test_routing.chatroom_routing", path="^/ws/v(?P[0-9]+)"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": "/boom/"}, consumer=None, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/chat/django/"}, consumer=None, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/ws/v2/chat/django/"}, consumer=consumer_2, kwargs={"version": "2", "room": "django"}, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/ws/v1/mentions/"}, consumer=consumer_3, kwargs={"version": "1"}, ) # Check it works without the ^s too. router = Router([ include("channels.tests.test_routing.chatroom_routing_nolinestart", path="/ws/v(?P[0-9]+)"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": "/ws/v2/chat/django/"}, consumer=consumer_2, kwargs={"version": "2", "room": "django"}, ) def test_positional_pattern(self): """ Tests that regexes with positional groups are rejected. """ with self.assertRaises(ValueError): Router([ route("http.request", consumer_1, path=r"^/chat/([^/]+)/$"), ]) def test_mixed_unicode_bytes(self): """ Tests that having the message key be bytes and pattern unicode (or vice-versa) still works. """ # Unicode patterns, byte message router = Router([ route("websocket.connect", consumer_1, path="^/foo/"), include("channels.tests.test_routing.chatroom_routing", path="^/ws/v(?P[0-9]+)"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": b"/boom/"}, consumer=None, ) self.assertRoute( router, channel="websocket.connect", content={"path": b"/foo/"}, consumer=consumer_1, ) self.assertRoute( router, channel="websocket.connect", content={"path": b"/ws/v2/chat/django/"}, consumer=consumer_2, kwargs={"version": "2", "room": "django"}, ) # Byte patterns, unicode message router = Router([ route("websocket.connect", consumer_1, path=b"^/foo/"), include("channels.tests.test_routing.chatroom_routing", path=b"^/ws/v(?P[0-9]+)"), ]) self.assertRoute( router, channel="websocket.connect", content={"path": "/boom/"}, consumer=None, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/foo/"}, consumer=consumer_1, ) self.assertRoute( router, channel="websocket.connect", content={"path": "/ws/v2/chat/django/"}, consumer=consumer_2, kwargs={"version": "2", "room": "django"}, ) def test_channels(self): """ Tests that the router reports channels to listen on correctly """ router = Router([ route("http.request", consumer_1, path=r"^/chat/$"), route("http.disconnect", consumer_2), route("http.request", consumer_3), route_class(TestClassConsumer), ]) # Initial check self.assertEqual( router.channels, {"http.request", "http.disconnect", "test.channel"}, ) # Dynamically add route, recheck router.add_route(route("websocket.receive", consumer_1)) self.assertEqual( router.channels, {"http.request", "http.disconnect", "websocket.receive", "test.channel"}, )