14
13
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
15
14
from django.contrib.sessions.models import Session
16
15
from django.contrib.sessions.middleware import SessionMiddleware
17
from django.core.cache.backends.base import CacheKeyWarning
16
from django.core.cache import get_cache
17
from django.core import management
18
18
from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
19
19
from django.http import HttpResponse
20
20
from django.test import TestCase, RequestFactory
21
from django.test.utils import override_settings, get_warnings_state, restore_warnings_state
21
from django.test.utils import override_settings
22
from django.utils import six
22
23
from django.utils import timezone
23
24
from django.utils import unittest
84
85
self.session['some key'] = 1
85
86
self.session.modified = False
86
87
self.session.accessed = False
87
self.assertTrue('some key' in self.session)
88
self.assertIn('some key', self.session)
88
89
self.assertTrue(self.session.accessed)
89
90
self.assertFalse(self.session.modified)
91
92
def test_values(self):
92
self.assertEqual(self.session.values(), [])
93
self.assertEqual(list(self.session.values()), [])
93
94
self.assertTrue(self.session.accessed)
94
95
self.session['some key'] = 1
95
self.assertEqual(self.session.values(), [1])
96
self.assertEqual(list(self.session.values()), [1])
97
98
def test_iterkeys(self):
98
99
self.session['x'] = 1
99
100
self.session.modified = False
100
101
self.session.accessed = False
101
i = self.session.iterkeys()
102
i = six.iterkeys(self.session)
102
103
self.assertTrue(hasattr(i, '__iter__'))
103
104
self.assertTrue(self.session.accessed)
104
105
self.assertFalse(self.session.modified)
108
109
self.session['x'] = 1
109
110
self.session.modified = False
110
111
self.session.accessed = False
111
i = self.session.itervalues()
112
i = six.itervalues(self.session)
112
113
self.assertTrue(hasattr(i, '__iter__'))
113
114
self.assertTrue(self.session.accessed)
114
115
self.assertFalse(self.session.modified)
118
119
self.session['x'] = 1
119
120
self.session.modified = False
120
121
self.session.accessed = False
121
i = self.session.iteritems()
122
i = six.iteritems(self.session)
122
123
self.assertTrue(hasattr(i, '__iter__'))
123
124
self.assertTrue(self.session.accessed)
124
125
self.assertFalse(self.session.modified)
128
129
self.session['x'] = 1
129
130
self.session.modified = False
130
131
self.session.accessed = False
131
self.assertEqual(self.session.items(), [('x', 1)])
132
self.assertEqual(list(self.session.items()), [('x', 1)])
132
133
self.session.clear()
133
self.assertEqual(self.session.items(), [])
134
self.assertEqual(list(self.session.items()), [])
134
135
self.assertTrue(self.session.accessed)
135
136
self.assertTrue(self.session.modified)
137
138
def test_save(self):
139
if (hasattr(self.session, '_cache') and'DummyCache' in
140
settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND']):
141
raise unittest.SkipTest("Session saving tests require a real cache backend")
138
142
self.session.save()
139
143
self.assertTrue(self.session.exists(self.session.session_key))
157
161
self.session['a'], self.session['b'] = 'c', 'd'
158
162
self.session.save()
159
163
prev_key = self.session.session_key
160
prev_data = self.session.items()
164
prev_data = list(self.session.items())
161
165
self.session.cycle_key()
162
166
self.assertNotEqual(self.session.session_key, prev_key)
163
self.assertEqual(self.session.items(), prev_data)
167
self.assertEqual(list(self.session.items()), prev_data)
165
169
def test_invalid_key(self):
166
170
# Submitting an invalid session key (either by guessing, or if the db has
195
199
self.assertEqual(self.session.get_expiry_age(), settings.SESSION_COOKIE_AGE)
197
201
def test_custom_expiry_seconds(self):
202
modification = timezone.now()
199
204
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
date = self.session.get_expiry_date(modification=modification)
207
self.assertEqual(date, modification + timedelta(seconds=10))
209
age = self.session.get_expiry_age(modification=modification)
210
self.assertEqual(age, 10)
206
212
def test_custom_expiry_timedelta(self):
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))
213
modification = timezone.now()
215
# Mock timezone.now, because set_expiry calls it on this code path.
216
original_now = timezone.now
218
timezone.now = lambda: modification
219
self.session.set_expiry(timedelta(seconds=10))
221
timezone.now = original_now
223
date = self.session.get_expiry_date(modification=modification)
224
self.assertEqual(date, modification + timedelta(seconds=10))
226
age = self.session.get_expiry_age(modification=modification)
227
self.assertEqual(age, 10)
215
229
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))
230
modification = timezone.now()
232
self.session.set_expiry(modification + timedelta(seconds=10))
234
date = self.session.get_expiry_date(modification=modification)
235
self.assertEqual(date, modification + timedelta(seconds=10))
237
age = self.session.get_expiry_age(modification=modification)
238
self.assertEqual(age, 10)
224
240
def test_custom_expiry_reset(self):
225
241
self.session.set_expiry(None)
256
272
encoded = self.session.encode(data)
257
273
self.assertEqual(self.session.decode(encoded), data)
275
def test_actual_expiry(self):
276
# Regression test for #19200
277
old_session_key = None
278
new_session_key = None
280
self.session['foo'] = 'bar'
281
self.session.set_expiry(-timedelta(seconds=10))
283
old_session_key = self.session.session_key
284
# With an expiry date in the past, the session expires instantly.
285
new_session = self.backend(self.session.session_key)
286
new_session_key = new_session.session_key
287
self.assertNotIn('foo', new_session)
289
self.session.delete(old_session_key)
290
self.session.delete(new_session_key)
260
293
class DatabaseSessionTests(SessionTestsMixin, TestCase):
288
321
del self.session._session_cache
289
322
self.assertEqual(self.session['y'], 2)
292
DatabaseSessionWithTimeZoneTests = override_settings(USE_TZ=True)(DatabaseSessionTests)
324
@override_settings(SESSION_ENGINE="django.contrib.sessions.backends.db")
325
def test_clearsessions_command(self):
327
Test clearsessions command for clearing expired sessions.
329
self.assertEqual(0, Session.objects.count())
331
# One object in the future
332
self.session['foo'] = 'bar'
333
self.session.set_expiry(3600)
336
# One object in the past
337
other_session = self.backend()
338
other_session['foo'] = 'bar'
339
other_session.set_expiry(-3600)
342
# Two sessions are in the database before clearsessions...
343
self.assertEqual(2, Session.objects.count())
344
management.call_command('clearsessions')
345
# ... and one is deleted.
346
self.assertEqual(1, Session.objects.count())
349
@override_settings(USE_TZ=True)
350
class DatabaseSessionWithTimeZoneTests(DatabaseSessionTests):
295
354
class CacheDBSessionTests(SessionTestsMixin, TestCase):
297
356
backend = CacheDBSession
358
@unittest.skipIf('DummyCache' in
359
settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND'],
360
"Session saving tests require a real cache backend")
299
361
def test_exists_searches_cache_first(self):
300
362
self.session.save()
301
363
with self.assertNumQueries(0):
302
364
self.assertTrue(self.session.exists(self.session.session_key))
304
366
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)
367
# Some backends might issue a warning
368
with warnings.catch_warnings():
369
warnings.simplefilter("ignore")
370
self.session._session_key = (string.ascii_letters + string.digits) * 20
371
self.assertEqual(self.session.load(), {})
374
@override_settings(USE_TZ=True)
375
class CacheDBSessionWithTimeZoneTests(CacheDBSessionTests):
316
379
# Don't need DB flushing for these tests, so can use unittest.TestCase as base class
319
382
backend = FileSession
322
super(FileSessionTests, self).setUp()
323
385
# Do file session tests in an isolated directory, and kill it after we're done.
324
386
self.original_session_file_path = settings.SESSION_FILE_PATH
325
387
self.temp_session_store = settings.SESSION_FILE_PATH = tempfile.mkdtemp()
388
# Reset the file session backend's internal caches
389
if hasattr(self.backend, '_storage_path'):
390
del self.backend._storage_path
391
super(FileSessionTests, self).setUp()
327
393
def tearDown(self):
394
super(FileSessionTests, self).tearDown()
328
395
settings.SESSION_FILE_PATH = self.original_session_file_path
329
396
shutil.rmtree(self.temp_session_store)
330
super(FileSessionTests, self).tearDown()
332
398
@override_settings(
333
399
SESSION_FILE_PATH="/if/this/directory/exists/you/have/a/weird/computer")
334
400
def test_configuration_check(self):
401
del self.backend._storage_path
335
402
# Make sure the file backend checks for a good storage dir
336
403
self.assertRaises(ImproperlyConfigured, self.backend)
345
412
self.assertRaises(SuspiciousOperation,
346
413
self.backend("a/b/c").load)
415
@override_settings(SESSION_ENGINE="django.contrib.sessions.backends.file")
416
def test_clearsessions_command(self):
418
Test clearsessions command for clearing expired sessions.
420
storage_path = self.backend._get_storage_path()
421
file_prefix = settings.SESSION_COOKIE_NAME
423
def count_sessions():
424
return len([session_file for session_file in os.listdir(storage_path)
425
if session_file.startswith(file_prefix)])
427
self.assertEqual(0, count_sessions())
429
# One object in the future
430
self.session['foo'] = 'bar'
431
self.session.set_expiry(3600)
434
# One object in the past
435
other_session = self.backend()
436
other_session['foo'] = 'bar'
437
other_session.set_expiry(-3600)
440
# Two sessions are in the filesystem before clearsessions...
441
self.assertEqual(2, count_sessions())
442
management.call_command('clearsessions')
443
# ... and one is deleted.
444
self.assertEqual(1, count_sessions())
349
447
class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
351
449
backend = CacheSession
353
451
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)
452
# Some backends might issue a warning
453
with warnings.catch_warnings():
454
warnings.simplefilter("ignore")
455
self.session._session_key = (string.ascii_letters + string.digits) * 20
456
self.assertEqual(self.session.load(), {})
458
def test_default_cache(self):
460
self.assertNotEqual(get_cache('default').get(self.session.cache_key), None)
462
@override_settings(CACHES={
464
'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
467
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
469
}, SESSION_CACHE_ALIAS='sessions')
470
def test_non_default_cache(self):
472
self.assertEqual(get_cache('default').get(self.session.cache_key), None)
473
self.assertNotEqual(get_cache('sessions').get(self.session.cache_key), None)
362
476
class SessionMiddlewareTests(unittest.TestCase):
406
520
# Handle the response through the middleware
407
521
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:
411
response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])
522
self.assertFalse(response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])
413
524
self.assertNotIn('httponly',
414
525
str(response.cookies[settings.SESSION_COOKIE_NAME]))
527
def test_session_save_on_500(self):
528
request = RequestFactory().get('/')
529
response = HttpResponse('Horrible error')
530
response.status_code = 500
531
middleware = SessionMiddleware()
533
# Simulate a request the modifies the session
534
middleware.process_request(request)
535
request.session['hello'] = 'world'
537
# Handle the response through the middleware
538
response = middleware.process_response(request, response)
540
# Check that the value wasn't saved above.
541
self.assertNotIn('hello', request.session.load())
417
544
class CookieSessionTests(SessionTestsMixin, TestCase):