daphne/channels/tests/test_sessions.py
Sachin Rekhi 363b5a09e9 improve @enforce_ordering to leverage a wait channel to avoid spinlocks (#144)
* improved @enforce_ordering to leverage a wait channel to avoid spinlocks

* addressed pyflake issues

* renamed wait channel to __wait__.<reply channel>

* handled potential ChannelFull exception

* updated sessions unit tests

* updated enforce_ordering tests to reflect new approach of leveraging wait channels

* addressed pyflake issues

* more pyflake fixes

* removed close_on_error handling on enforce_ordering since only worked on websockets
2016-05-12 10:38:06 -07:00

246 lines
7.8 KiB
Python

from __future__ import unicode_literals
from django.conf import settings
from django.test import override_settings
from channels.message import Message
from channels.sessions import channel_session, http_session, enforce_ordering, session_for_reply_channel
from channels.tests import ChannelTestCase
from channels import DEFAULT_CHANNEL_LAYER, channel_layers
@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_enforce_ordering_slight(self):
"""
Tests that slight mode of enforce_ordering works
"""
# Construct messages to send
message0 = Message(
{"reply_channel": "test-reply-a", "order": 0},
"websocket.connect",
channel_layers[DEFAULT_CHANNEL_LAYER]
)
message1 = Message(
{"reply_channel": "test-reply-a", "order": 1},
"websocket.receive",
channel_layers[DEFAULT_CHANNEL_LAYER]
)
message2 = Message(
{"reply_channel": "test-reply-a", "order": 2},
"websocket.receive",
channel_layers[DEFAULT_CHANNEL_LAYER]
)
# Run them in an acceptable slight order
@enforce_ordering(slight=True)
def inner(message):
pass
inner(message0)
inner(message2)
inner(message1)
# Ensure wait channel is empty
wait_channel = "__wait__.%s" % "test-reply-a"
next_message = self.get_next_message(wait_channel)
self.assertEqual(next_message, None)
def test_enforce_ordering_slight_fail(self):
"""
Tests that slight mode of enforce_ordering fails on bad ordering
"""
# Construct messages to send
message2 = Message(
{"reply_channel": "test-reply-e", "order": 2},
"websocket.receive",
channel_layers[DEFAULT_CHANNEL_LAYER]
)
# Run them in an acceptable strict order
@enforce_ordering(slight=True)
def inner(message):
pass
inner(message2)
# Ensure wait channel is not empty
wait_channel = "__wait__.%s" % "test-reply-e"
next_message = self.get_next_message(wait_channel)
self.assertNotEqual(next_message, None)
def test_enforce_ordering_strict(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_strict_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(slight=True)
def inner(message):
pass
with self.assertRaises(ValueError):
inner(message0)