App Engine Python SDK version 1.7.4 (2)
[gae.git] / python / lib / django_1_4 / django / contrib / sessions / tests.py
blob7686bd254eeb2085987d408f6d2cca6be1c8ab80
1 from __future__ import with_statement
3 from datetime import datetime, timedelta
4 import shutil
5 import string
6 import tempfile
7 import warnings
9 from django.conf import settings
10 from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
11 from django.contrib.sessions.backends.cache import SessionStore as CacheSession
12 from django.contrib.sessions.backends.cached_db import SessionStore as CacheDBSession
13 from django.contrib.sessions.backends.file import SessionStore as FileSession
14 from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
15 from django.contrib.sessions.models import Session
16 from django.contrib.sessions.middleware import SessionMiddleware
17 from django.core.cache.backends.base import CacheKeyWarning
18 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
19 from django.http import HttpResponse
20 from django.test import TestCase, RequestFactory
21 from django.test.utils import override_settings, get_warnings_state, restore_warnings_state
22 from django.utils import timezone
23 from django.utils import unittest
26 class SessionTestsMixin(object):
27 # This does not inherit from TestCase to avoid any tests being run with this
28 # class, which wouldn't work, and to allow different TestCase subclasses to
29 # be used.
31 backend = None # subclasses must specify
33 def setUp(self):
34 self.session = self.backend()
36 def tearDown(self):
37 # NB: be careful to delete any sessions created; stale sessions fill up
38 # the /tmp (with some backends) and eventually overwhelm it after lots
39 # of runs (think buildbots)
40 self.session.delete()
42 def test_new_session(self):
43 self.assertFalse(self.session.modified)
44 self.assertFalse(self.session.accessed)
46 def test_get_empty(self):
47 self.assertEqual(self.session.get('cat'), None)
49 def test_store(self):
50 self.session['cat'] = "dog"
51 self.assertTrue(self.session.modified)
52 self.assertEqual(self.session.pop('cat'), 'dog')
54 def test_pop(self):
55 self.session['some key'] = 'exists'
56 # Need to reset these to pretend we haven't accessed it:
57 self.accessed = False
58 self.modified = False
60 self.assertEqual(self.session.pop('some key'), 'exists')
61 self.assertTrue(self.session.accessed)
62 self.assertTrue(self.session.modified)
63 self.assertEqual(self.session.get('some key'), None)
65 def test_pop_default(self):
66 self.assertEqual(self.session.pop('some key', 'does not exist'),
67 'does not exist')
68 self.assertTrue(self.session.accessed)
69 self.assertFalse(self.session.modified)
71 def test_setdefault(self):
72 self.assertEqual(self.session.setdefault('foo', 'bar'), 'bar')
73 self.assertEqual(self.session.setdefault('foo', 'baz'), 'bar')
74 self.assertTrue(self.session.accessed)
75 self.assertTrue(self.session.modified)
77 def test_update(self):
78 self.session.update({'update key': 1})
79 self.assertTrue(self.session.accessed)
80 self.assertTrue(self.session.modified)
81 self.assertEqual(self.session.get('update key', None), 1)
83 def test_has_key(self):
84 self.session['some key'] = 1
85 self.session.modified = False
86 self.session.accessed = False
87 self.assertTrue('some key' in self.session)
88 self.assertTrue(self.session.accessed)
89 self.assertFalse(self.session.modified)
91 def test_values(self):
92 self.assertEqual(self.session.values(), [])
93 self.assertTrue(self.session.accessed)
94 self.session['some key'] = 1
95 self.assertEqual(self.session.values(), [1])
97 def test_iterkeys(self):
98 self.session['x'] = 1
99 self.session.modified = False
100 self.session.accessed = False
101 i = self.session.iterkeys()
102 self.assertTrue(hasattr(i, '__iter__'))
103 self.assertTrue(self.session.accessed)
104 self.assertFalse(self.session.modified)
105 self.assertEqual(list(i), ['x'])
107 def test_itervalues(self):
108 self.session['x'] = 1
109 self.session.modified = False
110 self.session.accessed = False
111 i = self.session.itervalues()
112 self.assertTrue(hasattr(i, '__iter__'))
113 self.assertTrue(self.session.accessed)
114 self.assertFalse(self.session.modified)
115 self.assertEqual(list(i), [1])
117 def test_iteritems(self):
118 self.session['x'] = 1
119 self.session.modified = False
120 self.session.accessed = False
121 i = self.session.iteritems()
122 self.assertTrue(hasattr(i, '__iter__'))
123 self.assertTrue(self.session.accessed)
124 self.assertFalse(self.session.modified)
125 self.assertEqual(list(i), [('x', 1)])
127 def test_clear(self):
128 self.session['x'] = 1
129 self.session.modified = False
130 self.session.accessed = False
131 self.assertEqual(self.session.items(), [('x', 1)])
132 self.session.clear()
133 self.assertEqual(self.session.items(), [])
134 self.assertTrue(self.session.accessed)
135 self.assertTrue(self.session.modified)
137 def test_save(self):
138 self.session.save()
139 self.assertTrue(self.session.exists(self.session.session_key))
141 def test_delete(self):
142 self.session.save()
143 self.session.delete(self.session.session_key)
144 self.assertFalse(self.session.exists(self.session.session_key))
146 def test_flush(self):
147 self.session['foo'] = 'bar'
148 self.session.save()
149 prev_key = self.session.session_key
150 self.session.flush()
151 self.assertFalse(self.session.exists(prev_key))
152 self.assertNotEqual(self.session.session_key, prev_key)
153 self.assertTrue(self.session.modified)
154 self.assertTrue(self.session.accessed)
156 def test_cycle(self):
157 self.session['a'], self.session['b'] = 'c', 'd'
158 self.session.save()
159 prev_key = self.session.session_key
160 prev_data = self.session.items()
161 self.session.cycle_key()
162 self.assertNotEqual(self.session.session_key, prev_key)
163 self.assertEqual(self.session.items(), prev_data)
165 def test_invalid_key(self):
166 # Submitting an invalid session key (either by guessing, or if the db has
167 # removed the key) results in a new key being generated.
168 try:
169 session = self.backend('1')
170 try:
171 session.save()
172 except AttributeError:
173 self.fail("The session object did not save properly. Middleware may be saving cache items without namespaces.")
174 self.assertNotEqual(session.session_key, '1')
175 self.assertEqual(session.get('cat'), None)
176 session.delete()
177 finally:
178 # Some backends leave a stale cache entry for the invalid
179 # session key; make sure that entry is manually deleted
180 session.delete('1')
182 def test_session_key_is_read_only(self):
183 def set_session_key(session):
184 session.session_key = session._get_new_session_key()
185 self.assertRaises(AttributeError, set_session_key, self.session)
187 # Custom session expiry
188 def test_default_expiry(self):
189 # A normal session has a max age equal to settings
190 self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
192 # So does a custom session with an idle expiration time of 0 (but it'll
193 # expire at browser close)
194 self.session.set_expiry(0)
195 self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
197 def test_custom_expiry_seconds(self):
198 # Using seconds
199 self.session.set_expiry(10)
200 delta = self.session.get_expiry_date() - timezone.now()
201 self.assertTrue(delta.seconds in (9, 10))
203 age = self.session.get_expiry_age()
204 self.assertTrue(age in (9, 10))
206 def test_custom_expiry_timedelta(self):
207 # Using timedelta
208 self.session.set_expiry(timedelta(seconds=10))
209 delta = self.session.get_expiry_date() - timezone.now()
210 self.assertTrue(delta.seconds in (9, 10))
212 age = self.session.get_expiry_age()
213 self.assertTrue(age in (9, 10))
215 def test_custom_expiry_datetime(self):
216 # Using fixed datetime
217 self.session.set_expiry(timezone.now() + timedelta(seconds=10))
218 delta = self.session.get_expiry_date() - timezone.now()
219 self.assertTrue(delta.seconds in (9, 10))
221 age = self.session.get_expiry_age()
222 self.assertTrue(age in (9, 10))
224 def test_custom_expiry_reset(self):
225 self.session.set_expiry(None)
226 self.session.set_expiry(10)
227 self.session.set_expiry(None)
228 self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
230 def test_get_expire_at_browser_close(self):
231 # Tests get_expire_at_browser_close with different settings and different
232 # set_expiry calls
233 with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=False):
234 self.session.set_expiry(10)
235 self.assertFalse(self.session.get_expire_at_browser_close())
237 self.session.set_expiry(0)
238 self.assertTrue(self.session.get_expire_at_browser_close())
240 self.session.set_expiry(None)
241 self.assertFalse(self.session.get_expire_at_browser_close())
243 with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=True):
244 self.session.set_expiry(10)
245 self.assertFalse(self.session.get_expire_at_browser_close())
247 self.session.set_expiry(0)
248 self.assertTrue(self.session.get_expire_at_browser_close())
250 self.session.set_expiry(None)
251 self.assertTrue(self.session.get_expire_at_browser_close())
253 def test_decode(self):
254 # Ensure we can decode what we encode
255 data = {'a test key': 'a test value'}
256 encoded = self.session.encode(data)
257 self.assertEqual(self.session.decode(encoded), data)
260 class DatabaseSessionTests(SessionTestsMixin, TestCase):
262 backend = DatabaseSession
264 def test_session_get_decoded(self):
266 Test we can use Session.get_decoded to retrieve data stored
267 in normal way
269 self.session['x'] = 1
270 self.session.save()
272 s = Session.objects.get(session_key=self.session.session_key)
274 self.assertEqual(s.get_decoded(), {'x': 1})
276 def test_sessionmanager_save(self):
278 Test SessionManager.save method
280 # Create a session
281 self.session['y'] = 1
282 self.session.save()
284 s = Session.objects.get(session_key=self.session.session_key)
285 # Change it
286 Session.objects.save(s.session_key, {'y': 2}, s.expire_date)
287 # Clear cache, so that it will be retrieved from DB
288 del self.session._session_cache
289 self.assertEqual(self.session['y'], 2)
292 DatabaseSessionWithTimeZoneTests = override_settings(USE_TZ=True)(DatabaseSessionTests)
295 class CacheDBSessionTests(SessionTestsMixin, TestCase):
297 backend = CacheDBSession
299 def test_exists_searches_cache_first(self):
300 self.session.save()
301 with self.assertNumQueries(0):
302 self.assertTrue(self.session.exists(self.session.session_key))
304 def test_load_overlong_key(self):
305 warnings_state = get_warnings_state()
306 warnings.filterwarnings('ignore',
307 category=CacheKeyWarning)
308 self.session._session_key = (string.ascii_letters + string.digits) * 20
309 self.assertEqual(self.session.load(), {})
310 restore_warnings_state(warnings_state)
313 CacheDBSessionWithTimeZoneTests = override_settings(USE_TZ=True)(CacheDBSessionTests)
316 # Don't need DB flushing for these tests, so can use unittest.TestCase as base class
317 class FileSessionTests(SessionTestsMixin, unittest.TestCase):
319 backend = FileSession
321 def setUp(self):
322 super(FileSessionTests, self).setUp()
323 # Do file session tests in an isolated directory, and kill it after we're done.
324 self.original_session_file_path = settings.SESSION_FILE_PATH
325 self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp()
327 def tearDown(self):
328 settings.SESSION_FILE_PATH = self.original_session_file_path
329 shutil.rmtree(self.temp_session_store)
330 super(FileSessionTests, self).tearDown()
332 @override_settings(
333 SESSION_FILE_PATH="/if/this/directory/exists/you/have/a/weird/computer")
334 def test_configuration_check(self):
335 # Make sure the file backend checks for a good storage dir
336 self.assertRaises(ImproperlyConfigured, self.backend)
338 def test_invalid_key_backslash(self):
339 # Ensure we don't allow directory-traversal
340 self.assertRaises(SuspiciousOperation,
341 self.backend("a\\b\\c").load)
343 def test_invalid_key_forwardslash(self):
344 # Ensure we don't allow directory-traversal
345 self.assertRaises(SuspiciousOperation,
346 self.backend("a/b/c").load)
349 class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
351 backend = CacheSession
353 def test_load_overlong_key(self):
354 warnings_state = get_warnings_state()
355 warnings.filterwarnings('ignore',
356 category=CacheKeyWarning)
357 self.session._session_key = (string.ascii_letters + string.digits) * 20
358 self.assertEqual(self.session.load(), {})
359 restore_warnings_state(warnings_state)
362 class SessionMiddlewareTests(unittest.TestCase):
364 @override_settings(SESSION_COOKIE_SECURE=True)
365 def test_secure_session_cookie(self):
366 request = RequestFactory().get('/')
367 response = HttpResponse('Session test')
368 middleware = SessionMiddleware()
370 # Simulate a request the modifies the session
371 middleware.process_request(request)
372 request.session['hello'] = 'world'
374 # Handle the response through the middleware
375 response = middleware.process_response(request, response)
376 self.assertTrue(
377 response.cookies[settings.SESSION_COOKIE_NAME]['secure'])
379 @override_settings(SESSION_COOKIE_HTTPONLY=True)
380 def test_httponly_session_cookie(self):
381 request = RequestFactory().get('/')
382 response = HttpResponse('Session test')
383 middleware = SessionMiddleware()
385 # Simulate a request the modifies the session
386 middleware.process_request(request)
387 request.session['hello'] = 'world'
389 # Handle the response through the middleware
390 response = middleware.process_response(request, response)
391 self.assertTrue(
392 response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])
393 self.assertIn('httponly',
394 str(response.cookies[settings.SESSION_COOKIE_NAME]))
396 @override_settings(SESSION_COOKIE_HTTPONLY=False)
397 def test_no_httponly_session_cookie(self):
398 request = RequestFactory().get('/')
399 response = HttpResponse('Session test')
400 middleware = SessionMiddleware()
402 # Simulate a request the modifies the session
403 middleware.process_request(request)
404 request.session['hello'] = 'world'
406 # Handle the response through the middleware
407 response = middleware.process_response(request, response)
408 # If it isn't in the cookie, that's fine (Python 2.5)
409 if 'httponly' in settings.SESSION_COOKIE_NAME:
410 self.assertFalse(
411 response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])
413 self.assertNotIn('httponly',
414 str(response.cookies[settings.SESSION_COOKIE_NAME]))
417 class CookieSessionTests(SessionTestsMixin, TestCase):
419 backend = CookieSession
421 def test_save(self):
423 This test tested exists() in the other session backends, but that
424 doesn't make sense for us.
426 pass
428 def test_cycle(self):
430 This test tested cycle_key() which would create a new session
431 key for the same session data. But we can't invalidate previously
432 signed cookies (other than letting them expire naturally) so
433 testing for this behavior is meaningless.
435 pass