/tests/regressiontests/syndication/tests.py
Python | 364 lines | 287 code | 36 blank | 41 comment | 23 complexity | 96bc08473119b3a96d1c9f17fcb5d749 MD5 | raw file
1import datetime 2import warnings 3from xml.dom import minidom 4 5from django.contrib.syndication import feeds, views 6from django.core.exceptions import ImproperlyConfigured 7from django.test import TestCase 8from django.utils import tzinfo 9from django.utils.feedgenerator import rfc2822_date, rfc3339_date 10 11from models import Entry 12 13 14class FeedTestCase(TestCase): 15 fixtures = ['feeddata.json'] 16 17 def assertChildNodes(self, elem, expected): 18 actual = set([n.nodeName for n in elem.childNodes]) 19 expected = set(expected) 20 self.assertEqual(actual, expected) 21 22 def assertChildNodeContent(self, elem, expected): 23 for k, v in expected.items(): 24 self.assertEqual( 25 elem.getElementsByTagName(k)[0].firstChild.wholeText, v) 26 27 def assertCategories(self, elem, expected): 28 self.assertEqual(set(i.firstChild.wholeText for i in elem.childNodes if i.nodeName == 'category'), set(expected)); 29 30###################################### 31# Feed view 32###################################### 33 34class SyndicationFeedTest(FeedTestCase): 35 """ 36 Tests for the high-level syndication feed framework. 37 """ 38 39 def test_rss2_feed(self): 40 """ 41 Test the structure and content of feeds generated by Rss201rev2Feed. 42 """ 43 response = self.client.get('/syndication/rss2/') 44 doc = minidom.parseString(response.content) 45 46 # Making sure there's only 1 `rss` element and that the correct 47 # RSS version was specified. 48 feed_elem = doc.getElementsByTagName('rss') 49 self.assertEqual(len(feed_elem), 1) 50 feed = feed_elem[0] 51 self.assertEqual(feed.getAttribute('version'), '2.0') 52 53 # Making sure there's only one `channel` element w/in the 54 # `rss` element. 55 chan_elem = feed.getElementsByTagName('channel') 56 self.assertEqual(len(chan_elem), 1) 57 chan = chan_elem[0] 58 59 # Find the last build date 60 d = Entry.objects.latest('date').date 61 ltz = tzinfo.LocalTimezone(d) 62 last_build_date = rfc2822_date(d.replace(tzinfo=ltz)) 63 64 self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item', 'atom:link', 'ttl', 'copyright', 'category']) 65 self.assertChildNodeContent(chan, { 66 'title': 'My blog', 67 'description': 'A more thorough description of my blog.', 68 'link': 'http://example.com/blog/', 69 'language': 'en', 70 'lastBuildDate': last_build_date, 71 #'atom:link': '', 72 'ttl': '600', 73 'copyright': 'Copyright (c) 2007, Sally Smith', 74 }) 75 self.assertCategories(chan, ['python', 'django']); 76 77 # Ensure the content of the channel is correct 78 self.assertChildNodeContent(chan, { 79 'title': 'My blog', 80 'link': 'http://example.com/blog/', 81 }) 82 83 # Check feed_url is passed 84 self.assertEqual( 85 chan.getElementsByTagName('atom:link')[0].getAttribute('href'), 86 'http://example.com/syndication/rss2/' 87 ) 88 89 # Find the pubdate of the first feed item 90 d = Entry.objects.get(pk=1).date 91 ltz = tzinfo.LocalTimezone(d) 92 pub_date = rfc2822_date(d.replace(tzinfo=ltz)) 93 94 items = chan.getElementsByTagName('item') 95 self.assertEqual(len(items), Entry.objects.count()) 96 self.assertChildNodeContent(items[0], { 97 'title': 'My first entry', 98 'description': 'Overridden description: My first entry', 99 'link': 'http://example.com/blog/1/', 100 'guid': 'http://example.com/blog/1/', 101 'pubDate': pub_date, 102 'author': 'test@example.com (Sally Smith)', 103 }) 104 self.assertCategories(items[0], ['python', 'testing']); 105 106 for item in items: 107 self.assertChildNodes(item, ['title', 'link', 'description', 'guid', 'category', 'pubDate', 'author']) 108 109 def test_rss091_feed(self): 110 """ 111 Test the structure and content of feeds generated by RssUserland091Feed. 112 """ 113 response = self.client.get('/syndication/rss091/') 114 doc = minidom.parseString(response.content) 115 116 # Making sure there's only 1 `rss` element and that the correct 117 # RSS version was specified. 118 feed_elem = doc.getElementsByTagName('rss') 119 self.assertEqual(len(feed_elem), 1) 120 feed = feed_elem[0] 121 self.assertEqual(feed.getAttribute('version'), '0.91') 122 123 # Making sure there's only one `channel` element w/in the 124 # `rss` element. 125 chan_elem = feed.getElementsByTagName('channel') 126 self.assertEqual(len(chan_elem), 1) 127 chan = chan_elem[0] 128 self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item', 'atom:link', 'ttl', 'copyright', 'category']) 129 130 # Ensure the content of the channel is correct 131 self.assertChildNodeContent(chan, { 132 'title': 'My blog', 133 'link': 'http://example.com/blog/', 134 }) 135 self.assertCategories(chan, ['python', 'django']) 136 137 # Check feed_url is passed 138 self.assertEqual( 139 chan.getElementsByTagName('atom:link')[0].getAttribute('href'), 140 'http://example.com/syndication/rss091/' 141 ) 142 143 items = chan.getElementsByTagName('item') 144 self.assertEqual(len(items), Entry.objects.count()) 145 self.assertChildNodeContent(items[0], { 146 'title': 'My first entry', 147 'description': 'Overridden description: My first entry', 148 'link': 'http://example.com/blog/1/', 149 }) 150 for item in items: 151 self.assertChildNodes(item, ['title', 'link', 'description']) 152 self.assertCategories(item, []) 153 154 def test_atom_feed(self): 155 """ 156 Test the structure and content of feeds generated by Atom1Feed. 157 """ 158 response = self.client.get('/syndication/atom/') 159 feed = minidom.parseString(response.content).firstChild 160 161 self.assertEqual(feed.nodeName, 'feed') 162 self.assertEqual(feed.getAttribute('xmlns'), 'http://www.w3.org/2005/Atom') 163 self.assertChildNodes(feed, ['title', 'subtitle', 'link', 'id', 'updated', 'entry', 'rights', 'category', 'author']) 164 for link in feed.getElementsByTagName('link'): 165 if link.getAttribute('rel') == 'self': 166 self.assertEqual(link.getAttribute('href'), 'http://example.com/syndication/atom/') 167 168 entries = feed.getElementsByTagName('entry') 169 self.assertEqual(len(entries), Entry.objects.count()) 170 for entry in entries: 171 self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'category', 'updated', 'rights', 'author']) 172 summary = entry.getElementsByTagName('summary')[0] 173 self.assertEqual(summary.getAttribute('type'), 'html') 174 175 def test_custom_feed_generator(self): 176 response = self.client.get('/syndication/custom/') 177 feed = minidom.parseString(response.content).firstChild 178 179 self.assertEqual(feed.nodeName, 'feed') 180 self.assertEqual(feed.getAttribute('django'), 'rocks') 181 self.assertChildNodes(feed, ['title', 'subtitle', 'link', 'id', 'updated', 'entry', 'spam', 'rights', 'category', 'author']) 182 183 entries = feed.getElementsByTagName('entry') 184 self.assertEqual(len(entries), Entry.objects.count()) 185 for entry in entries: 186 self.assertEqual(entry.getAttribute('bacon'), 'yum') 187 self.assertChildNodes(entry, ['title', 'link', 'id', 'summary', 'ministry', 'rights', 'author', 'updated', 'category']) 188 summary = entry.getElementsByTagName('summary')[0] 189 self.assertEqual(summary.getAttribute('type'), 'html') 190 191 def test_title_escaping(self): 192 """ 193 Tests that titles are escaped correctly in RSS feeds. 194 """ 195 response = self.client.get('/syndication/rss2/') 196 doc = minidom.parseString(response.content) 197 for item in doc.getElementsByTagName('item'): 198 link = item.getElementsByTagName('link')[0] 199 if link.firstChild.wholeText == 'http://example.com/blog/4/': 200 title = item.getElementsByTagName('title')[0] 201 self.assertEqual(title.firstChild.wholeText, u'A & B < C > D') 202 203 def test_naive_datetime_conversion(self): 204 """ 205 Test that datetimes are correctly converted to the local time zone. 206 """ 207 # Naive date times passed in get converted to the local time zone, so 208 # check the recived zone offset against the local offset. 209 response = self.client.get('/syndication/naive-dates/') 210 doc = minidom.parseString(response.content) 211 updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText 212 213 d = Entry.objects.latest('date').date 214 ltz = tzinfo.LocalTimezone(d) 215 latest = rfc3339_date(d.replace(tzinfo=ltz)) 216 217 self.assertEqual(updated, latest) 218 219 def test_aware_datetime_conversion(self): 220 """ 221 Test that datetimes with timezones don't get trodden on. 222 """ 223 response = self.client.get('/syndication/aware-dates/') 224 doc = minidom.parseString(response.content) 225 updated = doc.getElementsByTagName('updated')[0].firstChild.wholeText 226 self.assertEqual(updated[-6:], '+00:42') 227 228 def test_feed_url(self): 229 """ 230 Test that the feed_url can be overridden. 231 """ 232 response = self.client.get('/syndication/feedurl/') 233 doc = minidom.parseString(response.content) 234 for link in doc.getElementsByTagName('link'): 235 if link.getAttribute('rel') == 'self': 236 self.assertEqual(link.getAttribute('href'), 'http://example.com/customfeedurl/') 237 238 def test_secure_urls(self): 239 """ 240 Test URLs are prefixed with https:// when feed is requested over HTTPS. 241 """ 242 response = self.client.get('/syndication/rss2/', **{ 243 'wsgi.url_scheme': 'https', 244 }) 245 doc = minidom.parseString(response.content) 246 chan = doc.getElementsByTagName('channel')[0] 247 self.assertEqual( 248 chan.getElementsByTagName('link')[0].firstChild.wholeText[0:5], 249 'https' 250 ) 251 atom_link = chan.getElementsByTagName('atom:link')[0] 252 self.assertEqual(atom_link.getAttribute('href')[0:5], 'https') 253 for link in doc.getElementsByTagName('link'): 254 if link.getAttribute('rel') == 'self': 255 self.assertEqual(link.getAttribute('href')[0:5], 'https') 256 257 def test_item_link_error(self): 258 """ 259 Test that a ImproperlyConfigured is raised if no link could be found 260 for the item(s). 261 """ 262 self.assertRaises(ImproperlyConfigured, 263 self.client.get, 264 '/syndication/articles/') 265 266 def test_template_feed(self): 267 """ 268 Test that the item title and description can be overridden with 269 templates. 270 """ 271 response = self.client.get('/syndication/template/') 272 doc = minidom.parseString(response.content) 273 feed = doc.getElementsByTagName('rss')[0] 274 chan = feed.getElementsByTagName('channel')[0] 275 items = chan.getElementsByTagName('item') 276 277 self.assertChildNodeContent(items[0], { 278 'title': 'Title in your templates: My first entry', 279 'description': 'Description in your templates: My first entry', 280 'link': 'http://example.com/blog/1/', 281 }) 282 283 def test_add_domain(self): 284 """ 285 Test add_domain() prefixes domains onto the correct URLs. 286 """ 287 self.assertEqual( 288 views.add_domain('example.com', '/foo/?arg=value'), 289 'http://example.com/foo/?arg=value' 290 ) 291 self.assertEqual( 292 views.add_domain('example.com', '/foo/?arg=value', True), 293 'https://example.com/foo/?arg=value' 294 ) 295 self.assertEqual( 296 views.add_domain('example.com', 'http://djangoproject.com/doc/'), 297 'http://djangoproject.com/doc/' 298 ) 299 self.assertEqual( 300 views.add_domain('example.com', 'https://djangoproject.com/doc/'), 301 'https://djangoproject.com/doc/' 302 ) 303 self.assertEqual( 304 views.add_domain('example.com', 'mailto:uhoh@djangoproject.com'), 305 'mailto:uhoh@djangoproject.com' 306 ) 307 308 309###################################### 310# Deprecated feeds 311###################################### 312 313class DeprecatedSyndicationFeedTest(FeedTestCase): 314 """ 315 Tests for the deprecated API (feed() view and the feed_dict etc). 316 """ 317 def setUp(self): 318 self.save_warnings_state() 319 warnings.filterwarnings('ignore', category=DeprecationWarning, 320 module='django.contrib.syndication.feeds') 321 warnings.filterwarnings('ignore', category=DeprecationWarning, 322 module='django.contrib.syndication.views') 323 324 def tearDown(self): 325 self.restore_warnings_state() 326 327 def test_empty_feed_dict(self): 328 """ 329 Test that an empty feed_dict raises a 404. 330 """ 331 response = self.client.get('/syndication/depr-feeds-empty/aware-dates/') 332 self.assertEqual(response.status_code, 404) 333 334 def test_nonexistent_slug(self): 335 """ 336 Test that a non-existent slug raises a 404. 337 """ 338 response = self.client.get('/syndication/depr-feeds/foobar/') 339 self.assertEqual(response.status_code, 404) 340 341 def test_rss_feed(self): 342 """ 343 A simple test for Rss201rev2Feed feeds generated by the deprecated 344 system. 345 """ 346 response = self.client.get('/syndication/depr-feeds/rss/') 347 doc = minidom.parseString(response.content) 348 feed = doc.getElementsByTagName('rss')[0] 349 self.assertEqual(feed.getAttribute('version'), '2.0') 350 351 chan = feed.getElementsByTagName('channel')[0] 352 self.assertChildNodes(chan, ['title', 'link', 'description', 'language', 'lastBuildDate', 'item', 'atom:link']) 353 354 items = chan.getElementsByTagName('item') 355 self.assertEqual(len(items), Entry.objects.count()) 356 357 def test_complex_base_url(self): 358 """ 359 Tests that the base url for a complex feed doesn't raise a 500 360 exception. 361 """ 362 response = self.client.get('/syndication/depr-feeds/complex/') 363 self.assertEqual(response.status_code, 404) 364