PageRenderTime 76ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/django/contrib/sessions/tests.py

https://code.google.com/p/mango-py/
Python | 378 lines | 258 code | 78 blank | 42 comment | 5 complexity | ffb1e2395d9540fa5e9d9e72efba8409 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. import base64
  2. from datetime import datetime, timedelta
  3. import pickle
  4. import shutil
  5. import tempfile
  6. from django.conf import settings
  7. from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
  8. from django.contrib.sessions.backends.cache import SessionStore as CacheSession
  9. from django.contrib.sessions.backends.cached_db import SessionStore as CacheDBSession
  10. from django.contrib.sessions.backends.file import SessionStore as FileSession
  11. from django.contrib.sessions.models import Session
  12. from django.contrib.sessions.middleware import SessionMiddleware
  13. from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
  14. from django.http import HttpResponse
  15. from django.test import TestCase, RequestFactory
  16. from django.utils import unittest
  17. from django.utils.hashcompat import md5_constructor
  18. class SessionTestsMixin(object):
  19. # This does not inherit from TestCase to avoid any tests being run with this
  20. # class, which wouldn't work, and to allow different TestCase subclasses to
  21. # be used.
  22. backend = None # subclasses must specify
  23. def setUp(self):
  24. self.session = self.backend()
  25. def tearDown(self):
  26. # NB: be careful to delete any sessions created; stale sessions fill up
  27. # the /tmp (with some backends) and eventually overwhelm it after lots
  28. # of runs (think buildbots)
  29. self.session.delete()
  30. def test_new_session(self):
  31. self.assertFalse(self.session.modified)
  32. self.assertFalse(self.session.accessed)
  33. def test_get_empty(self):
  34. self.assertEqual(self.session.get('cat'), None)
  35. def test_store(self):
  36. self.session['cat'] = "dog"
  37. self.assertTrue(self.session.modified)
  38. self.assertEqual(self.session.pop('cat'), 'dog')
  39. def test_pop(self):
  40. self.session['some key'] = 'exists'
  41. # Need to reset these to pretend we haven't accessed it:
  42. self.accessed = False
  43. self.modified = False
  44. self.assertEqual(self.session.pop('some key'), 'exists')
  45. self.assertTrue(self.session.accessed)
  46. self.assertTrue(self.session.modified)
  47. self.assertEqual(self.session.get('some key'), None)
  48. def test_pop_default(self):
  49. self.assertEqual(self.session.pop('some key', 'does not exist'),
  50. 'does not exist')
  51. self.assertTrue(self.session.accessed)
  52. self.assertFalse(self.session.modified)
  53. def test_setdefault(self):
  54. self.assertEqual(self.session.setdefault('foo', 'bar'), 'bar')
  55. self.assertEqual(self.session.setdefault('foo', 'baz'), 'bar')
  56. self.assertTrue(self.session.accessed)
  57. self.assertTrue(self.session.modified)
  58. def test_update(self):
  59. self.session.update({'update key': 1})
  60. self.assertTrue(self.session.accessed)
  61. self.assertTrue(self.session.modified)
  62. self.assertEqual(self.session.get('update key', None), 1)
  63. def test_has_key(self):
  64. self.session['some key'] = 1
  65. self.session.modified = False
  66. self.session.accessed = False
  67. self.assertTrue(self.session.has_key('some key'))
  68. self.assertTrue(self.session.accessed)
  69. self.assertFalse(self.session.modified)
  70. def test_values(self):
  71. self.assertEqual(self.session.values(), [])
  72. self.assertTrue(self.session.accessed)
  73. self.session['some key'] = 1
  74. self.assertEqual(self.session.values(), [1])
  75. def test_iterkeys(self):
  76. self.session['x'] = 1
  77. self.session.modified = False
  78. self.session.accessed = False
  79. i = self.session.iterkeys()
  80. self.assertTrue(hasattr(i, '__iter__'))
  81. self.assertTrue(self.session.accessed)
  82. self.assertFalse(self.session.modified)
  83. self.assertEqual(list(i), ['x'])
  84. def test_itervalues(self):
  85. self.session['x'] = 1
  86. self.session.modified = False
  87. self.session.accessed = False
  88. i = self.session.itervalues()
  89. self.assertTrue(hasattr(i, '__iter__'))
  90. self.assertTrue(self.session.accessed)
  91. self.assertFalse(self.session.modified)
  92. self.assertEqual(list(i), [1])
  93. def test_iteritems(self):
  94. self.session['x'] = 1
  95. self.session.modified = False
  96. self.session.accessed = False
  97. i = self.session.iteritems()
  98. self.assertTrue(hasattr(i, '__iter__'))
  99. self.assertTrue(self.session.accessed)
  100. self.assertFalse(self.session.modified)
  101. self.assertEqual(list(i), [('x',1)])
  102. def test_clear(self):
  103. self.session['x'] = 1
  104. self.session.modified = False
  105. self.session.accessed = False
  106. self.assertEqual(self.session.items(), [('x',1)])
  107. self.session.clear()
  108. self.assertEqual(self.session.items(), [])
  109. self.assertTrue(self.session.accessed)
  110. self.assertTrue(self.session.modified)
  111. def test_save(self):
  112. self.session.save()
  113. self.assertTrue(self.session.exists(self.session.session_key))
  114. def test_delete(self):
  115. self.session.delete(self.session.session_key)
  116. self.assertFalse(self.session.exists(self.session.session_key))
  117. def test_flush(self):
  118. self.session['foo'] = 'bar'
  119. self.session.save()
  120. prev_key = self.session.session_key
  121. self.session.flush()
  122. self.assertFalse(self.session.exists(prev_key))
  123. self.assertNotEqual(self.session.session_key, prev_key)
  124. self.assertTrue(self.session.modified)
  125. self.assertTrue(self.session.accessed)
  126. def test_cycle(self):
  127. self.session['a'], self.session['b'] = 'c', 'd'
  128. self.session.save()
  129. prev_key = self.session.session_key
  130. prev_data = self.session.items()
  131. self.session.cycle_key()
  132. self.assertNotEqual(self.session.session_key, prev_key)
  133. self.assertEqual(self.session.items(), prev_data)
  134. def test_invalid_key(self):
  135. # Submitting an invalid session key (either by guessing, or if the db has
  136. # removed the key) results in a new key being generated.
  137. try:
  138. session = self.backend('1')
  139. session.save()
  140. self.assertNotEqual(session.session_key, '1')
  141. self.assertEqual(session.get('cat'), None)
  142. session.delete()
  143. finally:
  144. # Some backends leave a stale cache entry for the invalid
  145. # session key; make sure that entry is manually deleted
  146. session.delete('1')
  147. # Custom session expiry
  148. def test_default_expiry(self):
  149. # A normal session has a max age equal to settings
  150. self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
  151. # So does a custom session with an idle expiration time of 0 (but it'll
  152. # expire at browser close)
  153. self.session.set_expiry(0)
  154. self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
  155. def test_custom_expiry_seconds(self):
  156. # Using seconds
  157. self.session.set_expiry(10)
  158. delta = self.session.get_expiry_date() - datetime.now()
  159. self.assertTrue(delta.seconds in (9, 10))
  160. age = self.session.get_expiry_age()
  161. self.assertTrue(age in (9, 10))
  162. def test_custom_expiry_timedelta(self):
  163. # Using timedelta
  164. self.session.set_expiry(timedelta(seconds=10))
  165. delta = self.session.get_expiry_date() - datetime.now()
  166. self.assertTrue(delta.seconds in (9, 10))
  167. age = self.session.get_expiry_age()
  168. self.assertTrue(age in (9, 10))
  169. def test_custom_expiry_datetime(self):
  170. # Using fixed datetime
  171. self.session.set_expiry(datetime.now() + timedelta(seconds=10))
  172. delta = self.session.get_expiry_date() - datetime.now()
  173. self.assertTrue(delta.seconds in (9, 10))
  174. age = self.session.get_expiry_age()
  175. self.assertTrue(age in (9, 10))
  176. def test_custom_expiry_reset(self):
  177. self.session.set_expiry(None)
  178. self.session.set_expiry(10)
  179. self.session.set_expiry(None)
  180. self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
  181. def test_get_expire_at_browser_close(self):
  182. # Tests get_expire_at_browser_close with different settings and different
  183. # set_expiry calls
  184. try:
  185. try:
  186. original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
  187. settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
  188. self.session.set_expiry(10)
  189. self.assertFalse(self.session.get_expire_at_browser_close())
  190. self.session.set_expiry(0)
  191. self.assertTrue(self.session.get_expire_at_browser_close())
  192. self.session.set_expiry(None)
  193. self.assertFalse(self.session.get_expire_at_browser_close())
  194. settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
  195. self.session.set_expiry(10)
  196. self.assertFalse(self.session.get_expire_at_browser_close())
  197. self.session.set_expiry(0)
  198. self.assertTrue(self.session.get_expire_at_browser_close())
  199. self.session.set_expiry(None)
  200. self.assertTrue(self.session.get_expire_at_browser_close())
  201. except:
  202. raise
  203. finally:
  204. settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
  205. def test_decode(self):
  206. # Ensure we can decode what we encode
  207. data = {'a test key': 'a test value'}
  208. encoded = self.session.encode(data)
  209. self.assertEqual(self.session.decode(encoded), data)
  210. def test_decode_django12(self):
  211. # Ensure we can decode values encoded using Django 1.2
  212. # Hard code the Django 1.2 method here:
  213. def encode(session_dict):
  214. pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
  215. pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
  216. return base64.encodestring(pickled + pickled_md5)
  217. data = {'a test key': 'a test value'}
  218. encoded = encode(data)
  219. self.assertEqual(self.session.decode(encoded), data)
  220. class DatabaseSessionTests(SessionTestsMixin, TestCase):
  221. backend = DatabaseSession
  222. def test_session_get_decoded(self):
  223. """
  224. Test we can use Session.get_decoded to retrieve data stored
  225. in normal way
  226. """
  227. self.session['x'] = 1
  228. self.session.save()
  229. s = Session.objects.get(session_key=self.session.session_key)
  230. self.assertEqual(s.get_decoded(), {'x': 1})
  231. def test_sessionmanager_save(self):
  232. """
  233. Test SessionManager.save method
  234. """
  235. # Create a session
  236. self.session['y'] = 1
  237. self.session.save()
  238. s = Session.objects.get(session_key=self.session.session_key)
  239. # Change it
  240. Session.objects.save(s.session_key, {'y':2}, s.expire_date)
  241. # Clear cache, so that it will be retrieved from DB
  242. del self.session._session_cache
  243. self.assertEqual(self.session['y'], 2)
  244. class CacheDBSessionTests(SessionTestsMixin, TestCase):
  245. backend = CacheDBSession
  246. # Don't need DB flushing for these tests, so can use unittest.TestCase as base class
  247. class FileSessionTests(SessionTestsMixin, unittest.TestCase):
  248. backend = FileSession
  249. def setUp(self):
  250. super(FileSessionTests, self).setUp()
  251. # Do file session tests in an isolated directory, and kill it after we're done.
  252. self.original_session_file_path = settings.SESSION_FILE_PATH
  253. self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp()
  254. def tearDown(self):
  255. settings.SESSION_FILE_PATH = self.original_session_file_path
  256. shutil.rmtree(self.temp_session_store)
  257. super(FileSessionTests, self).tearDown()
  258. def test_configuration_check(self):
  259. # Make sure the file backend checks for a good storage dir
  260. settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer"
  261. self.assertRaises(ImproperlyConfigured, self.backend)
  262. def test_invalid_key_backslash(self):
  263. # Ensure we don't allow directory-traversal
  264. self.assertRaises(SuspiciousOperation,
  265. self.backend("a\\b\\c").load)
  266. def test_invalid_key_forwardslash(self):
  267. # Ensure we don't allow directory-traversal
  268. self.assertRaises(SuspiciousOperation,
  269. self.backend("a/b/c").load)
  270. class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
  271. backend = CacheSession
  272. class SessionMiddlewareTests(unittest.TestCase):
  273. def setUp(self):
  274. self.old_SESSION_COOKIE_SECURE = settings.SESSION_COOKIE_SECURE
  275. self.old_SESSION_COOKIE_HTTPONLY = settings.SESSION_COOKIE_HTTPONLY
  276. def tearDown(self):
  277. settings.SESSION_COOKIE_SECURE = self.old_SESSION_COOKIE_SECURE
  278. settings.SESSION_COOKIE_HTTPONLY = self.old_SESSION_COOKIE_HTTPONLY
  279. def test_secure_session_cookie(self):
  280. settings.SESSION_COOKIE_SECURE = True
  281. request = RequestFactory().get('/')
  282. response = HttpResponse('Session test')
  283. middleware = SessionMiddleware()
  284. # Simulate a request the modifies the session
  285. middleware.process_request(request)
  286. request.session['hello'] = 'world'
  287. # Handle the response through the middleware
  288. response = middleware.process_response(request, response)
  289. self.assertTrue(response.cookies[settings.SESSION_COOKIE_NAME]['secure'])
  290. def test_httponly_session_cookie(self):
  291. settings.SESSION_COOKIE_HTTPONLY = True
  292. request = RequestFactory().get('/')
  293. response = HttpResponse('Session test')
  294. middleware = SessionMiddleware()
  295. # Simulate a request the modifies the session
  296. middleware.process_request(request)
  297. request.session['hello'] = 'world'
  298. # Handle the response through the middleware
  299. response = middleware.process_response(request, response)
  300. self.assertTrue(response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])