PageRenderTime 166ms CodeModel.GetById 75ms app.highlight 57ms RepoModel.GetById 29ms app.codeStats 0ms

/django/contrib/sessions/tests.py

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