test_http.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625
  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 ignore_warnings, modify_settings, override_settings
  7. from django.utils import translation
  8. from django.utils.deprecation import RemovedInDjango50Warning
  9. from django.utils.formats import localize
  10. from .base import SitemapTestsBase
  11. from .models import I18nTestModel, TestModel
  12. class HTTPSitemapTests(SitemapTestsBase):
  13. use_sitemap_err_msg = (
  14. "To use sitemaps, either enable the sites framework or pass a "
  15. "Site/RequestSite object in your view."
  16. )
  17. def test_simple_sitemap_index(self):
  18. "A simple sitemap index can be rendered"
  19. response = self.client.get("/simple/index.xml")
  20. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  21. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  22. <sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
  23. </sitemapindex>
  24. """ % (
  25. self.base_url,
  26. date.today(),
  27. )
  28. self.assertXMLEqual(response.content.decode(), expected_content)
  29. def test_sitemap_not_callable(self):
  30. """A sitemap may not be callable."""
  31. response = self.client.get("/simple-not-callable/index.xml")
  32. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  33. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  34. <sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
  35. </sitemapindex>
  36. """ % (
  37. self.base_url,
  38. date.today(),
  39. )
  40. self.assertXMLEqual(response.content.decode(), expected_content)
  41. def test_paged_sitemap(self):
  42. """A sitemap may have multiple pages."""
  43. response = self.client.get("/simple-paged/index.xml")
  44. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  45. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  46. <sitemap><loc>{0}/simple/sitemap-simple.xml</loc><lastmod>{1}</lastmod></sitemap><sitemap><loc>{0}/simple/sitemap-simple.xml?p=2</loc><lastmod>{1}</lastmod></sitemap>
  47. </sitemapindex>
  48. """.format(
  49. self.base_url, date.today()
  50. )
  51. self.assertXMLEqual(response.content.decode(), expected_content)
  52. @override_settings(
  53. TEMPLATES=[
  54. {
  55. "BACKEND": "django.template.backends.django.DjangoTemplates",
  56. "DIRS": [os.path.join(os.path.dirname(__file__), "templates")],
  57. }
  58. ]
  59. )
  60. def test_simple_sitemap_custom_lastmod_index(self):
  61. "A simple sitemap index can be rendered with a custom template"
  62. response = self.client.get("/simple/custom-lastmod-index.xml")
  63. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  64. <!-- This is a customised template -->
  65. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  66. <sitemap><loc>%s/simple/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
  67. </sitemapindex>
  68. """ % (
  69. self.base_url,
  70. date.today(),
  71. )
  72. self.assertXMLEqual(response.content.decode(), expected_content)
  73. def test_simple_sitemap_section(self):
  74. "A simple sitemap section can be rendered"
  75. response = self.client.get("/simple/sitemap-simple.xml")
  76. expected_content = (
  77. '<?xml version="1.0" encoding="UTF-8"?>\n'
  78. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  79. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  80. "<url><loc>%s/location/</loc><lastmod>%s</lastmod>"
  81. "<changefreq>never</changefreq><priority>0.5</priority></url>\n"
  82. "</urlset>"
  83. ) % (
  84. self.base_url,
  85. date.today(),
  86. )
  87. self.assertXMLEqual(response.content.decode(), expected_content)
  88. def test_no_section(self):
  89. response = self.client.get("/simple/sitemap-simple2.xml")
  90. self.assertEqual(
  91. str(response.context["exception"]),
  92. "No sitemap available for section: 'simple2'",
  93. )
  94. self.assertEqual(response.status_code, 404)
  95. def test_empty_page(self):
  96. response = self.client.get("/simple/sitemap-simple.xml?p=0")
  97. self.assertEqual(str(response.context["exception"]), "Page 0 empty")
  98. self.assertEqual(response.status_code, 404)
  99. def test_page_not_int(self):
  100. response = self.client.get("/simple/sitemap-simple.xml?p=test")
  101. self.assertEqual(str(response.context["exception"]), "No page 'test'")
  102. self.assertEqual(response.status_code, 404)
  103. def test_simple_sitemap(self):
  104. "A simple sitemap can be rendered"
  105. response = self.client.get("/simple/sitemap.xml")
  106. expected_content = (
  107. '<?xml version="1.0" encoding="UTF-8"?>\n'
  108. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  109. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  110. "<url><loc>%s/location/</loc><lastmod>%s</lastmod>"
  111. "<changefreq>never</changefreq><priority>0.5</priority></url>\n"
  112. "</urlset>"
  113. ) % (
  114. self.base_url,
  115. date.today(),
  116. )
  117. self.assertXMLEqual(response.content.decode(), expected_content)
  118. @override_settings(
  119. TEMPLATES=[
  120. {
  121. "BACKEND": "django.template.backends.django.DjangoTemplates",
  122. "DIRS": [os.path.join(os.path.dirname(__file__), "templates")],
  123. }
  124. ]
  125. )
  126. def test_simple_custom_sitemap(self):
  127. "A simple sitemap can be rendered with a custom template"
  128. response = self.client.get("/simple/custom-sitemap.xml")
  129. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  130. <!-- This is a customised template -->
  131. <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  132. <url><loc>%s/location/</loc><lastmod>%s</lastmod><changefreq>never</changefreq><priority>0.5</priority></url>
  133. </urlset>
  134. """ % (
  135. self.base_url,
  136. date.today(),
  137. )
  138. self.assertXMLEqual(response.content.decode(), expected_content)
  139. def test_sitemap_last_modified(self):
  140. "Last-Modified header is set correctly"
  141. response = self.client.get("/lastmod/sitemap.xml")
  142. self.assertEqual(
  143. response.headers["Last-Modified"], "Wed, 13 Mar 2013 10:00:00 GMT"
  144. )
  145. def test_sitemap_last_modified_date(self):
  146. """
  147. The Last-Modified header should be support dates (without time).
  148. """
  149. response = self.client.get("/lastmod/date-sitemap.xml")
  150. self.assertEqual(
  151. response.headers["Last-Modified"], "Wed, 13 Mar 2013 00:00:00 GMT"
  152. )
  153. def test_sitemap_last_modified_tz(self):
  154. """
  155. The Last-Modified header should be converted from timezone aware dates
  156. to GMT.
  157. """
  158. response = self.client.get("/lastmod/tz-sitemap.xml")
  159. self.assertEqual(
  160. response.headers["Last-Modified"], "Wed, 13 Mar 2013 15:00:00 GMT"
  161. )
  162. def test_sitemap_last_modified_missing(self):
  163. "Last-Modified header is missing when sitemap has no lastmod"
  164. response = self.client.get("/generic/sitemap.xml")
  165. self.assertFalse(response.has_header("Last-Modified"))
  166. def test_sitemap_last_modified_mixed(self):
  167. "Last-Modified header is omitted when lastmod not on all items"
  168. response = self.client.get("/lastmod-mixed/sitemap.xml")
  169. self.assertFalse(response.has_header("Last-Modified"))
  170. def test_sitemaps_lastmod_mixed_ascending_last_modified_missing(self):
  171. """
  172. The Last-Modified header is omitted when lastmod isn't found in all
  173. sitemaps. Test sitemaps are sorted by lastmod in ascending order.
  174. """
  175. response = self.client.get("/lastmod-sitemaps/mixed-ascending.xml")
  176. self.assertFalse(response.has_header("Last-Modified"))
  177. def test_sitemaps_lastmod_mixed_descending_last_modified_missing(self):
  178. """
  179. The Last-Modified header is omitted when lastmod isn't found in all
  180. sitemaps. Test sitemaps are sorted by lastmod in descending order.
  181. """
  182. response = self.client.get("/lastmod-sitemaps/mixed-descending.xml")
  183. self.assertFalse(response.has_header("Last-Modified"))
  184. def test_sitemaps_lastmod_ascending(self):
  185. """
  186. The Last-Modified header is set to the most recent sitemap lastmod.
  187. Test sitemaps are sorted by lastmod in ascending order.
  188. """
  189. response = self.client.get("/lastmod-sitemaps/ascending.xml")
  190. self.assertEqual(
  191. response.headers["Last-Modified"], "Sat, 20 Apr 2013 05:00:00 GMT"
  192. )
  193. def test_sitemaps_lastmod_descending(self):
  194. """
  195. The Last-Modified header is set to the most recent sitemap lastmod.
  196. Test sitemaps are sorted by lastmod in descending order.
  197. """
  198. response = self.client.get("/lastmod-sitemaps/descending.xml")
  199. self.assertEqual(
  200. response.headers["Last-Modified"], "Sat, 20 Apr 2013 05:00:00 GMT"
  201. )
  202. def test_sitemap_get_latest_lastmod_none(self):
  203. """
  204. sitemapindex.lastmod is omitted when Sitemap.lastmod is
  205. callable and Sitemap.get_latest_lastmod is not implemented
  206. """
  207. response = self.client.get("/lastmod/get-latest-lastmod-none-sitemap.xml")
  208. self.assertNotContains(response, "<lastmod>")
  209. def test_sitemap_get_latest_lastmod(self):
  210. """
  211. sitemapindex.lastmod is included when Sitemap.lastmod is
  212. attribute and Sitemap.get_latest_lastmod is implemented
  213. """
  214. response = self.client.get("/lastmod/get-latest-lastmod-sitemap.xml")
  215. self.assertContains(response, "<lastmod>2013-03-13T10:00:00</lastmod>")
  216. def test_sitemap_latest_lastmod_timezone(self):
  217. """
  218. lastmod datestamp shows timezones if Sitemap.get_latest_lastmod
  219. returns an aware datetime.
  220. """
  221. response = self.client.get("/lastmod/latest-lastmod-timezone-sitemap.xml")
  222. self.assertContains(response, "<lastmod>2013-03-13T10:00:00-05:00</lastmod>")
  223. def test_localized_priority(self):
  224. """The priority value should not be localized."""
  225. with translation.override("fr"):
  226. self.assertEqual("0,3", localize(0.3))
  227. # Priorities aren't rendered in localized format.
  228. response = self.client.get("/simple/sitemap.xml")
  229. self.assertContains(response, "<priority>0.5</priority>")
  230. self.assertContains(response, "<lastmod>%s</lastmod>" % date.today())
  231. @modify_settings(INSTALLED_APPS={"remove": "django.contrib.sites"})
  232. def test_requestsite_sitemap(self):
  233. # Hitting the flatpages sitemap without the sites framework installed
  234. # doesn't raise an exception.
  235. response = self.client.get("/simple/sitemap.xml")
  236. expected_content = (
  237. '<?xml version="1.0" encoding="UTF-8"?>\n'
  238. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  239. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  240. "<url><loc>http://testserver/location/</loc><lastmod>%s</lastmod>"
  241. "<changefreq>never</changefreq><priority>0.5</priority></url>\n"
  242. "</urlset>"
  243. ) % date.today()
  244. self.assertXMLEqual(response.content.decode(), expected_content)
  245. @ignore_warnings(category=RemovedInDjango50Warning)
  246. def test_sitemap_get_urls_no_site_1(self):
  247. """
  248. Check we get ImproperlyConfigured if we don't pass a site object to
  249. Sitemap.get_urls and no Site objects exist
  250. """
  251. Site.objects.all().delete()
  252. with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg):
  253. Sitemap().get_urls()
  254. @modify_settings(INSTALLED_APPS={"remove": "django.contrib.sites"})
  255. @ignore_warnings(category=RemovedInDjango50Warning)
  256. def test_sitemap_get_urls_no_site_2(self):
  257. """
  258. Check we get ImproperlyConfigured when we don't pass a site object to
  259. Sitemap.get_urls if Site objects exists, but the sites framework is not
  260. actually installed.
  261. """
  262. with self.assertRaisesMessage(ImproperlyConfigured, self.use_sitemap_err_msg):
  263. Sitemap().get_urls()
  264. @ignore_warnings(category=RemovedInDjango50Warning)
  265. def test_sitemap_item(self):
  266. """
  267. Check to make sure that the raw item is included with each
  268. Sitemap.get_url() url result.
  269. """
  270. test_sitemap = Sitemap()
  271. test_sitemap.items = TestModel.objects.order_by("pk").all
  272. def is_testmodel(url):
  273. return isinstance(url["item"], TestModel)
  274. item_in_url_info = all(map(is_testmodel, test_sitemap.get_urls()))
  275. self.assertTrue(item_in_url_info)
  276. def test_cached_sitemap_index(self):
  277. """
  278. A cached sitemap index can be rendered (#2713).
  279. """
  280. response = self.client.get("/cached/index.xml")
  281. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  282. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  283. <sitemap><loc>%s/cached/sitemap-simple.xml</loc><lastmod>%s</lastmod></sitemap>
  284. </sitemapindex>
  285. """ % (
  286. self.base_url,
  287. date.today(),
  288. )
  289. self.assertXMLEqual(response.content.decode(), expected_content)
  290. def test_x_robots_sitemap(self):
  291. response = self.client.get("/simple/index.xml")
  292. self.assertEqual(response.headers["X-Robots-Tag"], "noindex, noodp, noarchive")
  293. response = self.client.get("/simple/sitemap.xml")
  294. self.assertEqual(response.headers["X-Robots-Tag"], "noindex, noodp, noarchive")
  295. def test_empty_sitemap(self):
  296. response = self.client.get("/empty/sitemap.xml")
  297. self.assertEqual(response.status_code, 200)
  298. @override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
  299. def test_simple_i18n_sitemap_index(self):
  300. """
  301. A simple i18n sitemap index can be rendered, without logging variable
  302. lookup errors.
  303. """
  304. with self.assertNoLogs("django.template", "DEBUG"):
  305. response = self.client.get("/simple/i18n.xml")
  306. expected_content = (
  307. '<?xml version="1.0" encoding="UTF-8"?>\n'
  308. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  309. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  310. "<url><loc>{0}/en/i18n/testmodel/{1}/</loc><changefreq>never</changefreq>"
  311. "<priority>0.5</priority></url><url><loc>{0}/pt/i18n/testmodel/{1}/</loc>"
  312. "<changefreq>never</changefreq><priority>0.5</priority></url>\n"
  313. "</urlset>"
  314. ).format(self.base_url, self.i18n_model.pk)
  315. self.assertXMLEqual(response.content.decode(), expected_content)
  316. @override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
  317. def test_alternate_i18n_sitemap_index(self):
  318. """
  319. A i18n sitemap with alternate/hreflang links can be rendered.
  320. """
  321. response = self.client.get("/alternates/i18n.xml")
  322. url, pk = self.base_url, self.i18n_model.pk
  323. expected_urls = f"""
  324. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  325. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  326. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  327. </url>
  328. <url><loc>{url}/pt/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  329. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  330. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  331. </url>
  332. """.replace(
  333. "\n", ""
  334. )
  335. expected_content = (
  336. f'<?xml version="1.0" encoding="UTF-8"?>\n'
  337. f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  338. f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  339. f"{expected_urls}\n"
  340. f"</urlset>"
  341. )
  342. self.assertXMLEqual(response.content.decode(), expected_content)
  343. @override_settings(
  344. LANGUAGES=(("en", "English"), ("pt", "Portuguese"), ("es", "Spanish"))
  345. )
  346. def test_alternate_i18n_sitemap_limited(self):
  347. """
  348. A i18n sitemap index with limited languages can be rendered.
  349. """
  350. response = self.client.get("/limited/i18n.xml")
  351. url, pk = self.base_url, self.i18n_model.pk
  352. expected_urls = f"""
  353. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  354. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  355. <xhtml:link rel="alternate" hreflang="es" href="{url}/es/i18n/testmodel/{pk}/"/>
  356. </url>
  357. <url><loc>{url}/es/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  358. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  359. <xhtml:link rel="alternate" hreflang="es" href="{url}/es/i18n/testmodel/{pk}/"/>
  360. </url>
  361. """.replace(
  362. "\n", ""
  363. )
  364. expected_content = (
  365. f'<?xml version="1.0" encoding="UTF-8"?>\n'
  366. f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  367. f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  368. f"{expected_urls}\n"
  369. f"</urlset>"
  370. )
  371. self.assertXMLEqual(response.content.decode(), expected_content)
  372. @override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
  373. def test_alternate_i18n_sitemap_xdefault(self):
  374. """
  375. A i18n sitemap index with x-default can be rendered.
  376. """
  377. response = self.client.get("/x-default/i18n.xml")
  378. url, pk = self.base_url, self.i18n_model.pk
  379. expected_urls = f"""
  380. <url><loc>{url}/en/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  381. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  382. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  383. <xhtml:link rel="alternate" hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/>
  384. </url>
  385. <url><loc>{url}/pt/i18n/testmodel/{pk}/</loc><changefreq>never</changefreq><priority>0.5</priority>
  386. <xhtml:link rel="alternate" hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>
  387. <xhtml:link rel="alternate" hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>
  388. <xhtml:link rel="alternate" hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/>
  389. </url>
  390. """.replace(
  391. "\n", ""
  392. )
  393. expected_content = (
  394. f'<?xml version="1.0" encoding="UTF-8"?>\n'
  395. f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  396. f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  397. f"{expected_urls}\n"
  398. f"</urlset>"
  399. )
  400. self.assertXMLEqual(response.content.decode(), expected_content)
  401. @override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
  402. def test_language_for_item_i18n_sitemap(self):
  403. """
  404. A i18n sitemap index in which item can be chosen to be displayed for a
  405. lang or not.
  406. """
  407. only_pt = I18nTestModel.objects.create(name="Only for PT")
  408. response = self.client.get("/item-by-lang/i18n.xml")
  409. url, pk, only_pt_pk = self.base_url, self.i18n_model.pk, only_pt.pk
  410. expected_urls = (
  411. f"<url><loc>{url}/en/i18n/testmodel/{pk}/</loc>"
  412. f"<changefreq>never</changefreq><priority>0.5</priority></url>"
  413. f"<url><loc>{url}/pt/i18n/testmodel/{pk}/</loc>"
  414. f"<changefreq>never</changefreq><priority>0.5</priority></url>"
  415. f"<url><loc>{url}/pt/i18n/testmodel/{only_pt_pk}/</loc>"
  416. f"<changefreq>never</changefreq><priority>0.5</priority></url>"
  417. )
  418. expected_content = (
  419. f'<?xml version="1.0" encoding="UTF-8"?>\n'
  420. f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  421. f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  422. f"{expected_urls}\n"
  423. f"</urlset>"
  424. )
  425. self.assertXMLEqual(response.content.decode(), expected_content)
  426. @override_settings(LANGUAGES=(("en", "English"), ("pt", "Portuguese")))
  427. def test_alternate_language_for_item_i18n_sitemap(self):
  428. """
  429. A i18n sitemap index in which item can be chosen to be displayed for a
  430. lang or not.
  431. """
  432. only_pt = I18nTestModel.objects.create(name="Only for PT")
  433. response = self.client.get("/item-by-lang-alternates/i18n.xml")
  434. url, pk, only_pt_pk = self.base_url, self.i18n_model.pk, only_pt.pk
  435. expected_urls = (
  436. f"<url><loc>{url}/en/i18n/testmodel/{pk}/</loc>"
  437. f"<changefreq>never</changefreq><priority>0.5</priority>"
  438. f'<xhtml:link rel="alternate" '
  439. f'hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>'
  440. f'<xhtml:link rel="alternate" '
  441. f'hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>'
  442. f'<xhtml:link rel="alternate" '
  443. f'hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/></url>'
  444. f"<url><loc>{url}/pt/i18n/testmodel/{pk}/</loc>"
  445. f"<changefreq>never</changefreq><priority>0.5</priority>"
  446. f'<xhtml:link rel="alternate" '
  447. f'hreflang="en" href="{url}/en/i18n/testmodel/{pk}/"/>'
  448. f'<xhtml:link rel="alternate" '
  449. f'hreflang="pt" href="{url}/pt/i18n/testmodel/{pk}/"/>'
  450. f'<xhtml:link rel="alternate" '
  451. f'hreflang="x-default" href="{url}/i18n/testmodel/{pk}/"/></url>'
  452. f"<url><loc>{url}/pt/i18n/testmodel/{only_pt_pk}/</loc>"
  453. f"<changefreq>never</changefreq><priority>0.5</priority>"
  454. f'<xhtml:link rel="alternate" '
  455. f'hreflang="pt" href="{url}/pt/i18n/testmodel/{only_pt_pk}/"/></url>'
  456. )
  457. expected_content = (
  458. f'<?xml version="1.0" encoding="UTF-8"?>\n'
  459. f'<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  460. f'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  461. f"{expected_urls}\n"
  462. f"</urlset>"
  463. )
  464. self.assertXMLEqual(response.content.decode(), expected_content)
  465. def test_sitemap_without_entries(self):
  466. response = self.client.get("/sitemap-without-entries/sitemap.xml")
  467. expected_content = (
  468. '<?xml version="1.0" encoding="UTF-8"?>\n'
  469. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  470. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n\n'
  471. "</urlset>"
  472. )
  473. self.assertXMLEqual(response.content.decode(), expected_content)
  474. def test_callable_sitemod_partial(self):
  475. """
  476. Not all items have `lastmod`. Therefore the `Last-Modified` header
  477. is not set by the detail or index sitemap view.
  478. """
  479. index_response = self.client.get("/callable-lastmod-partial/index.xml")
  480. sitemap_response = self.client.get("/callable-lastmod-partial/sitemap.xml")
  481. self.assertNotIn("Last-Modified", index_response)
  482. self.assertNotIn("Last-Modified", sitemap_response)
  483. expected_content_index = """<?xml version="1.0" encoding="UTF-8"?>
  484. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  485. <sitemap><loc>http://example.com/simple/sitemap-callable-lastmod.xml</loc></sitemap>
  486. </sitemapindex>
  487. """
  488. expected_content_sitemap = (
  489. '<?xml version="1.0" encoding="UTF-8"?>\n'
  490. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  491. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  492. "<url><loc>http://example.com/location/</loc>"
  493. "<lastmod>2013-03-13</lastmod></url><url>"
  494. "<loc>http://example.com/location/</loc></url>\n"
  495. "</urlset>"
  496. )
  497. self.assertXMLEqual(index_response.content.decode(), expected_content_index)
  498. self.assertXMLEqual(sitemap_response.content.decode(), expected_content_sitemap)
  499. def test_callable_sitemod_full(self):
  500. """
  501. All items in the sitemap have `lastmod`. The `Last-Modified` header
  502. is set for the detail and index sitemap view.
  503. """
  504. index_response = self.client.get("/callable-lastmod-full/index.xml")
  505. sitemap_response = self.client.get("/callable-lastmod-full/sitemap.xml")
  506. self.assertEqual(
  507. index_response.headers["Last-Modified"], "Thu, 13 Mar 2014 10:00:00 GMT"
  508. )
  509. self.assertEqual(
  510. sitemap_response.headers["Last-Modified"], "Thu, 13 Mar 2014 10:00:00 GMT"
  511. )
  512. expected_content_index = """<?xml version="1.0" encoding="UTF-8"?>
  513. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  514. <sitemap><loc>http://example.com/simple/sitemap-callable-lastmod.xml</loc><lastmod>2014-03-13T10:00:00</lastmod></sitemap>
  515. </sitemapindex>
  516. """
  517. expected_content_sitemap = (
  518. '<?xml version="1.0" encoding="UTF-8"?>\n'
  519. '<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" '
  520. 'xmlns:xhtml="http://www.w3.org/1999/xhtml">\n'
  521. "<url><loc>http://example.com/location/</loc>"
  522. "<lastmod>2013-03-13</lastmod></url>"
  523. "<url><loc>http://example.com/location/</loc>"
  524. "<lastmod>2014-03-13</lastmod></url>\n"
  525. "</urlset>"
  526. )
  527. self.assertXMLEqual(index_response.content.decode(), expected_content_index)
  528. self.assertXMLEqual(sitemap_response.content.decode(), expected_content_sitemap)
  529. def test_callable_sitemod_no_items(self):
  530. index_response = self.client.get("/callable-lastmod-no-items/index.xml")
  531. self.assertNotIn("Last-Modified", index_response)
  532. expected_content_index = """<?xml version="1.0" encoding="UTF-8"?>
  533. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  534. <sitemap><loc>http://example.com/simple/sitemap-callable-lastmod.xml</loc></sitemap>
  535. </sitemapindex>
  536. """
  537. self.assertXMLEqual(index_response.content.decode(), expected_content_index)
  538. # RemovedInDjango50Warning
  539. class DeprecatedTests(SitemapTestsBase):
  540. @override_settings(
  541. TEMPLATES=[
  542. {
  543. "BACKEND": "django.template.backends.django.DjangoTemplates",
  544. "DIRS": [os.path.join(os.path.dirname(__file__), "templates")],
  545. }
  546. ]
  547. )
  548. def test_simple_sitemap_custom_index_warning(self):
  549. msg = (
  550. "Calling `__str__` on SitemapIndexItem is deprecated, use the `location` "
  551. "attribute instead."
  552. )
  553. with self.assertRaisesMessage(RemovedInDjango50Warning, msg):
  554. self.client.get("/simple/custom-index.xml")
  555. @ignore_warnings(category=RemovedInDjango50Warning)
  556. @override_settings(
  557. TEMPLATES=[
  558. {
  559. "BACKEND": "django.template.backends.django.DjangoTemplates",
  560. "DIRS": [os.path.join(os.path.dirname(__file__), "templates")],
  561. }
  562. ]
  563. )
  564. def test_simple_sitemap_custom_index(self):
  565. "A simple sitemap index can be rendered with a custom template"
  566. response = self.client.get("/simple/custom-index.xml")
  567. expected_content = """<?xml version="1.0" encoding="UTF-8"?>
  568. <!-- This is a customised template -->
  569. <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  570. <sitemap><loc>%s/simple/sitemap-simple.xml</loc></sitemap>
  571. </sitemapindex>
  572. """ % (
  573. self.base_url
  574. )
  575. self.assertXMLEqual(response.content.decode(), expected_content)