test_http.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. import os
  2. from datetime import date
  3. from django.contrib.sitemaps import Sitemap
  4. from django.contrib.sites.models import Site
  5. from django.core.exceptions import ImproperlyConfigured
  6. from django.test import modify_settings, override_settings
  7. from django.utils import translation
  8. from django.utils.formats import localize
  9. from .base import SitemapTestsBase
  10. from .models import TestModel
  11. class HTTPSitemapTests(SitemapTestsBase):
  12. use_sitemap_err_msg = (
  13. 'To use sitemaps, either enable the sites framework or pass a '
  14. 'Site/RequestSite object in your view.'
  15. )
  16. def test_simple_sitemap_index(self):
  17. "A simple sitemap index can be rendered"
  18. response = self.client.get('/simple/index.xml')
  19. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  20. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  21. <sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
  22. </sitemapindex>
  23. """ % self.base_url
  24. self.assertXMLEqual(response.content.decode(), expected_content)
  25. def test_sitemap_not_callable(self):
  26. """A sitemap may not be callable."""
  27. response = self.client.get('/simple-not-callable/index.xml')
  28. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  29. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  30. <sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
  31. </sitemapindex>
  32. """ % self.base_url
  33. self.assertXMLEqual(response.content.decode(), expected_content)
  34. def test_paged_sitemap(self):
  35. """A sitemap may have multiple pages."""
  36. response = self.client.get('/simple-paged/index.xml')
  37. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  38. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  39. <sitemap><loc>{0}/simple/sitemap-simple.xml</loc></sitemap><sitemap><loc>{0}/simple/sitemap-simple.xml?p=2</loc></sitemap>
  40. </sitemapindex>
  41. """.format(self.base_url)
  42. self.assertXMLEqual(response.content.decode(), expected_content)
  43. @override_settings(TEMPLATES=[{
  44. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  45. 'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
  46. }])
  47. def test_simple_sitemap_custom_index(self):
  48. "A simple sitemap index can be rendered with a custom template"
  49. response = self.client.get('/simple/custom-index.xml')
  50. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  51. <!-- This is a customised template -->
  52. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  53. <sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
  54. </sitemapindex>
  55. """ % self.base_url
  56. self.assertXMLEqual(response.content.decode(), expected_content)
  57. def test_simple_sitemap_section(self):
  58. "A simple sitemap section can be rendered"
  59. response = self.client.get('/simple/sitemap-simple.xml')
  60. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  61. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  62. <url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  63. </urlset>
  64. """ % (self.base_url, date.today())
  65. self.assertXMLEqual(response.content.decode(), expected_content)
  66. def test_no_section(self):
  67. response = self.client.get('/simple/sitemap-simple2.xml')
  68. self.assertEqual(str(response.context['exception']), "No sitemap available for section: 'simple2'")
  69. self.assertEqual(response.status_code, 404)
  70. def test_empty_page(self):
  71. response = self.client.get('/simple/sitemap-simple.xml?p=0')
  72. self.assertEqual(str(response.context['exception']), 'Page 0 empty')
  73. self.assertEqual(response.status_code, 404)
  74. def test_page_not_int(self):
  75. response = self.client.get('/simple/sitemap-simple.xml?p=test')
  76. self.assertEqual(str(response.context['exception']), "No page 'test'")
  77. self.assertEqual(response.status_code, 404)
  78. def test_simple_sitemap(self):
  79. "A simple sitemap can be rendered"
  80. response = self.client.get('/simple/sitemap.xml')
  81. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  82. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  83. <url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  84. </urlset>
  85. """ % (self.base_url, date.today())
  86. self.assertXMLEqual(response.content.decode(), expected_content)
  87. @override_settings(TEMPLATES=[{
  88. 'BACKEND': 'django.template.backends.django.DjangoTemplates',
  89. 'DIRS': [os.path.join(os.path.dirname(__file__), 'templates')],
  90. }])
  91. def test_simple_custom_sitemap(self):
  92. "A simple sitemap can be rendered with a custom template"
  93. response = self.client.get('/simple/custom-sitemap.xml')
  94. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  95. <!-- This is a customised template -->
  96. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  97. <url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  98. </urlset>
  99. """ % (self.base_url, date.today())
  100. self.assertXMLEqual(response.content.decode(), expected_content)
  101. def test_sitemap_last_modified(self):
  102. "Last-Modified header is set correctly"
  103. response = self.client.get('/lastmod/sitemap.xml')
  104. self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT')
  105. def test_sitemap_last_modified_date(self):
  106. """
  107. The Last-Modified header should be support dates (without time).
  108. """
  109. response = self.client.get('/lastmod/date-sitemap.xml')
  110. self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 00:00:00 GMT')
  111. def test_sitemap_last_modified_tz(self):
  112. """
  113. The Last-Modified header should be converted from timezone aware dates
  114. to GMT.
  115. """
  116. response = self.client.get('/lastmod/tz-sitemap.xml')
  117. self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 15:00:00 GMT')
  118. def test_sitemap_last_modified_missing(self):
  119. "Last-Modified header is missing when sitemap has no lastmod"
  120. response = self.client.get('/generic/sitemap.xml')
  121. self.assertFalse(response.has_header('Last-Modified'))
  122. def test_sitemap_last_modified_mixed(self):
  123. "Last-Modified header is omitted when lastmod not on all items"
  124. response = self.client.get('/lastmod-mixed/sitemap.xml')
  125. self.assertFalse(response.has_header('Last-Modified'))
  126. def test_sitemaps_lastmod_mixed_ascending_last_modified_missing(self):
  127. """
  128. The Last-Modified header is omitted when lastmod isn't found in all
  129. sitemaps. Test sitemaps are sorted by lastmod in ascending order.
  130. """
  131. response = self.client.get('/lastmod-sitemaps/mixed-ascending.xml')
  132. self.assertFalse(response.has_header('Last-Modified'))
  133. def test_sitemaps_lastmod_mixed_descending_last_modified_missing(self):
  134. """
  135. The Last-Modified header is omitted when lastmod isn't found in all
  136. sitemaps. Test sitemaps are sorted by lastmod in descending order.
  137. """
  138. response = self.client.get('/lastmod-sitemaps/mixed-descending.xml')
  139. self.assertFalse(response.has_header('Last-Modified'))
  140. def test_sitemaps_lastmod_ascending(self):
  141. """
  142. The Last-Modified header is set to the most recent sitemap lastmod.
  143. Test sitemaps are sorted by lastmod in ascending order.
  144. """
  145. response = self.client.get('/lastmod-sitemaps/ascending.xml')
  146. self.assertEqual(response['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
  147. def test_sitemaps_lastmod_descending(self):
  148. """
  149. The Last-Modified header is set to the most recent sitemap lastmod.
  150. Test sitemaps are sorted by lastmod in descending order.
  151. """
  152. response = self.client.get('/lastmod-sitemaps/descending.xml')
  153. self.assertEqual(response['Last-Modified'], 'Sat, 20 Apr 2013 05:00:00 GMT')
  154. @override_settings(USE_I18N=True, USE_L10N=True)
  155. def test_localized_priority(self):
  156. """The priority value should not be localized."""
  157. with translation.override('fr'):
  158. self.assertEqual('0,3', localize(0.3))
  159. # Priorities aren't rendered in localized format.
  160. response = self.client.get('/simple/sitemap.xml')
  161. self.assertContains(response, '<priority>0.5</priority>')
  162. self.assertContains(response, '<lastmod>%s</lastmod>' % date.today())
  163. @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
  164. def test_requestsite_sitemap(self):
  165. # Hitting the flatpages sitemap without the sites framework installed
  166. # doesn't raise an exception.
  167. response = self.client.get('/simple/sitemap.xml')
  168. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  169. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  170. <url><loc>http://testserver/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  171. </urlset>
  172. """ % date.today()
  173. self.assertXMLEqual(response.content.decode(), expected_content)
  174. def test_sitemap_get_urls_no_site_1(self):
  175. """
  176. Check we get ImproperlyConfigured if we don't pass a site object to
  177. Sitemap.get_urls and no Site objects exist
  178. """
  179. Site.objects.all().delete()
  180. with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg):
  181. Sitemap().get_urls()
  182. @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'})
  183. def test_sitemap_get_urls_no_site_2(self):
  184. """
  185. Check we get ImproperlyConfigured when we don't pass a site object to
  186. Sitemap.get_urls if Site objects exists, but the sites framework is not
  187. actually installed.
  188. """
  189. with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg):
  190. Sitemap().get_urls()
  191. def test_sitemap_item(self):
  192. """
  193. Check to make sure that the raw item is included with each
  194. Sitemap.get_url() url result.
  195. """
  196. test_sitemap = Sitemap()
  197. test_sitemap.items = TestModel.objects.order_by('pk').all
  198. def is_testmodel(url):
  199. return isinstance(url['item'], TestModel)
  200. item_in_url_info = all(map(is_testmodel, test_sitemap.get_urls()))
  201. self.assertTrue(item_in_url_info)
  202. def test_cached_sitemap_index(self):
  203. """
  204. A cached sitemap index can be rendered (#2713).
  205. """
  206. response = self.client.get('/cached/index.xml')
  207. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  208. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  209. <sitemap><loc>%s/cached/sitemap-simple.xml</loc></sitemap>
  210. </sitemapindex>
  211. """ % self.base_url
  212. self.assertXMLEqual(response.content.decode(), expected_content)
  213. def test_x_robots_sitemap(self):
  214. response = self.client.get('/simple/index.xml')
  215. self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive')
  216. response = self.client.get('/simple/sitemap.xml')
  217. self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive')
  218. def test_empty_sitemap(self):
  219. response = self.client.get('/empty/sitemap.xml')
  220. self.assertEqual(response.status_code, 200)
  221. @override_settings(LANGUAGES=(('en', 'English'), ('pt', 'Portuguese')))
  222. def test_simple_i18n_sitemap_index(self):
  223. """
  224. A simple i18n sitemap index can be rendered.
  225. """
  226. response = self.client.get('/simple/i18n.xml')
  227. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  228. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  229. <url><loc>{0}/en/i18n/testmodel/{1}/</loc><changefreq>never</changefreq><priority>0.5</priority></url><url><loc>{0}/pt/i18n/testmodel/{1}/</loc><changefreq>never</changefreq><priority>0.5</priority></url>
  230. </urlset>
  231. """.format(self.base_url, self.i18n_model.pk)
  232. self.assertXMLEqual(response.content.decode(), expected_content)
  233. @override_settings(LANGUAGES=(('en', 'English'), ('pt', 'Portuguese')))
  234. def test_alternate_i18n_sitemap_index(self):
  235. """
  236. A i18n sitemap with alternate/hreflang links can be rendered.
  237. """
  238. response = self.client.get('/alternates/i18n.xml')
  239. url, pk = self.base_url, self.i18n_model.pk
  240. expected_urls = f"""
  241. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  242. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  243. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  244. </url>
  245. <url><loc>{url}/pt/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  246. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  247. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  248. </url>
  249. """.replace('\n', '')
  250. expected_content = f"""<?xml version="1.0" encoding="UTF-8"?>
  251. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  252. {expected_urls}
  253. </urlset>
  254. """
  255. self.assertXMLEqual(response.content.decode(), expected_content)
  256. @override_settings(LANGUAGES=(('en', 'English'), ('pt', 'Portuguese'), ('es', 'Spanish')))
  257. def test_alternate_i18n_sitemap_limited(self):
  258. """
  259. A i18n sitemap index with limited languages can be rendered.
  260. """
  261. response = self.client.get('/limited/i18n.xml')
  262. url, pk = self.base_url, self.i18n_model.pk
  263. expected_urls = f"""
  264. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  265. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  266. <xhtml:link rel="alternate" hreflang="es" href="{url}/es/i18n/testmodel/{pk}/"/>
  267. </url>
  268. <url><loc>{url}/es/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  269. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  270. <xhtml:link rel="alternate" hreflang="es" href="{url}/es/i18n/testmodel/{pk}/"/>
  271. </url>
  272. """.replace('\n', '')
  273. expected_content = f"""<?xml version="1.0" encoding="UTF-8"?>
  274. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  275. {expected_urls}
  276. </urlset>
  277. """
  278. self.assertXMLEqual(response.content.decode(), expected_content)
  279. @override_settings(LANGUAGES=(('en', 'English'), ('pt', 'Portuguese')))
  280. def test_alternate_i18n_sitemap_xdefault(self):
  281. """
  282. A i18n sitemap index with x-default can be rendered.
  283. """
  284. response = self.client.get('/x-default/i18n.xml')
  285. url, pk = self.base_url, self.i18n_model.pk
  286. expected_urls = f"""
  287. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  288. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  289. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  290. <xhtml:link rel="alternate" hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/>
  291. </url>
  292. <url><loc>{url}/pt/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  293. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  294. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  295. <xhtml:link rel="alternate" hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/>
  296. </url>
  297. """.replace('\n', '')
  298. expected_content = f"""<?xml version="1.0" encoding="UTF-8"?>
  299. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  300. {expected_urls}
  301. </urlset>
  302. """
  303. self.assertXMLEqual(response.content.decode(), expected_content)
  304. def test_sitemap_without_entries(self):
  305. response = self.client.get('/sitemap-without-entries/sitemap.xml')
  306. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  307. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml">
  308. </urlset>"""
  309. self.assertXMLEqual(response.content.decode(), expected_content)