From fbc467c26bc0adb9867b97d0bb5642b2a85eb357 Mon Sep 17 00:00:00 2001 From: Tim Graham Date: Tue, 10 Feb 2015 08:11:25 -0500 Subject: Moved contrib.sitemaps tests out of contrib. --- tests/sitemaps_tests/__init__.py | 0 tests/sitemaps_tests/base.py | 20 ++ tests/sitemaps_tests/models.py | 16 ++ tests/sitemaps_tests/templates/custom_sitemap.xml | 14 ++ .../templates/custom_sitemap_index.xml | 5 + tests/sitemaps_tests/test_generic.py | 23 +++ tests/sitemaps_tests/test_http.py | 224 +++++++++++++++++++++ tests/sitemaps_tests/test_https.py | 72 +++++++ tests/sitemaps_tests/urls/__init__.py | 0 tests/sitemaps_tests/urls/http.py | 137 +++++++++++++ tests/sitemaps_tests/urls/https.py | 19 ++ 11 files changed, 530 insertions(+) create mode 100644 tests/sitemaps_tests/__init__.py create mode 100644 tests/sitemaps_tests/base.py create mode 100644 tests/sitemaps_tests/models.py create mode 100644 tests/sitemaps_tests/templates/custom_sitemap.xml create mode 100644 tests/sitemaps_tests/templates/custom_sitemap_index.xml create mode 100644 tests/sitemaps_tests/test_generic.py create mode 100644 tests/sitemaps_tests/test_http.py create mode 100644 tests/sitemaps_tests/test_https.py create mode 100644 tests/sitemaps_tests/urls/__init__.py create mode 100644 tests/sitemaps_tests/urls/http.py create mode 100644 tests/sitemaps_tests/urls/https.py (limited to 'tests/sitemaps_tests') diff --git a/tests/sitemaps_tests/__init__.py b/tests/sitemaps_tests/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/sitemaps_tests/base.py b/tests/sitemaps_tests/base.py new file mode 100644 index 0000000000..c3476cf08e --- /dev/null +++ b/tests/sitemaps_tests/base.py @@ -0,0 +1,20 @@ +from django.apps import apps +from django.core.cache import cache +from django.test import TestCase, modify_settings, override_settings + +from .models import I18nTestModel, TestModel + + +@modify_settings(INSTALLED_APPS={'append': 'django.contrib.sitemaps'}) +@override_settings(ROOT_URLCONF='sitemaps_tests.urls.http') +class SitemapTestsBase(TestCase): + protocol = 'http' + sites_installed = apps.is_installed('django.contrib.sites') + domain = 'example.com' if sites_installed else 'testserver' + + def setUp(self): + self.base_url = '%s://%s' % (self.protocol, self.domain) + cache.clear() + # Create an object for sitemap content. + TestModel.objects.create(name='Test Object') + self.i18n_model = I18nTestModel.objects.create(name='Test Object') diff --git a/tests/sitemaps_tests/models.py b/tests/sitemaps_tests/models.py new file mode 100644 index 0000000000..c155c848ba --- /dev/null +++ b/tests/sitemaps_tests/models.py @@ -0,0 +1,16 @@ +from django.core.urlresolvers import reverse +from django.db import models + + +class TestModel(models.Model): + name = models.CharField(max_length=100) + + def get_absolute_url(self): + return '/testmodel/%s/' % self.id + + +class I18nTestModel(models.Model): + name = models.CharField(max_length=100) + + def get_absolute_url(self): + return reverse('i18n_testmodel', args=[self.id]) diff --git a/tests/sitemaps_tests/templates/custom_sitemap.xml b/tests/sitemaps_tests/templates/custom_sitemap.xml new file mode 100644 index 0000000000..594aef1a3e --- /dev/null +++ b/tests/sitemaps_tests/templates/custom_sitemap.xml @@ -0,0 +1,14 @@ + + + +{% spaceless %} +{% for url in urlset %} + + {{ url.location }} + {% if url.lastmod %}{{ url.lastmod|date:"Y-m-d" }}{% endif %} + {% if url.changefreq %}{{ url.changefreq }}{% endif %} + {% if url.priority %}{{ url.priority }}{% endif %} + +{% endfor %} +{% endspaceless %} + diff --git a/tests/sitemaps_tests/templates/custom_sitemap_index.xml b/tests/sitemaps_tests/templates/custom_sitemap_index.xml new file mode 100644 index 0000000000..406c6b7606 --- /dev/null +++ b/tests/sitemaps_tests/templates/custom_sitemap_index.xml @@ -0,0 +1,5 @@ + + + +{% for location in sitemaps %}{{ location }}{% endfor %} + diff --git a/tests/sitemaps_tests/test_generic.py b/tests/sitemaps_tests/test_generic.py new file mode 100644 index 0000000000..96736c261a --- /dev/null +++ b/tests/sitemaps_tests/test_generic.py @@ -0,0 +1,23 @@ +from __future__ import unicode_literals + +from django.test import override_settings + +from .base import SitemapTestsBase +from .models import TestModel + + +@override_settings(ABSOLUTE_URL_OVERRIDES={}) +class GenericViewsSitemapTests(SitemapTestsBase): + + def test_generic_sitemap(self): + "A minimal generic sitemap can be rendered" + response = self.client.get('/generic/sitemap.xml') + expected = '' + for pk in TestModel.objects.values_list("id", flat=True): + expected += "%s/testmodel/%s/" % (self.base_url, pk) + expected_content = """ + +%s + +""" % expected + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) diff --git a/tests/sitemaps_tests/test_http.py b/tests/sitemaps_tests/test_http.py new file mode 100644 index 0000000000..2817f844d6 --- /dev/null +++ b/tests/sitemaps_tests/test_http.py @@ -0,0 +1,224 @@ +from __future__ import unicode_literals + +import os +from datetime import date +from unittest import skipUnless + +from django.apps import apps +from django.conf import settings +from django.contrib.sitemaps import GenericSitemap, Sitemap +from django.contrib.sites.models import Site +from django.core.exceptions import ImproperlyConfigured +from django.test import ignore_warnings, modify_settings, override_settings +from django.utils._os import upath +from django.utils.deprecation import RemovedInDjango20Warning +from django.utils.formats import localize +from django.utils.translation import activate, deactivate + +from .base import SitemapTestsBase +from .models import TestModel + + +class HTTPSitemapTests(SitemapTestsBase): + + @ignore_warnings(category=RemovedInDjango20Warning) + def test_simple_sitemap_index(self): + "A simple sitemap index can be rendered" + # The URL for views.sitemap in tests/urls/http.py has been updated + # with a name but since reversing by Python path is tried first + # before reversing by name and works since we're giving + # name='django.contrib.sitemaps.views.sitemap', we need to silence + # the erroneous warning until reversing by dotted path is removed. + # The test will work without modification when it's removed. + response = self.client.get('/simple/index.xml') + expected_content = """ + +%s/simple/sitemap-simple.xml + +""" % self.base_url + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + @ignore_warnings(category=RemovedInDjango20Warning) + @override_settings(TEMPLATES=[{ + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(os.path.dirname(upath(__file__)), 'templates')], + }]) + def test_simple_sitemap_custom_index(self): + "A simple sitemap index can be rendered with a custom template" + # The URL for views.sitemap in tests/urls/http.py has been updated + # with a name but since reversing by Python path is tried first + # before reversing by name and works since we're giving + # name='django.contrib.sitemaps.views.sitemap', we need to silence + # the erroneous warning until reversing by dotted path is removed. + # The test will work without modification when it's removed. + response = self.client.get('/simple/custom-index.xml') + expected_content = """ + + +%s/simple/sitemap-simple.xml + +""" % self.base_url + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_simple_sitemap_section(self): + "A simple sitemap section can be rendered" + response = self.client.get('/simple/sitemap-simple.xml') + expected_content = """ + +%s/location/%snever0.5 + +""" % (self.base_url, date.today()) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_simple_sitemap(self): + "A simple sitemap can be rendered" + response = self.client.get('/simple/sitemap.xml') + expected_content = """ + +%s/location/%snever0.5 + +""" % (self.base_url, date.today()) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + @override_settings(TEMPLATES=[{ + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(os.path.dirname(upath(__file__)), 'templates')], + }]) + def test_simple_custom_sitemap(self): + "A simple sitemap can be rendered with a custom template" + response = self.client.get('/simple/custom-sitemap.xml') + expected_content = """ + + +%s/location/%snever0.5 + +""" % (self.base_url, date.today()) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_sitemap_last_modified(self): + "Tests that Last-Modified header is set correctly" + response = self.client.get('/lastmod/sitemap.xml') + self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 10:00:00 GMT') + + def test_sitemap_last_modified_date(self): + """ + The Last-Modified header should be support dates (without time). + """ + response = self.client.get('/lastmod/date-sitemap.xml') + self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 00:00:00 GMT') + + def test_sitemap_last_modified_tz(self): + """ + The Last-Modified header should be converted from timezone aware dates + to GMT. + """ + response = self.client.get('/lastmod/tz-sitemap.xml') + self.assertEqual(response['Last-Modified'], 'Wed, 13 Mar 2013 15:00:00 GMT') + + def test_sitemap_last_modified_missing(self): + "Tests that Last-Modified header is missing when sitemap has no lastmod" + response = self.client.get('/generic/sitemap.xml') + self.assertFalse(response.has_header('Last-Modified')) + + def test_sitemap_last_modified_mixed(self): + "Tests that Last-Modified header is omitted when lastmod not on all items" + response = self.client.get('/lastmod-mixed/sitemap.xml') + self.assertFalse(response.has_header('Last-Modified')) + + @skipUnless(settings.USE_I18N, "Internationalization is not enabled") + @override_settings(USE_L10N=True) + def test_localized_priority(self): + "The priority value should not be localized (Refs #14164)" + activate('fr') + self.assertEqual('0,3', localize(0.3)) + + # Retrieve the sitemap. Check that priorities + # haven't been rendered in localized format + response = self.client.get('/simple/sitemap.xml') + self.assertContains(response, '0.5') + self.assertContains(response, '%s' % date.today()) + deactivate() + + @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) + def test_requestsite_sitemap(self): + # Make sure hitting the flatpages sitemap without the sites framework + # installed doesn't raise an exception. + response = self.client.get('/simple/sitemap.xml') + expected_content = """ + +http://testserver/location/%snever0.5 + +""" % date.today() + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + @skipUnless(apps.is_installed('django.contrib.sites'), + "django.contrib.sites app not installed.") + def test_sitemap_get_urls_no_site_1(self): + """ + Check we get ImproperlyConfigured if we don't pass a site object to + Sitemap.get_urls and no Site objects exist + """ + Site.objects.all().delete() + self.assertRaises(ImproperlyConfigured, Sitemap().get_urls) + + @modify_settings(INSTALLED_APPS={'remove': 'django.contrib.sites'}) + def test_sitemap_get_urls_no_site_2(self): + """ + Check we get ImproperlyConfigured when we don't pass a site object to + Sitemap.get_urls if Site objects exists, but the sites framework is not + actually installed. + """ + self.assertRaises(ImproperlyConfigured, Sitemap().get_urls) + + def test_sitemap_item(self): + """ + Check to make sure that the raw item is included with each + Sitemap.get_url() url result. + """ + test_sitemap = GenericSitemap({'queryset': TestModel.objects.all()}) + + def is_testmodel(url): + return isinstance(url['item'], TestModel) + item_in_url_info = all(map(is_testmodel, test_sitemap.get_urls())) + self.assertTrue(item_in_url_info) + + def test_cached_sitemap_index(self): + """ + Check that a cached sitemap index can be rendered (#2713). + """ + response = self.client.get('/cached/index.xml') + expected_content = """ + +%s/cached/sitemap-simple.xml + +""" % self.base_url + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + @ignore_warnings(category=RemovedInDjango20Warning) + def test_x_robots_sitemap(self): + # The URL for views.sitemap in tests/urls/http.py has been updated + # with a name but since reversing by Python path is tried first + # before reversing by name and works since we're giving + # name='django.contrib.sitemaps.views.sitemap', we need to silence + # the erroneous warning until reversing by dotted path is removed. + # The test will work without modification when it's removed. + response = self.client.get('/simple/index.xml') + self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') + + response = self.client.get('/simple/sitemap.xml') + self.assertEqual(response['X-Robots-Tag'], 'noindex, noodp, noarchive') + + def test_empty_sitemap(self): + response = self.client.get('/empty/sitemap.xml') + self.assertEqual(response.status_code, 200) + + @override_settings(LANGUAGES=(('en', 'English'), ('pt', 'Portuguese'))) + def test_simple_i18nsitemap_index(self): + "A simple i18n sitemap index can be rendered" + response = self.client.get('/simple/i18n.xml') + expected_content = """ + +{0}/en/i18n/testmodel/{1}/never0.5{0}/pt/i18n/testmodel/{1}/never0.5 + +""".format(self.base_url, self.i18n_model.pk) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) diff --git a/tests/sitemaps_tests/test_https.py b/tests/sitemaps_tests/test_https.py new file mode 100644 index 0000000000..f7b363dfe2 --- /dev/null +++ b/tests/sitemaps_tests/test_https.py @@ -0,0 +1,72 @@ +from __future__ import unicode_literals + +from datetime import date + +from django.test import ignore_warnings, override_settings +from django.utils.deprecation import RemovedInDjango20Warning + +from .base import SitemapTestsBase + + +@override_settings(ROOT_URLCONF='sitemaps_tests.urls.https') +class HTTPSSitemapTests(SitemapTestsBase): + protocol = 'https' + + @ignore_warnings(category=RemovedInDjango20Warning) + def test_secure_sitemap_index(self): + "A secure sitemap index can be rendered" + # The URL for views.sitemap in tests/urls/https.py has been updated + # with a name but since reversing by Python path is tried first + # before reversing by name and works since we're giving + # name='django.contrib.sitemaps.views.sitemap', we need to silence + # the erroneous warning until reversing by dotted path is removed. + # The test will work without modification when it's removed. + response = self.client.get('/secure/index.xml') + expected_content = """ + +%s/secure/sitemap-simple.xml + +""" % self.base_url + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_secure_sitemap_section(self): + "A secure sitemap section can be rendered" + response = self.client.get('/secure/sitemap-simple.xml') + expected_content = """ + +%s/location/%snever0.5 + +""" % (self.base_url, date.today()) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + +@override_settings(SECURE_PROXY_SSL_HEADER=False) +class HTTPSDetectionSitemapTests(SitemapTestsBase): + extra = {'wsgi.url_scheme': 'https'} + + @ignore_warnings(category=RemovedInDjango20Warning) + def test_sitemap_index_with_https_request(self): + "A sitemap index requested in HTTPS is rendered with HTTPS links" + # The URL for views.sitemap in tests/urls/https.py has been updated + # with a name but since reversing by Python path is tried first + # before reversing by name and works since we're giving + # name='django.contrib.sitemaps.views.sitemap', we need to silence + # the erroneous warning until reversing by dotted path is removed. + # The test will work without modification when it's removed. + response = self.client.get('/simple/index.xml', **self.extra) + expected_content = """ + +%s/simple/sitemap-simple.xml + +""" % self.base_url.replace('http://', 'https://') + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) + + def test_sitemap_section_with_https_request(self): + "A sitemap section requested in HTTPS is rendered with HTTPS links" + response = self.client.get('/simple/sitemap-simple.xml', **self.extra) + expected_content = """ + +%s/location/%snever0.5 + +""" % (self.base_url.replace('http://', 'https://'), date.today()) + self.assertXMLEqual(response.content.decode('utf-8'), expected_content) diff --git a/tests/sitemaps_tests/urls/__init__.py b/tests/sitemaps_tests/urls/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/sitemaps_tests/urls/http.py b/tests/sitemaps_tests/urls/http.py new file mode 100644 index 0000000000..df423c7636 --- /dev/null +++ b/tests/sitemaps_tests/urls/http.py @@ -0,0 +1,137 @@ +from datetime import date, datetime + +from django.conf.urls import url +from django.conf.urls.i18n import i18n_patterns +from django.contrib.sitemaps import GenericSitemap, Sitemap, views +from django.http import HttpResponse +from django.utils import timezone +from django.views.decorators.cache import cache_page + +from ..models import I18nTestModel, TestModel + + +class SimpleSitemap(Sitemap): + changefreq = "never" + priority = 0.5 + location = '/location/' + lastmod = datetime.now() + + def items(self): + return [object()] + + +class SimpleI18nSitemap(Sitemap): + changefreq = "never" + priority = 0.5 + i18n = True + + def items(self): + return I18nTestModel.objects.all() + + +class EmptySitemap(Sitemap): + changefreq = "never" + priority = 0.5 + location = '/location/' + + def items(self): + return [] + + +class FixedLastmodSitemap(SimpleSitemap): + lastmod = datetime(2013, 3, 13, 10, 0, 0) + + +class FixedLastmodMixedSitemap(Sitemap): + changefreq = "never" + priority = 0.5 + location = '/location/' + loop = 0 + + def items(self): + o1 = TestModel() + o1.lastmod = datetime(2013, 3, 13, 10, 0, 0) + o2 = TestModel() + return [o1, o2] + + +class DateSiteMap(SimpleSitemap): + lastmod = date(2013, 3, 13) + + +class TimezoneSiteMap(SimpleSitemap): + lastmod = datetime(2013, 3, 13, 10, 0, 0, tzinfo=timezone.get_fixed_timezone(-300)) + + +def testmodelview(request, id): + return HttpResponse() + + +simple_sitemaps = { + 'simple': SimpleSitemap, +} + +simple_i18nsitemaps = { + 'simple': SimpleI18nSitemap, +} + +empty_sitemaps = { + 'empty': EmptySitemap, +} + +fixed_lastmod_sitemaps = { + 'fixed-lastmod': FixedLastmodSitemap, +} + +fixed_lastmod__mixed_sitemaps = { + 'fixed-lastmod-mixed': FixedLastmodMixedSitemap, +} + +generic_sitemaps = { + 'generic': GenericSitemap({'queryset': TestModel.objects.all()}), +} + + +urlpatterns = [ + url(r'^simple/index\.xml$', views.index, {'sitemaps': simple_sitemaps}), + url(r'^simple/custom-index\.xml$', views.index, + {'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap_index.xml'}), + url(r'^simple/sitemap-(?P
.+)\.xml$', views.sitemap, + {'sitemaps': simple_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^simple/sitemap\.xml$', views.sitemap, + {'sitemaps': simple_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^simple/i18n\.xml$', views.sitemap, + {'sitemaps': simple_i18nsitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^simple/custom-sitemap\.xml$', views.sitemap, + {'sitemaps': simple_sitemaps, 'template_name': 'custom_sitemap.xml'}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^empty/sitemap\.xml$', views.sitemap, + {'sitemaps': empty_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^lastmod/sitemap\.xml$', views.sitemap, + {'sitemaps': fixed_lastmod_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^lastmod-mixed/sitemap\.xml$', views.sitemap, + {'sitemaps': fixed_lastmod__mixed_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^lastmod/date-sitemap.xml$', views.sitemap, + {'sitemaps': {'date-sitemap': DateSiteMap}}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^lastmod/tz-sitemap.xml$', views.sitemap, + {'sitemaps': {'tz-sitemap': TimezoneSiteMap}}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^generic/sitemap\.xml$', views.sitemap, + {'sitemaps': generic_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), + url(r'^cached/index\.xml$', cache_page(1)(views.index), + {'sitemaps': simple_sitemaps, 'sitemap_url_name': 'cached_sitemap'}), + url(r'^cached/sitemap-(?P
.+)\.xml', cache_page(1)(views.sitemap), + {'sitemaps': simple_sitemaps}, name='cached_sitemap') +] + +urlpatterns += i18n_patterns( + url(r'^i18n/testmodel/(?P\d+)/$', testmodelview, name='i18n_testmodel'), +) diff --git a/tests/sitemaps_tests/urls/https.py b/tests/sitemaps_tests/urls/https.py new file mode 100644 index 0000000000..ec4ab1489f --- /dev/null +++ b/tests/sitemaps_tests/urls/https.py @@ -0,0 +1,19 @@ +from django.conf.urls import url +from django.contrib.sitemaps import views + +from .http import SimpleSitemap + + +class HTTPSSitemap(SimpleSitemap): + protocol = 'https' + +secure_sitemaps = { + 'simple': HTTPSSitemap, +} + +urlpatterns = [ + url(r'^secure/index\.xml$', views.index, {'sitemaps': secure_sitemaps}), + url(r'^secure/sitemap-(?P
.+)\.xml$', views.sitemap, + {'sitemaps': secure_sitemaps}, + name='django.contrib.sitemaps.views.sitemap'), +] -- cgit v1.2.1