from __future__ import unicode_literals from django.conf import settings from django.test import override_settings from channels import DEFAULT_CHANNEL_LAYER, channel_layers from channels.message import Message from channels.sessions import ( channel_and_http_session, channel_session, enforce_ordering, http_session, session_for_reply_channel, ) from channels.tests import ChannelTestCase @override_settings(SESSION_ENGINE="django.contrib.sessions.backends.cache") class SessionTests(ChannelTestCase): """ Tests the channels session module. """ def test_session_for_reply_channel(self): """ Tests storing and retrieving values by reply_channel. """ session1 = session_for_reply_channel("test-reply-channel") session1["testvalue"] = 42 session1.save(must_create=True) session2 = session_for_reply_channel("test-reply-channel") self.assertEqual(session2["testvalue"], 42) def test_channel_session(self): """ Tests the channel_session decorator """ # Construct message to send message = Message({"reply_channel": "test-reply"}, None, None) # Run through a simple fake consumer that assigns to it @channel_session def inner(message): message.channel_session["num_ponies"] = -1 inner(message) # Test the session worked session2 = session_for_reply_channel("test-reply") self.assertEqual(session2["num_ponies"], -1) def test_channel_session_double(self): """ Tests the channel_session decorator detects being wrapped in itself and doesn't blow up. """ # Construct message to send message = Message({"reply_channel": "test-reply"}, None, None) # Run through a simple fake consumer that should trigger the error @channel_session @channel_session def inner(message): message.channel_session["num_ponies"] = -1 inner(message) # Test the session worked session2 = session_for_reply_channel("test-reply") self.assertEqual(session2["num_ponies"], -1) def test_channel_session_no_reply(self): """ Tests the channel_session decorator detects no reply channel """ # Construct message to send message = Message({}, None, None) # Run through a simple fake consumer that should trigger the error @channel_session @channel_session def inner(message): message.channel_session["num_ponies"] = -1 with self.assertRaises(ValueError): inner(message) def test_http_session(self): """ Tests that http_session correctly extracts a session cookie. """ # Make a session to try against session1 = session_for_reply_channel("test-reply") # Construct message to send message = Message({ "reply_channel": "test-reply", "http_version": "1.1", "method": "GET", "path": "/test2/", "headers": { "host": b"example.com", "cookie": ("%s=%s" % (settings.SESSION_COOKIE_NAME, session1.session_key)).encode("ascii"), }, }, None, None) # Run it through http_session, make sure it works (test double here too) @http_session @http_session def inner(message): message.http_session["species"] = "horse" inner(message) # Check value assignment stuck session2 = session_for_reply_channel("test-reply") self.assertEqual(session2["species"], "horse") def test_channel_and_http_session(self): """ Tests that channel_and_http_session decorator stores the http session key and hydrates it when expected """ # Make a session to try against session = session_for_reply_channel("test-reply-session") # Construct message to send message = Message({ "reply_channel": "test-reply-session", "http_version": "1.1", "method": "GET", "path": "/test2/", "headers": { "host": b"example.com", "cookie": ("%s=%s" % (settings.SESSION_COOKIE_NAME, session.session_key)).encode("ascii"), }, }, None, None) @channel_and_http_session def inner(message): pass inner(message) # It should store the session key self.assertEqual(message.channel_session[settings.SESSION_COOKIE_NAME], session.session_key) # Construct a new message message2 = Message({"reply_channel": "test-reply-session", "path": "/"}, None, None) inner(message2) # It should hydrate the http_session self.assertEqual(message2.http_session.session_key, session.session_key) def test_enforce_ordering(self): """ Tests that strict mode of enforce_ordering works """ # Construct messages to send message0 = Message( {"reply_channel": "test-reply-b", "order": 0}, "websocket.connect", channel_layers[DEFAULT_CHANNEL_LAYER] ) message1 = Message( {"reply_channel": "test-reply-b", "order": 1}, "websocket.receive", channel_layers[DEFAULT_CHANNEL_LAYER] ) message2 = Message( {"reply_channel": "test-reply-b", "order": 2}, "websocket.receive", channel_layers[DEFAULT_CHANNEL_LAYER] ) # Run them in an acceptable strict order @enforce_ordering def inner(message): pass inner(message0) inner(message1) inner(message2) # Ensure wait channel is empty wait_channel = "__wait__.%s" % "test-reply-b" next_message = self.get_next_message(wait_channel) self.assertEqual(next_message, None) def test_enforce_ordering_fail(self): """ Tests that strict mode of enforce_ordering fails on bad ordering """ # Construct messages to send message0 = Message( {"reply_channel": "test-reply-c", "order": 0}, "websocket.connect", channel_layers[DEFAULT_CHANNEL_LAYER] ) message2 = Message( {"reply_channel": "test-reply-c", "order": 2}, "websocket.receive", channel_layers[DEFAULT_CHANNEL_LAYER] ) # Run them in an acceptable strict order @enforce_ordering def inner(message): pass inner(message0) inner(message2) # Ensure wait channel is not empty wait_channel = "__wait__.%s" % "test-reply-c" next_message = self.get_next_message(wait_channel) self.assertNotEqual(next_message, None) def test_enforce_ordering_fail_no_order(self): """ Makes sure messages with no "order" key fail """ message0 = Message( {"reply_channel": "test-reply-d"}, None, channel_layers[DEFAULT_CHANNEL_LAYER] ) @enforce_ordering def inner(message): pass with self.assertRaises(ValueError): inner(message0)