parts/django/tests/regressiontests/cache/tests.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 # -*- coding: utf-8 -*-
       
     2 
       
     3 # Unit tests for cache framework
       
     4 # Uses whatever cache backend is set in the test settings file.
       
     5 
       
     6 import os
       
     7 import shutil
       
     8 import tempfile
       
     9 import time
       
    10 import unittest
       
    11 import warnings
       
    12 
       
    13 from django.conf import settings
       
    14 from django.core import management
       
    15 from django.core.cache import get_cache
       
    16 from django.core.cache.backends.base import InvalidCacheBackendError, CacheKeyWarning
       
    17 from django.http import HttpResponse, HttpRequest
       
    18 from django.middleware.cache import FetchFromCacheMiddleware, UpdateCacheMiddleware
       
    19 from django.test.utils import get_warnings_state, restore_warnings_state
       
    20 from django.utils import translation
       
    21 from django.utils.cache import patch_vary_headers, get_cache_key, learn_cache_key
       
    22 from django.utils.hashcompat import md5_constructor
       
    23 from regressiontests.cache.models import Poll, expensive_calculation
       
    24 
       
    25 # functions/classes for complex data type tests
       
    26 def f():
       
    27     return 42
       
    28 class C:
       
    29     def m(n):
       
    30         return 24
       
    31 
       
    32 class DummyCacheTests(unittest.TestCase):
       
    33     # The Dummy cache backend doesn't really behave like a test backend,
       
    34     # so it has different test requirements.
       
    35     def setUp(self):
       
    36         self.cache = get_cache('dummy://')
       
    37 
       
    38     def test_simple(self):
       
    39         "Dummy cache backend ignores cache set calls"
       
    40         self.cache.set("key", "value")
       
    41         self.assertEqual(self.cache.get("key"), None)
       
    42 
       
    43     def test_add(self):
       
    44         "Add doesn't do anything in dummy cache backend"
       
    45         self.cache.add("addkey1", "value")
       
    46         result = self.cache.add("addkey1", "newvalue")
       
    47         self.assertEqual(result, True)
       
    48         self.assertEqual(self.cache.get("addkey1"), None)
       
    49 
       
    50     def test_non_existent(self):
       
    51         "Non-existent keys aren't found in the dummy cache backend"
       
    52         self.assertEqual(self.cache.get("does_not_exist"), None)
       
    53         self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
       
    54 
       
    55     def test_get_many(self):
       
    56         "get_many returns nothing for the dummy cache backend"
       
    57         self.cache.set('a', 'a')
       
    58         self.cache.set('b', 'b')
       
    59         self.cache.set('c', 'c')
       
    60         self.cache.set('d', 'd')
       
    61         self.assertEqual(self.cache.get_many(['a', 'c', 'd']), {})
       
    62         self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {})
       
    63 
       
    64     def test_delete(self):
       
    65         "Cache deletion is transparently ignored on the dummy cache backend"
       
    66         self.cache.set("key1", "spam")
       
    67         self.cache.set("key2", "eggs")
       
    68         self.assertEqual(self.cache.get("key1"), None)
       
    69         self.cache.delete("key1")
       
    70         self.assertEqual(self.cache.get("key1"), None)
       
    71         self.assertEqual(self.cache.get("key2"), None)
       
    72 
       
    73     def test_has_key(self):
       
    74         "The has_key method doesn't ever return True for the dummy cache backend"
       
    75         self.cache.set("hello1", "goodbye1")
       
    76         self.assertEqual(self.cache.has_key("hello1"), False)
       
    77         self.assertEqual(self.cache.has_key("goodbye1"), False)
       
    78 
       
    79     def test_in(self):
       
    80         "The in operator doesn't ever return True for the dummy cache backend"
       
    81         self.cache.set("hello2", "goodbye2")
       
    82         self.assertEqual("hello2" in self.cache, False)
       
    83         self.assertEqual("goodbye2" in self.cache, False)
       
    84 
       
    85     def test_incr(self):
       
    86         "Dummy cache values can't be incremented"
       
    87         self.cache.set('answer', 42)
       
    88         self.assertRaises(ValueError, self.cache.incr, 'answer')
       
    89         self.assertRaises(ValueError, self.cache.incr, 'does_not_exist')
       
    90 
       
    91     def test_decr(self):
       
    92         "Dummy cache values can't be decremented"
       
    93         self.cache.set('answer', 42)
       
    94         self.assertRaises(ValueError, self.cache.decr, 'answer')
       
    95         self.assertRaises(ValueError, self.cache.decr, 'does_not_exist')
       
    96 
       
    97     def test_data_types(self):
       
    98         "All data types are ignored equally by the dummy cache"
       
    99         stuff = {
       
   100             'string'    : 'this is a string',
       
   101             'int'       : 42,
       
   102             'list'      : [1, 2, 3, 4],
       
   103             'tuple'     : (1, 2, 3, 4),
       
   104             'dict'      : {'A': 1, 'B' : 2},
       
   105             'function'  : f,
       
   106             'class'     : C,
       
   107         }
       
   108         self.cache.set("stuff", stuff)
       
   109         self.assertEqual(self.cache.get("stuff"), None)
       
   110 
       
   111     def test_expiration(self):
       
   112         "Expiration has no effect on the dummy cache"
       
   113         self.cache.set('expire1', 'very quickly', 1)
       
   114         self.cache.set('expire2', 'very quickly', 1)
       
   115         self.cache.set('expire3', 'very quickly', 1)
       
   116 
       
   117         time.sleep(2)
       
   118         self.assertEqual(self.cache.get("expire1"), None)
       
   119 
       
   120         self.cache.add("expire2", "newvalue")
       
   121         self.assertEqual(self.cache.get("expire2"), None)
       
   122         self.assertEqual(self.cache.has_key("expire3"), False)
       
   123 
       
   124     def test_unicode(self):
       
   125         "Unicode values are ignored by the dummy cache"
       
   126         stuff = {
       
   127             u'ascii': u'ascii_value',
       
   128             u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
       
   129             u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
       
   130             u'ascii': {u'x' : 1 }
       
   131             }
       
   132         for (key, value) in stuff.items():
       
   133             self.cache.set(key, value)
       
   134             self.assertEqual(self.cache.get(key), None)
       
   135 
       
   136     def test_set_many(self):
       
   137         "set_many does nothing for the dummy cache backend"
       
   138         self.cache.set_many({'a': 1, 'b': 2})
       
   139 
       
   140     def test_delete_many(self):
       
   141         "delete_many does nothing for the dummy cache backend"
       
   142         self.cache.delete_many(['a', 'b'])
       
   143 
       
   144     def test_clear(self):
       
   145         "clear does nothing for the dummy cache backend"
       
   146         self.cache.clear()
       
   147 
       
   148 
       
   149 class BaseCacheTests(object):
       
   150     # A common set of tests to apply to all cache backends
       
   151     def tearDown(self):
       
   152         self.cache.clear()
       
   153 
       
   154     def test_simple(self):
       
   155         # Simple cache set/get works
       
   156         self.cache.set("key", "value")
       
   157         self.assertEqual(self.cache.get("key"), "value")
       
   158 
       
   159     def test_add(self):
       
   160         # A key can be added to a cache
       
   161         self.cache.add("addkey1", "value")
       
   162         result = self.cache.add("addkey1", "newvalue")
       
   163         self.assertEqual(result, False)
       
   164         self.assertEqual(self.cache.get("addkey1"), "value")
       
   165 
       
   166     def test_non_existent(self):
       
   167         # Non-existent cache keys return as None/default
       
   168         # get with non-existent keys
       
   169         self.assertEqual(self.cache.get("does_not_exist"), None)
       
   170         self.assertEqual(self.cache.get("does_not_exist", "bang!"), "bang!")
       
   171 
       
   172     def test_get_many(self):
       
   173         # Multiple cache keys can be returned using get_many
       
   174         self.cache.set('a', 'a')
       
   175         self.cache.set('b', 'b')
       
   176         self.cache.set('c', 'c')
       
   177         self.cache.set('d', 'd')
       
   178         self.assertEqual(self.cache.get_many(['a', 'c', 'd']), {'a' : 'a', 'c' : 'c', 'd' : 'd'})
       
   179         self.assertEqual(self.cache.get_many(['a', 'b', 'e']), {'a' : 'a', 'b' : 'b'})
       
   180 
       
   181     def test_delete(self):
       
   182         # Cache keys can be deleted
       
   183         self.cache.set("key1", "spam")
       
   184         self.cache.set("key2", "eggs")
       
   185         self.assertEqual(self.cache.get("key1"), "spam")
       
   186         self.cache.delete("key1")
       
   187         self.assertEqual(self.cache.get("key1"), None)
       
   188         self.assertEqual(self.cache.get("key2"), "eggs")
       
   189 
       
   190     def test_has_key(self):
       
   191         # The cache can be inspected for cache keys
       
   192         self.cache.set("hello1", "goodbye1")
       
   193         self.assertEqual(self.cache.has_key("hello1"), True)
       
   194         self.assertEqual(self.cache.has_key("goodbye1"), False)
       
   195 
       
   196     def test_in(self):
       
   197         # The in operator can be used to inspet cache contents
       
   198         self.cache.set("hello2", "goodbye2")
       
   199         self.assertEqual("hello2" in self.cache, True)
       
   200         self.assertEqual("goodbye2" in self.cache, False)
       
   201 
       
   202     def test_incr(self):
       
   203         # Cache values can be incremented
       
   204         self.cache.set('answer', 41)
       
   205         self.assertEqual(self.cache.incr('answer'), 42)
       
   206         self.assertEqual(self.cache.get('answer'), 42)
       
   207         self.assertEqual(self.cache.incr('answer', 10), 52)
       
   208         self.assertEqual(self.cache.get('answer'), 52)
       
   209         self.assertRaises(ValueError, self.cache.incr, 'does_not_exist')
       
   210 
       
   211     def test_decr(self):
       
   212         # Cache values can be decremented
       
   213         self.cache.set('answer', 43)
       
   214         self.assertEqual(self.cache.decr('answer'), 42)
       
   215         self.assertEqual(self.cache.get('answer'), 42)
       
   216         self.assertEqual(self.cache.decr('answer', 10), 32)
       
   217         self.assertEqual(self.cache.get('answer'), 32)
       
   218         self.assertRaises(ValueError, self.cache.decr, 'does_not_exist')
       
   219 
       
   220     def test_data_types(self):
       
   221         # Many different data types can be cached
       
   222         stuff = {
       
   223             'string'    : 'this is a string',
       
   224             'int'       : 42,
       
   225             'list'      : [1, 2, 3, 4],
       
   226             'tuple'     : (1, 2, 3, 4),
       
   227             'dict'      : {'A': 1, 'B' : 2},
       
   228             'function'  : f,
       
   229             'class'     : C,
       
   230         }
       
   231         self.cache.set("stuff", stuff)
       
   232         self.assertEqual(self.cache.get("stuff"), stuff)
       
   233 
       
   234     def test_cache_read_for_model_instance(self):
       
   235         # Don't want fields with callable as default to be called on cache read
       
   236         expensive_calculation.num_runs = 0
       
   237         Poll.objects.all().delete()
       
   238         my_poll = Poll.objects.create(question="Well?")
       
   239         self.assertEqual(Poll.objects.count(), 1)
       
   240         pub_date = my_poll.pub_date
       
   241         self.cache.set('question', my_poll)
       
   242         cached_poll = self.cache.get('question')
       
   243         self.assertEqual(cached_poll.pub_date, pub_date)
       
   244         # We only want the default expensive calculation run once
       
   245         self.assertEqual(expensive_calculation.num_runs, 1)
       
   246 
       
   247     def test_cache_write_for_model_instance_with_deferred(self):
       
   248         # Don't want fields with callable as default to be called on cache write
       
   249         expensive_calculation.num_runs = 0
       
   250         Poll.objects.all().delete()
       
   251         my_poll = Poll.objects.create(question="What?")
       
   252         self.assertEqual(expensive_calculation.num_runs, 1)
       
   253         defer_qs = Poll.objects.all().defer('question')
       
   254         self.assertEqual(defer_qs.count(), 1)
       
   255         self.assertEqual(expensive_calculation.num_runs, 1)
       
   256         self.cache.set('deferred_queryset', defer_qs)
       
   257         # cache set should not re-evaluate default functions
       
   258         self.assertEqual(expensive_calculation.num_runs, 1)
       
   259 
       
   260     def test_cache_read_for_model_instance_with_deferred(self):
       
   261         # Don't want fields with callable as default to be called on cache read
       
   262         expensive_calculation.num_runs = 0
       
   263         Poll.objects.all().delete()
       
   264         my_poll = Poll.objects.create(question="What?")
       
   265         self.assertEqual(expensive_calculation.num_runs, 1)
       
   266         defer_qs = Poll.objects.all().defer('question')
       
   267         self.assertEqual(defer_qs.count(), 1)
       
   268         self.cache.set('deferred_queryset', defer_qs)
       
   269         self.assertEqual(expensive_calculation.num_runs, 1)
       
   270         runs_before_cache_read = expensive_calculation.num_runs
       
   271         cached_polls = self.cache.get('deferred_queryset')
       
   272         # We only want the default expensive calculation run on creation and set
       
   273         self.assertEqual(expensive_calculation.num_runs, runs_before_cache_read)
       
   274 
       
   275     def test_expiration(self):
       
   276         # Cache values can be set to expire
       
   277         self.cache.set('expire1', 'very quickly', 1)
       
   278         self.cache.set('expire2', 'very quickly', 1)
       
   279         self.cache.set('expire3', 'very quickly', 1)
       
   280 
       
   281         time.sleep(2)
       
   282         self.assertEqual(self.cache.get("expire1"), None)
       
   283 
       
   284         self.cache.add("expire2", "newvalue")
       
   285         self.assertEqual(self.cache.get("expire2"), "newvalue")
       
   286         self.assertEqual(self.cache.has_key("expire3"), False)
       
   287 
       
   288     def test_unicode(self):
       
   289         # Unicode values can be cached
       
   290         stuff = {
       
   291             u'ascii': u'ascii_value',
       
   292             u'unicode_ascii': u'Iñtërnâtiônàlizætiøn1',
       
   293             u'Iñtërnâtiônàlizætiøn': u'Iñtërnâtiônàlizætiøn2',
       
   294             u'ascii': {u'x' : 1 }
       
   295             }
       
   296         for (key, value) in stuff.items():
       
   297             self.cache.set(key, value)
       
   298             self.assertEqual(self.cache.get(key), value)
       
   299 
       
   300     def test_binary_string(self):
       
   301         # Binary strings should be cachable
       
   302         from zlib import compress, decompress
       
   303         value = 'value_to_be_compressed'
       
   304         compressed_value = compress(value)
       
   305         self.cache.set('binary1', compressed_value)
       
   306         compressed_result = self.cache.get('binary1')
       
   307         self.assertEqual(compressed_value, compressed_result)
       
   308         self.assertEqual(value, decompress(compressed_result))
       
   309 
       
   310     def test_set_many(self):
       
   311         # Multiple keys can be set using set_many
       
   312         self.cache.set_many({"key1": "spam", "key2": "eggs"})
       
   313         self.assertEqual(self.cache.get("key1"), "spam")
       
   314         self.assertEqual(self.cache.get("key2"), "eggs")
       
   315 
       
   316     def test_set_many_expiration(self):
       
   317         # set_many takes a second ``timeout`` parameter
       
   318         self.cache.set_many({"key1": "spam", "key2": "eggs"}, 1)
       
   319         time.sleep(2)
       
   320         self.assertEqual(self.cache.get("key1"), None)
       
   321         self.assertEqual(self.cache.get("key2"), None)
       
   322 
       
   323     def test_delete_many(self):
       
   324         # Multiple keys can be deleted using delete_many
       
   325         self.cache.set("key1", "spam")
       
   326         self.cache.set("key2", "eggs")
       
   327         self.cache.set("key3", "ham")
       
   328         self.cache.delete_many(["key1", "key2"])
       
   329         self.assertEqual(self.cache.get("key1"), None)
       
   330         self.assertEqual(self.cache.get("key2"), None)
       
   331         self.assertEqual(self.cache.get("key3"), "ham")
       
   332 
       
   333     def test_clear(self):
       
   334         # The cache can be emptied using clear
       
   335         self.cache.set("key1", "spam")
       
   336         self.cache.set("key2", "eggs")
       
   337         self.cache.clear()
       
   338         self.assertEqual(self.cache.get("key1"), None)
       
   339         self.assertEqual(self.cache.get("key2"), None)
       
   340 
       
   341     def test_long_timeout(self):
       
   342         '''
       
   343         Using a timeout greater than 30 days makes memcached think
       
   344         it is an absolute expiration timestamp instead of a relative
       
   345         offset. Test that we honour this convention. Refs #12399.
       
   346         '''
       
   347         self.cache.set('key1', 'eggs', 60*60*24*30 + 1) #30 days + 1 second
       
   348         self.assertEqual(self.cache.get('key1'), 'eggs')
       
   349 
       
   350         self.cache.add('key2', 'ham', 60*60*24*30 + 1)
       
   351         self.assertEqual(self.cache.get('key2'), 'ham')
       
   352 
       
   353         self.cache.set_many({'key3': 'sausage', 'key4': 'lobster bisque'}, 60*60*24*30 + 1)
       
   354         self.assertEqual(self.cache.get('key3'), 'sausage')
       
   355         self.assertEqual(self.cache.get('key4'), 'lobster bisque')
       
   356 
       
   357     def perform_cull_test(self, initial_count, final_count):
       
   358         """This is implemented as a utility method, because only some of the backends
       
   359         implement culling. The culling algorithm also varies slightly, so the final
       
   360         number of entries will vary between backends"""
       
   361         # Create initial cache key entries. This will overflow the cache, causing a cull
       
   362         for i in range(1, initial_count):
       
   363             self.cache.set('cull%d' % i, 'value', 1000)
       
   364         count = 0
       
   365         # Count how many keys are left in the cache.
       
   366         for i in range(1, initial_count):
       
   367             if self.cache.has_key('cull%d' % i):
       
   368                 count = count + 1
       
   369         self.assertEqual(count, final_count)
       
   370 
       
   371     def test_invalid_keys(self):
       
   372         """
       
   373         All the builtin backends (except memcached, see below) should warn on
       
   374         keys that would be refused by memcached. This encourages portable
       
   375         caching code without making it too difficult to use production backends
       
   376         with more liberal key rules. Refs #6447.
       
   377 
       
   378         """
       
   379         # On Python 2.6+ we could use the catch_warnings context
       
   380         # manager to test this warning nicely. Since we can't do that
       
   381         # yet, the cleanest option is to temporarily ask for
       
   382         # CacheKeyWarning to be raised as an exception.
       
   383         _warnings_state = get_warnings_state()
       
   384         warnings.simplefilter("error", CacheKeyWarning)
       
   385 
       
   386         try:
       
   387             # memcached does not allow whitespace or control characters in keys
       
   388             self.assertRaises(CacheKeyWarning, self.cache.set, 'key with spaces', 'value')
       
   389             # memcached limits key length to 250
       
   390             self.assertRaises(CacheKeyWarning, self.cache.set, 'a' * 251, 'value')
       
   391         finally:
       
   392             restore_warnings_state(_warnings_state)
       
   393 
       
   394 class DBCacheTests(unittest.TestCase, BaseCacheTests):
       
   395     def setUp(self):
       
   396         # Spaces are used in the table name to ensure quoting/escaping is working
       
   397         self._table_name = 'test cache table'
       
   398         management.call_command('createcachetable', self._table_name, verbosity=0, interactive=False)
       
   399         self.cache = get_cache('db://%s?max_entries=30' % self._table_name)
       
   400 
       
   401     def tearDown(self):
       
   402         from django.db import connection
       
   403         cursor = connection.cursor()
       
   404         cursor.execute('DROP TABLE %s' % connection.ops.quote_name(self._table_name))
       
   405 
       
   406     def test_cull(self):
       
   407         self.perform_cull_test(50, 29)
       
   408 
       
   409 class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
       
   410     def setUp(self):
       
   411         self.cache = get_cache('locmem://?max_entries=30')
       
   412 
       
   413     def test_cull(self):
       
   414         self.perform_cull_test(50, 29)
       
   415 
       
   416 # memcached backend isn't guaranteed to be available.
       
   417 # To check the memcached backend, the test settings file will
       
   418 # need to contain a CACHE_BACKEND setting that points at
       
   419 # your memcache server.
       
   420 if settings.CACHE_BACKEND.startswith('memcached://'):
       
   421     class MemcachedCacheTests(unittest.TestCase, BaseCacheTests):
       
   422         def setUp(self):
       
   423             self.cache = get_cache(settings.CACHE_BACKEND)
       
   424 
       
   425         def test_invalid_keys(self):
       
   426             """
       
   427             On memcached, we don't introduce a duplicate key validation
       
   428             step (for speed reasons), we just let the memcached API
       
   429             library raise its own exception on bad keys. Refs #6447.
       
   430 
       
   431             In order to be memcached-API-library agnostic, we only assert
       
   432             that a generic exception of some kind is raised.
       
   433 
       
   434             """
       
   435             # memcached does not allow whitespace or control characters in keys
       
   436             self.assertRaises(Exception, self.cache.set, 'key with spaces', 'value')
       
   437             # memcached limits key length to 250
       
   438             self.assertRaises(Exception, self.cache.set, 'a' * 251, 'value')
       
   439 
       
   440 
       
   441 class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
       
   442     """
       
   443     Specific test cases for the file-based cache.
       
   444     """
       
   445     def setUp(self):
       
   446         self.dirname = tempfile.mkdtemp()
       
   447         self.cache = get_cache('file://%s?max_entries=30' % self.dirname)
       
   448 
       
   449     def test_hashing(self):
       
   450         """Test that keys are hashed into subdirectories correctly"""
       
   451         self.cache.set("foo", "bar")
       
   452         keyhash = md5_constructor("foo").hexdigest()
       
   453         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
       
   454         self.assert_(os.path.exists(keypath))
       
   455 
       
   456     def test_subdirectory_removal(self):
       
   457         """
       
   458         Make sure that the created subdirectories are correctly removed when empty.
       
   459         """
       
   460         self.cache.set("foo", "bar")
       
   461         keyhash = md5_constructor("foo").hexdigest()
       
   462         keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
       
   463         self.assert_(os.path.exists(keypath))
       
   464 
       
   465         self.cache.delete("foo")
       
   466         self.assert_(not os.path.exists(keypath))
       
   467         self.assert_(not os.path.exists(os.path.dirname(keypath)))
       
   468         self.assert_(not os.path.exists(os.path.dirname(os.path.dirname(keypath))))
       
   469 
       
   470     def test_cull(self):
       
   471         self.perform_cull_test(50, 28)
       
   472 
       
   473 class CustomCacheKeyValidationTests(unittest.TestCase):
       
   474     """
       
   475     Tests for the ability to mixin a custom ``validate_key`` method to
       
   476     a custom cache backend that otherwise inherits from a builtin
       
   477     backend, and override the default key validation. Refs #6447.
       
   478 
       
   479     """
       
   480     def test_custom_key_validation(self):
       
   481         cache = get_cache('regressiontests.cache.liberal_backend://')
       
   482 
       
   483         # this key is both longer than 250 characters, and has spaces
       
   484         key = 'some key with spaces' * 15
       
   485         val = 'a value'
       
   486         cache.set(key, val)
       
   487         self.assertEqual(cache.get(key), val)
       
   488 
       
   489 class CacheUtils(unittest.TestCase):
       
   490     """TestCase for django.utils.cache functions."""
       
   491 
       
   492     def setUp(self):
       
   493         self.path = '/cache/test/'
       
   494         self.old_settings_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
       
   495         self.old_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
       
   496         self.orig_use_i18n = settings.USE_I18N
       
   497         settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
       
   498         settings.CACHE_MIDDLEWARE_SECONDS = 1
       
   499         settings.USE_I18N = False
       
   500 
       
   501     def tearDown(self):
       
   502         settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.old_settings_key_prefix
       
   503         settings.CACHE_MIDDLEWARE_SECONDS = self.old_middleware_seconds
       
   504         settings.USE_I18N = self.orig_use_i18n
       
   505 
       
   506     def _get_request(self, path):
       
   507         request = HttpRequest()
       
   508         request.META = {
       
   509             'SERVER_NAME': 'testserver',
       
   510             'SERVER_PORT': 80,
       
   511         }
       
   512         request.path = request.path_info = "/cache/%s" % path
       
   513         return request
       
   514 
       
   515     def test_patch_vary_headers(self):
       
   516         headers = (
       
   517             # Initial vary, new headers, resulting vary.
       
   518             (None, ('Accept-Encoding',), 'Accept-Encoding'),
       
   519             ('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'),
       
   520             ('Accept-Encoding', ('ACCEPT-ENCODING',), 'Accept-Encoding'),
       
   521             ('Cookie', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
       
   522             ('Cookie, Accept-Encoding', ('Accept-Encoding',), 'Cookie, Accept-Encoding'),
       
   523             ('Cookie, Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
       
   524             (None, ('Accept-Encoding', 'COOKIE'), 'Accept-Encoding, COOKIE'),
       
   525             ('Cookie,     Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
       
   526             ('Cookie    ,     Accept-Encoding', ('Accept-Encoding', 'cookie'), 'Cookie, Accept-Encoding'),
       
   527         )
       
   528         for initial_vary, newheaders, resulting_vary in headers:
       
   529             response = HttpResponse()
       
   530             if initial_vary is not None:
       
   531                 response['Vary'] = initial_vary
       
   532             patch_vary_headers(response, newheaders)
       
   533             self.assertEqual(response['Vary'], resulting_vary)
       
   534 
       
   535     def test_get_cache_key(self):
       
   536         request = self._get_request(self.path)
       
   537         response = HttpResponse()
       
   538         key_prefix = 'localprefix'
       
   539         # Expect None if no headers have been set yet.
       
   540         self.assertEqual(get_cache_key(request), None)
       
   541         # Set headers to an empty list.
       
   542         learn_cache_key(request, response)
       
   543         self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
       
   544         # Verify that a specified key_prefix is taken in to account.
       
   545         learn_cache_key(request, response, key_prefix=key_prefix)
       
   546         self.assertEqual(get_cache_key(request, key_prefix=key_prefix), 'views.decorators.cache.cache_page.localprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
       
   547 
       
   548     def test_learn_cache_key(self):
       
   549         request = self._get_request(self.path)
       
   550         response = HttpResponse()
       
   551         response['Vary'] = 'Pony'
       
   552         # Make sure that the Vary header is added to the key hash
       
   553         learn_cache_key(request, response)
       
   554         self.assertEqual(get_cache_key(request), 'views.decorators.cache.cache_page.settingsprefix.a8c87a3d8c44853d7f79474f7ffe4ad5.d41d8cd98f00b204e9800998ecf8427e')
       
   555 
       
   556 class CacheI18nTest(unittest.TestCase):
       
   557 
       
   558     def setUp(self):
       
   559         self.orig_cache_middleware_seconds = settings.CACHE_MIDDLEWARE_SECONDS
       
   560         self.orig_cache_middleware_key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
       
   561         self.orig_cache_backend = settings.CACHE_BACKEND
       
   562         self.orig_use_i18n = settings.USE_I18N
       
   563         self.orig_languages =  settings.LANGUAGES
       
   564         settings.LANGUAGES = (
       
   565                 ('en', 'English'),
       
   566                 ('es', 'Spanish'),
       
   567         )
       
   568         settings.CACHE_MIDDLEWARE_KEY_PREFIX = 'settingsprefix'
       
   569         self.path = '/cache/test/'
       
   570 
       
   571     def tearDown(self):
       
   572         settings.CACHE_MIDDLEWARE_SECONDS = self.orig_cache_middleware_seconds
       
   573         settings.CACHE_MIDDLEWARE_KEY_PREFIX = self.orig_cache_middleware_key_prefix
       
   574         settings.CACHE_BACKEND = self.orig_cache_backend
       
   575         settings.USE_I18N = self.orig_use_i18n
       
   576         settings.LANGUAGES = self.orig_languages
       
   577         translation.deactivate()
       
   578 
       
   579     def _get_request(self):
       
   580         request = HttpRequest()
       
   581         request.META = {
       
   582             'SERVER_NAME': 'testserver',
       
   583             'SERVER_PORT': 80,
       
   584         }
       
   585         request.path = request.path_info = self.path
       
   586         return request
       
   587 
       
   588     def _get_request_cache(self):
       
   589         request = HttpRequest()
       
   590         request.META = {
       
   591             'SERVER_NAME': 'testserver',
       
   592             'SERVER_PORT': 80,
       
   593         }
       
   594         request.path = request.path_info = self.path
       
   595         request._cache_update_cache = True
       
   596         request.method = 'GET'
       
   597         request.session = {}
       
   598         return request
       
   599 
       
   600     def test_cache_key_i18n(self):
       
   601         settings.USE_I18N = True
       
   602         request = self._get_request()
       
   603         lang = translation.get_language()
       
   604         response = HttpResponse()
       
   605         key = learn_cache_key(request, response)
       
   606         self.assertTrue(key.endswith(lang), "Cache keys should include the language name when i18n is active")
       
   607         key2 = get_cache_key(request)
       
   608         self.assertEqual(key, key2)
       
   609 
       
   610     def test_cache_key_no_i18n (self):
       
   611         settings.USE_I18N = False
       
   612         request = self._get_request()
       
   613         lang = translation.get_language()
       
   614         response = HttpResponse()
       
   615         key = learn_cache_key(request, response)
       
   616         self.assertFalse(key.endswith(lang), "Cache keys shouldn't include the language name when i18n is inactive")
       
   617 
       
   618     def test_middleware(self):
       
   619         def set_cache(request, lang, msg):
       
   620             translation.activate(lang)
       
   621             response = HttpResponse()
       
   622             response.content= msg
       
   623             return UpdateCacheMiddleware().process_response(request, response)
       
   624 
       
   625         settings.CACHE_MIDDLEWARE_SECONDS = 60
       
   626         settings.CACHE_MIDDLEWARE_KEY_PREFIX="test"
       
   627         settings.CACHE_BACKEND='locmem:///'
       
   628         settings.USE_I18N = True
       
   629         en_message ="Hello world!"
       
   630         es_message ="Hola mundo!"
       
   631 
       
   632         request = self._get_request_cache()
       
   633         set_cache(request, 'en', en_message)
       
   634         get_cache_data = FetchFromCacheMiddleware().process_request(request)
       
   635         # Check that we can recover the cache
       
   636         self.assertNotEqual(get_cache_data.content, None)
       
   637         self.assertEqual(en_message, get_cache_data.content)
       
   638         # change the session language and set content
       
   639         request = self._get_request_cache()
       
   640         set_cache(request, 'es', es_message)
       
   641         # change again the language
       
   642         translation.activate('en')
       
   643         # retrieve the content from cache
       
   644         get_cache_data = FetchFromCacheMiddleware().process_request(request)
       
   645         self.assertEqual(get_cache_data.content, en_message)
       
   646         # change again the language
       
   647         translation.activate('es')
       
   648         get_cache_data = FetchFromCacheMiddleware().process_request(request)
       
   649         self.assertEqual(get_cache_data.content, es_message)
       
   650 
       
   651 if __name__ == '__main__':
       
   652     unittest.main()