test_cache.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. from unittest import mock
  2. from asgiref.sync import iscoroutinefunction
  3. from django.http import HttpRequest, HttpResponse
  4. from django.test import SimpleTestCase
  5. from django.utils.decorators import method_decorator
  6. from django.views.decorators.cache import cache_control, cache_page, never_cache
  7. class HttpRequestProxy:
  8. def __init__(self, request):
  9. self._request = request
  10. def __getattr__(self, attr):
  11. """Proxy to the underlying HttpRequest object."""
  12. return getattr(self._request, attr)
  13. class CacheControlDecoratorTest(SimpleTestCase):
  14. def test_wrapped_sync_function_is_not_coroutine_function(self):
  15. def sync_view(request):
  16. return HttpResponse()
  17. wrapped_view = cache_control()(sync_view)
  18. self.assertIs(iscoroutinefunction(wrapped_view), False)
  19. def test_wrapped_async_function_is_coroutine_function(self):
  20. async def async_view(request):
  21. return HttpResponse()
  22. wrapped_view = cache_control()(async_view)
  23. self.assertIs(iscoroutinefunction(wrapped_view), True)
  24. def test_cache_control_decorator_http_request(self):
  25. class MyClass:
  26. @cache_control(a="b")
  27. def a_view(self, request):
  28. return HttpResponse()
  29. msg = (
  30. "cache_control didn't receive an HttpRequest. If you are "
  31. "decorating a classmethod, be sure to use @method_decorator."
  32. )
  33. request = HttpRequest()
  34. with self.assertRaisesMessage(TypeError, msg):
  35. MyClass().a_view(request)
  36. with self.assertRaisesMessage(TypeError, msg):
  37. MyClass().a_view(HttpRequestProxy(request))
  38. async def test_cache_control_decorator_http_request_async_view(self):
  39. class MyClass:
  40. @cache_control(a="b")
  41. async def async_view(self, request):
  42. return HttpResponse()
  43. msg = (
  44. "cache_control didn't receive an HttpRequest. If you are decorating a "
  45. "classmethod, be sure to use @method_decorator."
  46. )
  47. request = HttpRequest()
  48. with self.assertRaisesMessage(TypeError, msg):
  49. await MyClass().async_view(request)
  50. with self.assertRaisesMessage(TypeError, msg):
  51. await MyClass().async_view(HttpRequestProxy(request))
  52. def test_cache_control_decorator_http_request_proxy(self):
  53. class MyClass:
  54. @method_decorator(cache_control(a="b"))
  55. def a_view(self, request):
  56. return HttpResponse()
  57. request = HttpRequest()
  58. response = MyClass().a_view(HttpRequestProxy(request))
  59. self.assertEqual(response.headers["Cache-Control"], "a=b")
  60. def test_cache_control_empty_decorator(self):
  61. @cache_control()
  62. def a_view(request):
  63. return HttpResponse()
  64. response = a_view(HttpRequest())
  65. self.assertEqual(response.get("Cache-Control"), "")
  66. async def test_cache_control_empty_decorator_async_view(self):
  67. @cache_control()
  68. async def async_view(request):
  69. return HttpResponse()
  70. response = await async_view(HttpRequest())
  71. self.assertEqual(response.get("Cache-Control"), "")
  72. def test_cache_control_full_decorator(self):
  73. @cache_control(max_age=123, private=True, public=True, custom=456)
  74. def a_view(request):
  75. return HttpResponse()
  76. response = a_view(HttpRequest())
  77. cache_control_items = response.get("Cache-Control").split(", ")
  78. self.assertEqual(
  79. set(cache_control_items), {"max-age=123", "private", "public", "custom=456"}
  80. )
  81. async def test_cache_control_full_decorator_async_view(self):
  82. @cache_control(max_age=123, private=True, public=True, custom=456)
  83. async def async_view(request):
  84. return HttpResponse()
  85. response = await async_view(HttpRequest())
  86. cache_control_items = response.get("Cache-Control").split(", ")
  87. self.assertEqual(
  88. set(cache_control_items), {"max-age=123", "private", "public", "custom=456"}
  89. )
  90. class CachePageDecoratorTest(SimpleTestCase):
  91. def test_cache_page(self):
  92. def my_view(request):
  93. return "response"
  94. my_view_cached = cache_page(123)(my_view)
  95. self.assertEqual(my_view_cached(HttpRequest()), "response")
  96. my_view_cached2 = cache_page(123, key_prefix="test")(my_view)
  97. self.assertEqual(my_view_cached2(HttpRequest()), "response")
  98. class NeverCacheDecoratorTest(SimpleTestCase):
  99. def test_wrapped_sync_function_is_not_coroutine_function(self):
  100. def sync_view(request):
  101. return HttpResponse()
  102. wrapped_view = never_cache(sync_view)
  103. self.assertIs(iscoroutinefunction(wrapped_view), False)
  104. def test_wrapped_async_function_is_coroutine_function(self):
  105. async def async_view(request):
  106. return HttpResponse()
  107. wrapped_view = never_cache(async_view)
  108. self.assertIs(iscoroutinefunction(wrapped_view), True)
  109. @mock.patch("time.time")
  110. def test_never_cache_decorator_headers(self, mocked_time):
  111. @never_cache
  112. def a_view(request):
  113. return HttpResponse()
  114. mocked_time.return_value = 1167616461.0
  115. response = a_view(HttpRequest())
  116. self.assertEqual(
  117. response.headers["Expires"],
  118. "Mon, 01 Jan 2007 01:54:21 GMT",
  119. )
  120. self.assertEqual(
  121. response.headers["Cache-Control"],
  122. "max-age=0, no-cache, no-store, must-revalidate, private",
  123. )
  124. @mock.patch("time.time")
  125. async def test_never_cache_decorator_headers_async_view(self, mocked_time):
  126. @never_cache
  127. async def async_view(request):
  128. return HttpResponse()
  129. mocked_time.return_value = 1167616461.0
  130. response = await async_view(HttpRequest())
  131. self.assertEqual(response.headers["Expires"], "Mon, 01 Jan 2007 01:54:21 GMT")
  132. self.assertEqual(
  133. response.headers["Cache-Control"],
  134. "max-age=0, no-cache, no-store, must-revalidate, private",
  135. )
  136. def test_never_cache_decorator_expires_not_overridden(self):
  137. @never_cache
  138. def a_view(request):
  139. return HttpResponse(headers={"Expires": "tomorrow"})
  140. response = a_view(HttpRequest())
  141. self.assertEqual(response.headers["Expires"], "tomorrow")
  142. async def test_never_cache_decorator_expires_not_overridden_async_view(self):
  143. @never_cache
  144. async def async_view(request):
  145. return HttpResponse(headers={"Expires": "tomorrow"})
  146. response = await async_view(HttpRequest())
  147. self.assertEqual(response.headers["Expires"], "tomorrow")
  148. def test_never_cache_decorator_http_request(self):
  149. class MyClass:
  150. @never_cache
  151. def a_view(self, request):
  152. return HttpResponse()
  153. request = HttpRequest()
  154. msg = (
  155. "never_cache didn't receive an HttpRequest. If you are decorating "
  156. "a classmethod, be sure to use @method_decorator."
  157. )
  158. with self.assertRaisesMessage(TypeError, msg):
  159. MyClass().a_view(request)
  160. with self.assertRaisesMessage(TypeError, msg):
  161. MyClass().a_view(HttpRequestProxy(request))
  162. async def test_never_cache_decorator_http_request_async_view(self):
  163. class MyClass:
  164. @never_cache
  165. async def async_view(self, request):
  166. return HttpResponse()
  167. request = HttpRequest()
  168. msg = (
  169. "never_cache didn't receive an HttpRequest. If you are decorating "
  170. "a classmethod, be sure to use @method_decorator."
  171. )
  172. with self.assertRaisesMessage(TypeError, msg):
  173. await MyClass().async_view(request)
  174. with self.assertRaisesMessage(TypeError, msg):
  175. await MyClass().async_view(HttpRequestProxy(request))
  176. def test_never_cache_decorator_http_request_proxy(self):
  177. class MyClass:
  178. @method_decorator(never_cache)
  179. def a_view(self, request):
  180. return HttpResponse()
  181. request = HttpRequest()
  182. response = MyClass().a_view(HttpRequestProxy(request))
  183. self.assertIn("Cache-Control", response.headers)
  184. self.assertIn("Expires", response.headers)