astro-feedzirra 0.0.8.20090419

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,543 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::Feed do
4
+ describe "#add_common_feed_entry_element" do
5
+ before(:all) do
6
+ Feedzirra::Feed.add_common_feed_entry_element("wfw:commentRss", :as => :comment_rss)
7
+ end
8
+
9
+ it "should parse the added element out of Atom feeds" do
10
+ Feedzirra::Feed.parse(sample_wfw_feed).entries.first.comment_rss.should == "this is the new val"
11
+ end
12
+
13
+ it "should parse the added element out of Atom Feedburner feeds" do
14
+ Feedzirra::AtomEntry.new.should respond_to(:comment_rss)
15
+ end
16
+
17
+ it "should parse the added element out of RSS feeds" do
18
+ Feedzirra::RSSEntry.new.should respond_to(:comment_rss)
19
+ end
20
+ end
21
+
22
+ describe "#parse" do # many of these tests are redundant with the specific feed type tests, but I put them here for completeness
23
+ context "when there's an available parser" do
24
+ it "should parse an rdf feed" do
25
+ feed = Feedzirra::Feed.parse(sample_rdf_feed)
26
+ feed.title.should == "HREF Considered Harmful"
27
+ feed.entries.first.published.to_s.should == "Tue Sep 02 19:50:07 UTC 2008"
28
+ feed.entries.size.should == 10
29
+ end
30
+
31
+ it "should parse an rss feed" do
32
+ feed = Feedzirra::Feed.parse(sample_rss_feed)
33
+ feed.title.should == "Tender Lovemaking"
34
+ feed.entries.first.published.to_s.should == "Thu Dec 04 17:17:49 UTC 2008"
35
+ feed.entries.size.should == 10
36
+ end
37
+
38
+ it "should parse an atom feed" do
39
+ feed = Feedzirra::Feed.parse(sample_atom_feed)
40
+ feed.title.should == "Amazon Web Services Blog"
41
+ feed.entries.first.published.to_s.should == "Fri Jan 16 18:21:00 UTC 2009"
42
+ feed.entries.size.should == 10
43
+ end
44
+
45
+ it "should parse an feedburner atom feed" do
46
+ feed = Feedzirra::Feed.parse(sample_feedburner_atom_feed)
47
+ feed.title.should == "Paul Dix Explains Nothing"
48
+ feed.entries.first.published.to_s.should == "Thu Jan 22 15:50:22 UTC 2009"
49
+ feed.entries.size.should == 5
50
+ end
51
+
52
+ it "should parse an itunes feed" do
53
+ feed = Feedzirra::Feed.parse(sample_itunes_feed)
54
+ feed.title.should == "All About Everything"
55
+ feed.entries.first.published.to_s.should == "Wed, 15 Jun 2005 19:00:00 GMT"
56
+ feed.entries.first.itunes_author.should == "John Doe"
57
+ feed.entries.size.should == 3
58
+ end
59
+ end
60
+
61
+ context "when there's no available parser" do
62
+ it "raises Feedzirra::NoParserAvailable" do
63
+ proc {
64
+ Feedzirra::Feed.parse("I'm an invalid feed")
65
+ }.should raise_error(Feedzirra::NoParserAvailable)
66
+ end
67
+ end
68
+
69
+ it "should parse an feedburner rss feed" do
70
+ feed = Feedzirra::Feed.parse(sample_rss_feed_burner_feed)
71
+ feed.title.should == "Sam Harris: Author, Philosopher, Essayist, Atheist"
72
+ feed.entries.first.published.to_s.should == "Tue Jan 13 17:20:28 UTC 2009"
73
+ feed.entries.size.should == 10
74
+ end
75
+ end
76
+
77
+ describe "#determine_feed_parser_for_xml" do
78
+ it "should return the Feedzirra::Atom class for an atom feed" do
79
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_atom_feed).should == Feedzirra::Atom
80
+ end
81
+
82
+ it "should return the Feedzirra::AtomFeedBurner class for an atom feedburner feed" do
83
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_feedburner_atom_feed).should == Feedzirra::AtomFeedBurner
84
+ end
85
+
86
+ it "should return the Feedzirra::RSS class for an rdf/rss 1.0 feed" do
87
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rdf_feed).should == Feedzirra::RSS
88
+ end
89
+
90
+ it "should return the Feedzirra::RSS class for an rss feedburner feed" do
91
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rss_feed_burner_feed).should == Feedzirra::RSS
92
+ end
93
+
94
+ it "should return the Feedzirra::RSS object for an rss 2.0 feed" do
95
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_rss_feed).should == Feedzirra::RSS
96
+ end
97
+
98
+ it "should return the Feedzirra::ITunesRSS object for an itunes feed" do
99
+ Feedzirra::Feed.determine_feed_parser_for_xml(sample_itunes_feed).should == Feedzirra::ITunesRSS
100
+ end
101
+
102
+ end
103
+
104
+ describe "when adding feed types" do
105
+ it "should prioritize added types over the built in ones" do
106
+ feed_text = "Atom asdf"
107
+ Feedzirra::Atom.should be_able_to_parse(feed_text)
108
+ new_feed_type = Class.new do
109
+ def self.able_to_parse?(val)
110
+ true
111
+ end
112
+ end
113
+
114
+ new_feed_type.should be_able_to_parse(feed_text)
115
+ Feedzirra::Feed.add_feed_class(new_feed_type)
116
+ Feedzirra::Feed.determine_feed_parser_for_xml(feed_text).should == new_feed_type
117
+
118
+ # this is a hack so that this doesn't break the rest of the tests
119
+ Feedzirra::Feed.feed_classes.reject! {|o| o == new_feed_type }
120
+ end
121
+ end
122
+
123
+ describe '#etag_from_header' do
124
+ before(:each) do
125
+ @header = "HTTP/1.0 200 OK\r\nDate: Thu, 29 Jan 2009 03:55:24 GMT\r\nServer: Apache\r\nX-FB-Host: chi-write6\r\nLast-Modified: Wed, 28 Jan 2009 04:10:32 GMT\r\nETag: ziEyTl4q9GH04BR4jgkImd0GvSE\r\nP3P: CP=\"ALL DSP COR NID CUR OUR NOR\"\r\nConnection: close\r\nContent-Type: text/xml;charset=utf-8\r\n\r\n"
126
+ end
127
+
128
+ it "should return the etag from the header if it exists" do
129
+ Feedzirra::Feed.etag_from_header(@header).should == "ziEyTl4q9GH04BR4jgkImd0GvSE"
130
+ end
131
+
132
+ it "should return nil if there is no etag in the header" do
133
+ Feedzirra::Feed.etag_from_header("foo").should be_nil
134
+ end
135
+
136
+ end
137
+
138
+ describe '#last_modified_from_header' do
139
+ before(:each) do
140
+ @header = "HTTP/1.0 200 OK\r\nDate: Thu, 29 Jan 2009 03:55:24 GMT\r\nServer: Apache\r\nX-FB-Host: chi-write6\r\nLast-Modified: Wed, 28 Jan 2009 04:10:32 GMT\r\nETag: ziEyTl4q9GH04BR4jgkImd0GvSE\r\nP3P: CP=\"ALL DSP COR NID CUR OUR NOR\"\r\nConnection: close\r\nContent-Type: text/xml;charset=utf-8\r\n\r\n"
141
+ end
142
+
143
+ it "should return the last modified date from the header if it exists" do
144
+ Feedzirra::Feed.last_modified_from_header(@header).should == Time.parse("Wed, 28 Jan 2009 04:10:32 GMT")
145
+ end
146
+
147
+ it "should return nil if there is no last modified date in the header" do
148
+ Feedzirra::Feed.last_modified_from_header("foo").should be_nil
149
+ end
150
+ end
151
+
152
+ describe "fetching feeds" do
153
+ before(:each) do
154
+ @paul_feed = { :xml => load_sample("PaulDixExplainsNothing.xml"), :url => "http://feeds.feedburner.com/PaulDixExplainsNothing" }
155
+ @trotter_feed = { :xml => load_sample("TrotterCashionHome.xml"), :url => "http://feeds2.feedburner.com/trottercashion" }
156
+ end
157
+
158
+ describe "#fetch_raw" do
159
+ before(:each) do
160
+ @cmock = stub('cmock', :header_str => '', :body_str => @paul_feed[:xml] )
161
+ @multi = stub('curl_multi', :add => true, :perform => true)
162
+ @curl_easy = stub('curl_easy')
163
+ @curl = stub('curl', :headers => {}, :follow_location= => true, :on_failure => true)
164
+ @curl.stub!(:on_success).and_yield(@cmock)
165
+
166
+ Curl::Multi.stub!(:new).and_return(@multi)
167
+ Curl::Easy.stub!(:new).and_yield(@curl).and_return(@curl_easy)
168
+ end
169
+
170
+ it "should set user agent if it's passed as an option" do
171
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url], :user_agent => 'Custom Useragent')
172
+ @curl.headers['User-Agent'].should == 'Custom Useragent'
173
+ end
174
+
175
+ it "should set user agent to default if it's not passed as an option" do
176
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url])
177
+ @curl.headers['User-Agent'].should == Feedzirra::Feed::USER_AGENT
178
+ end
179
+
180
+ it "should set if modified since as an option if passed" do
181
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url], :if_modified_since => Time.parse("Wed, 28 Jan 2009 04:10:32 GMT"))
182
+ @curl.headers["If-Modified-Since"].should == 'Wed, 28 Jan 2009 04:10:32 GMT'
183
+ end
184
+
185
+ it "should set if none match as an option if passed" do
186
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url], :if_none_match => 'ziEyTl4q9GH04BR4jgkImd0GvSE')
187
+ @curl.headers["If-None-Match"].should == 'ziEyTl4q9GH04BR4jgkImd0GvSE'
188
+ end
189
+
190
+ it 'should set userpwd for http basic authentication if :http_authentication is passed' do
191
+ @curl.should_receive(:userpwd=).with('username:password')
192
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url], :http_authentication => ['username', 'password'])
193
+ end
194
+
195
+ it 'should set accepted encodings' do
196
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url])
197
+ @curl.headers["Accept-encoding"].should == 'gzip, deflate'
198
+ end
199
+
200
+ it "should return raw xml" do
201
+ Feedzirra::Feed.fetch_raw(@paul_feed[:url]).should =~ /^#{Regexp.escape('<?xml version="1.0" encoding="UTF-8"?>')}/
202
+ end
203
+
204
+ it "should take multiple feed urls and return a hash of urls and response xml" do
205
+ multi = stub('curl_multi', :add => true, :perform => true)
206
+ Curl::Multi.stub!(:new).and_return(multi)
207
+
208
+ paul_response = stub('paul_response', :header_str => '', :body_str => @paul_feed[:xml] )
209
+ trotter_response = stub('trotter_response', :header_str => '', :body_str => @trotter_feed[:xml] )
210
+
211
+ paul_curl = stub('paul_curl', :headers => {}, :follow_location= => true, :on_failure => true)
212
+ paul_curl.stub!(:on_success).and_yield(paul_response)
213
+
214
+ trotter_curl = stub('trotter_curl', :headers => {}, :follow_location= => true, :on_failure => true)
215
+ trotter_curl.stub!(:on_success).and_yield(trotter_response)
216
+
217
+ Curl::Easy.should_receive(:new).with(@paul_feed[:url]).ordered.and_yield(paul_curl)
218
+ Curl::Easy.should_receive(:new).with(@trotter_feed[:url]).ordered.and_yield(trotter_curl)
219
+
220
+ results = Feedzirra::Feed.fetch_raw([@paul_feed[:url], @trotter_feed[:url]])
221
+ results.keys.should include(@paul_feed[:url])
222
+ results.keys.should include(@trotter_feed[:url])
223
+ results[@paul_feed[:url]].should =~ /Paul Dix/
224
+ results[@trotter_feed[:url]].should =~ /Trotter Cashion/
225
+ end
226
+
227
+ it "should always return a hash when passed an array" do
228
+ results = Feedzirra::Feed.fetch_raw([@paul_feed[:url]])
229
+ results.class.should == Hash
230
+ end
231
+ end
232
+
233
+ describe "#add_url_to_multi" do
234
+ before(:each) do
235
+ @multi = Curl::Multi.new(@paul_feed[:url])
236
+ @multi.stub!(:add)
237
+ @easy_curl = Curl::Easy.new(@paul_feed[:url])
238
+
239
+ Curl::Easy.should_receive(:new).and_yield(@easy_curl)
240
+ end
241
+
242
+ it "should set user agent if it's passed as an option" do
243
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, :user_agent => 'My cool application')
244
+ @easy_curl.headers["User-Agent"].should == 'My cool application'
245
+ end
246
+
247
+ it "should set user agent to default if it's not passed as an option" do
248
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
249
+ @easy_curl.headers["User-Agent"].should == Feedzirra::Feed::USER_AGENT
250
+ end
251
+
252
+ it "should set if modified since as an option if passed" do
253
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, :if_modified_since => Time.parse("Jan 25 2009 04:10:32 GMT"))
254
+ @easy_curl.headers["If-Modified-Since"].should == 'Sun, 25 Jan 2009 04:10:32 GMT'
255
+ end
256
+
257
+ it 'should set follow location to true' do
258
+ @easy_curl.should_receive(:follow_location=).with(true)
259
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
260
+ end
261
+
262
+ it 'should set userpwd for http basic authentication if :http_authentication is passed' do
263
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, :http_authentication => ['myusername', 'mypassword'])
264
+ @easy_curl.userpwd.should == 'myusername:mypassword'
265
+ end
266
+
267
+ it 'should set accepted encodings' do
268
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
269
+ @easy_curl.headers["Accept-encoding"].should == 'gzip, deflate'
270
+ end
271
+
272
+ it "should set if_none_match as an option if passed" do
273
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, :if_none_match => 'ziEyTl4q9GH04BR4jgkImd0GvSE')
274
+ @easy_curl.headers["If-None-Match"].should == 'ziEyTl4q9GH04BR4jgkImd0GvSE'
275
+ end
276
+
277
+ describe 'on success' do
278
+ before(:each) do
279
+ @feed = mock('feed', :feed_url= => true, :etag= => true, :last_modified= => true)
280
+ Feedzirra::Feed.stub!(:decode_content).and_return(@paul_feed[:xml])
281
+ Feedzirra::Feed.stub!(:determine_feed_parser_for_xml).and_return(Feedzirra::AtomFeedBurner)
282
+ Feedzirra::AtomFeedBurner.stub!(:parse).and_return(@feed)
283
+ Feedzirra::Feed.stub!(:etag_from_header).and_return('ziEyTl4q9GH04BR4jgkImd0GvSE')
284
+ Feedzirra::Feed.stub!(:last_modified_from_header).and_return('Wed, 28 Jan 2009 04:10:32 GMT')
285
+ end
286
+
287
+ it 'should decode the response body' do
288
+ Feedzirra::Feed.should_receive(:decode_content).with(@easy_curl).and_return(@paul_feed[:xml])
289
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
290
+ @easy_curl.on_success.call(@easy_curl)
291
+ end
292
+
293
+ it 'should determine the xml parser class' do
294
+ Feedzirra::Feed.should_receive(:determine_feed_parser_for_xml).with(@paul_feed[:xml]).and_return(Feedzirra::AtomFeedBurner)
295
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
296
+ @easy_curl.on_success.call(@easy_curl)
297
+ end
298
+
299
+ it 'should parse the xml' do
300
+ Feedzirra::AtomFeedBurner.should_receive(:parse).with(@paul_feed[:xml]).and_return(@feed)
301
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
302
+ @easy_curl.on_success.call(@easy_curl)
303
+ end
304
+
305
+ describe 'when a compatible xml parser class is found' do
306
+ it 'should set the last effective url to the feed url' do
307
+ @easy_curl.should_receive(:last_effective_url).and_return(@paul_feed[:url])
308
+ @feed.should_receive(:feed_url=).with(@paul_feed[:url])
309
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
310
+ @easy_curl.on_success.call(@easy_curl)
311
+ end
312
+
313
+ it 'should set the etags on the feed' do
314
+ @feed.should_receive(:etag=).with('ziEyTl4q9GH04BR4jgkImd0GvSE')
315
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
316
+ @easy_curl.on_success.call(@easy_curl)
317
+ end
318
+
319
+ it 'should set the last modified on the feed' do
320
+ @feed.should_receive(:last_modified=).with('Wed, 28 Jan 2009 04:10:32 GMT')
321
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, {})
322
+ @easy_curl.on_success.call(@easy_curl)
323
+ end
324
+
325
+ it 'should add the feed to the responses' do
326
+ responses = {}
327
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], responses, {})
328
+ @easy_curl.on_success.call(@easy_curl)
329
+
330
+ responses.length.should == 1
331
+ responses['http://feeds.feedburner.com/PaulDixExplainsNothing'].should == @feed
332
+ end
333
+
334
+ it 'should call proc if :on_success option is passed' do
335
+ success = lambda { |url, feed| }
336
+ success.should_receive(:call).with(@paul_feed[:url], @feed)
337
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, { :on_success => success })
338
+ @easy_curl.on_success.call(@easy_curl)
339
+ end
340
+ end
341
+
342
+ describe 'when no compatible xml parser class is found' do
343
+ it 'should raise a NoParserAvailable exception'
344
+ end
345
+ end
346
+
347
+ describe 'on failure' do
348
+ before(:each) do
349
+ @headers = "HTTP/1.0 404 Not Found\r\nDate: Thu, 29 Jan 2009 03:55:24 GMT\r\nServer: Apache\r\nX-FB-Host: chi-write6\r\nLast-Modified: Wed, 28 Jan 2009 04:10:32 GMT\r\n"
350
+ @body = 'Page could not be found.'
351
+
352
+ @easy_curl.stub!(:response_code).and_return(404)
353
+ @easy_curl.stub!(:header_str).and_return(@headers)
354
+ @easy_curl.stub!(:body_str).and_return(@body)
355
+ end
356
+
357
+ it 'should call proc if :on_failure option is passed' do
358
+ failure = lambda { |url, feed| }
359
+ failure.should_receive(:call).with(@paul_feed[:url], 404, @headers, @body)
360
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], {}, { :on_failure => failure })
361
+ @easy_curl.on_failure.call(@easy_curl)
362
+ end
363
+
364
+ it 'should return the http code in the responses' do
365
+ responses = {}
366
+ Feedzirra::Feed.add_url_to_multi(@multi, @paul_feed[:url], [], responses, {})
367
+ @easy_curl.on_failure.call(@easy_curl)
368
+
369
+ responses.length.should == 1
370
+ responses[@paul_feed[:url]].should == 404
371
+ end
372
+ end
373
+ end
374
+
375
+ describe "#add_feed_to_multi" do
376
+ before(:each) do
377
+ @multi = Curl::Multi.new(@paul_feed[:url])
378
+ @multi.stub!(:add)
379
+ @easy_curl = Curl::Easy.new(@paul_feed[:url])
380
+ @feed = Feedzirra::Feed.parse(sample_feedburner_atom_feed)
381
+
382
+ Curl::Easy.should_receive(:new).and_yield(@easy_curl)
383
+ end
384
+
385
+ it "should set user agent if it's passed as an option" do
386
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, :user_agent => 'My cool application')
387
+ @easy_curl.headers["User-Agent"].should == 'My cool application'
388
+ end
389
+
390
+ it "should set user agent to default if it's not passed as an option" do
391
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
392
+ @easy_curl.headers["User-Agent"].should == Feedzirra::Feed::USER_AGENT
393
+ end
394
+
395
+ it "should set if modified since as an option if passed"
396
+
397
+ it 'should set follow location to true' do
398
+ @easy_curl.should_receive(:follow_location=).with(true)
399
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
400
+ end
401
+
402
+ it 'should set userpwd for http basic authentication if :http_authentication is passed' do
403
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, :http_authentication => ['myusername', 'mypassword'])
404
+ @easy_curl.userpwd.should == 'myusername:mypassword'
405
+ end
406
+
407
+ it "should set if_none_match as an option if passed" do
408
+ @feed.etag = 'ziEyTl4q9GH04BR4jgkImd0GvSE'
409
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
410
+ @easy_curl.headers["If-None-Match"].should == 'ziEyTl4q9GH04BR4jgkImd0GvSE'
411
+ end
412
+
413
+ describe 'on success' do
414
+ before(:each) do
415
+ @new_feed = @feed.clone
416
+ @feed.stub!(:update_from_feed)
417
+ Feedzirra::Feed.stub!(:decode_content).and_return(@paul_feed[:xml])
418
+ Feedzirra::Feed.stub!(:determine_feed_parser_for_xml).and_return(Feedzirra::AtomFeedBurner)
419
+ Feedzirra::AtomFeedBurner.stub!(:parse).and_return(@new_feed)
420
+ Feedzirra::Feed.stub!(:etag_from_header).and_return('ziEyTl4q9GH04BR4jgkImd0GvSE')
421
+ Feedzirra::Feed.stub!(:last_modified_from_header).and_return('Wed, 28 Jan 2009 04:10:32 GMT')
422
+ end
423
+
424
+ it 'should process the next feed in the queue'
425
+
426
+ it 'should parse the updated feed' do
427
+ Feedzirra::AtomFeedBurner.should_receive(:parse).and_return(@new_feed)
428
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
429
+ @easy_curl.on_success.call(@easy_curl)
430
+ end
431
+
432
+ it 'should set the last effective url to the feed url' do
433
+ @easy_curl.should_receive(:last_effective_url).and_return(@paul_feed[:url])
434
+ @new_feed.should_receive(:feed_url=).with(@paul_feed[:url])
435
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
436
+ @easy_curl.on_success.call(@easy_curl)
437
+ end
438
+
439
+ it 'should set the etags on the feed' do
440
+ @new_feed.should_receive(:etag=).with('ziEyTl4q9GH04BR4jgkImd0GvSE')
441
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
442
+ @easy_curl.on_success.call(@easy_curl)
443
+ end
444
+
445
+ it 'should set the last modified on the feed' do
446
+ @new_feed.should_receive(:last_modified=).with('Wed, 28 Jan 2009 04:10:32 GMT')
447
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
448
+ @easy_curl.on_success.call(@easy_curl)
449
+ end
450
+
451
+ it 'should add the feed to the responses' do
452
+ responses = {}
453
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], responses, {})
454
+ @easy_curl.on_success.call(@easy_curl)
455
+
456
+ responses.length.should == 1
457
+ responses['http://feeds.feedburner.com/PaulDixExplainsNothing'].should == @feed
458
+ end
459
+
460
+ it 'should call proc if :on_success option is passed' do
461
+ success = lambda { |feed| }
462
+ success.should_receive(:call).with(@feed)
463
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, { :on_success => success })
464
+ @easy_curl.on_success.call(@easy_curl)
465
+ end
466
+
467
+ it 'should call update from feed on the old feed with the updated feed' do
468
+ @feed.should_receive(:update_from_feed).with(@new_feed)
469
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, {})
470
+ @easy_curl.on_success.call(@easy_curl)
471
+ end
472
+ end
473
+
474
+ describe 'on failure' do
475
+ before(:each) do
476
+ @headers = "HTTP/1.0 404 Not Found\r\nDate: Thu, 29 Jan 2009 03:55:24 GMT\r\nServer: Apache\r\nX-FB-Host: chi-write6\r\nLast-Modified: Wed, 28 Jan 2009 04:10:32 GMT\r\n"
477
+ @body = 'Page could not be found.'
478
+
479
+ @easy_curl.stub!(:response_code).and_return(404)
480
+ @easy_curl.stub!(:header_str).and_return(@headers)
481
+ @easy_curl.stub!(:body_str).and_return(@body)
482
+ end
483
+
484
+ it 'should call on success callback if the response code is 304' do
485
+ success = lambda { |feed| }
486
+ success.should_receive(:call).with(@feed)
487
+ @easy_curl.should_receive(:response_code).and_return(304)
488
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], {}, { :on_success => success })
489
+ @easy_curl.on_failure.call(@easy_curl)
490
+ end
491
+
492
+ it 'should return the http code in the responses' do
493
+ responses = {}
494
+ Feedzirra::Feed.add_feed_to_multi(@multi, @feed, [], responses, {})
495
+ @easy_curl.on_failure.call(@easy_curl)
496
+
497
+ responses.length.should == 1
498
+ responses['http://www.pauldix.net/'].should == 404
499
+ end
500
+ end
501
+ end
502
+
503
+ describe "#fetch_and_parse" do
504
+ it 'should initiate the fetching and parsing using multicurl'
505
+ it "should pass any request options through to add_url_to_multi"
506
+ it 'should slice the feeds into groups of thirty for processing'
507
+ it "should return a feed object if a single feed is passed in"
508
+ it "should return an return an array of feed objects if multiple feeds are passed in"
509
+ end
510
+
511
+ describe "#decode_content" do
512
+ before(:each) do
513
+ @curl_easy = mock('curl_easy', :body_str => '<xml></xml>')
514
+ end
515
+
516
+ it 'should decode the response body using gzip if the Content-Encoding: is gzip' do
517
+ @curl_easy.stub!(:header_str).and_return('Content-Encoding: gzip')
518
+ string_io = mock('stringio', :read => @curl_easy.body_str, :close => true)
519
+ StringIO.should_receive(:new).and_return(string_io)
520
+ Zlib::GzipReader.should_receive(:new).with(string_io).and_return(string_io)
521
+ Feedzirra::Feed.decode_content(@curl_easy)
522
+ end
523
+
524
+ it 'should deflate the response body using inflate if the Content-Encoding: is deflate' do
525
+ @curl_easy.stub!(:header_str).and_return('Content-Encoding: deflate')
526
+ Zlib::Inflate.should_receive(:inflate).with(@curl_easy.body_str)
527
+ Feedzirra::Feed.decode_content(@curl_easy)
528
+ end
529
+
530
+ it 'should return the response body if it is not encoded' do
531
+ @curl_easy.stub!(:header_str).and_return('')
532
+ Feedzirra::Feed.decode_content(@curl_easy).should == '<xml></xml>'
533
+ end
534
+ end
535
+
536
+ describe "#update" do
537
+ it 'should perform the updating using multicurl'
538
+ it "should pass any request options through to add_feed_to_multi"
539
+ it "should return a feed object if a single feed is passed in"
540
+ it "should return an return an array of feed objects if multiple feeds are passed in"
541
+ end
542
+ end
543
+ end
@@ -0,0 +1,149 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Feedzirra::FeedUtilities do
4
+ before(:each) do
5
+ @klass = Class.new do
6
+ include Feedzirra::FeedUtilities
7
+ end
8
+ end
9
+
10
+ describe "instance methods" do
11
+ it "should provide an updated? accessor" do
12
+ feed = @klass.new
13
+ feed.should_not be_updated
14
+ feed.updated = true
15
+ feed.should be_updated
16
+ end
17
+
18
+ it "should provide a new_entries accessor" do
19
+ feed = @klass.new
20
+ feed.new_entries.should == []
21
+ feed.new_entries = [:foo]
22
+ feed.new_entries.should == [:foo]
23
+ end
24
+
25
+ it "should provide an etag accessor" do
26
+ feed = @klass.new
27
+ feed.etag = "foo"
28
+ feed.etag.should == "foo"
29
+ end
30
+
31
+ it "should provide a last_modified accessor" do
32
+ feed = @klass.new
33
+ time = Time.now
34
+ feed.last_modified = time
35
+ feed.last_modified.should == time
36
+ end
37
+
38
+ it "should return new_entries? as true when entries are put into new_entries" do
39
+ feed = @klass.new
40
+ feed.new_entries << :foo
41
+ feed.should have_new_entries
42
+ end
43
+
44
+ it "should return a last_modified value from the entry with the most recent published date if the last_modified date hasn't been set" do
45
+ feed = Feedzirra::Atom.new
46
+ entry =Feedzirra::AtomEntry.new
47
+ entry.published = Time.now.to_s
48
+ feed.entries << entry
49
+ feed.last_modified.should == entry.published
50
+ end
51
+
52
+ it "should not throw an error if one of the entries has published date of nil" do
53
+ feed = Feedzirra::Atom.new
54
+ entry = Feedzirra::AtomEntry.new
55
+ entry.published = Time.now.to_s
56
+ feed.entries << entry
57
+ feed.entries << Feedzirra::AtomEntry.new
58
+ feed.last_modified.should == entry.published
59
+ end
60
+ end
61
+
62
+ describe "#update_from_feed" do
63
+ describe "updating feed attributes" do
64
+ before(:each) do
65
+ # I'm using the Atom class when I know I should be using a different one. However, this update_from_feed
66
+ # method would only be called against a feed item.
67
+ @feed = Feedzirra::Atom.new
68
+ @feed.title = "A title"
69
+ @feed.url = "http://pauldix.net"
70
+ @feed.feed_url = "http://feeds.feedburner.com/PaulDixExplainsNothing"
71
+ @feed.updated = false
72
+ @updated_feed = @feed.dup
73
+ end
74
+
75
+ it "should update the title if changed" do
76
+ @updated_feed.title = "new title"
77
+ @feed.update_from_feed(@updated_feed)
78
+ @feed.title.should == @updated_feed.title
79
+ @feed.should be_updated
80
+ end
81
+
82
+ it "should not update the title if the same" do
83
+ @feed.update_from_feed(@updated_feed)
84
+ @feed.should_not be_updated
85
+ end
86
+
87
+ it "should update the feed_url if changed" do
88
+ @updated_feed.feed_url = "a new feed url"
89
+ @feed.update_from_feed(@updated_feed)
90
+ @feed.feed_url.should == @updated_feed.feed_url
91
+ @feed.should be_updated
92
+ end
93
+
94
+ it "should not update the feed_url if the same" do
95
+ @feed.update_from_feed(@updated_feed)
96
+ @feed.should_not be_updated
97
+ end
98
+
99
+ it "should update the url if changed" do
100
+ @updated_feed.url = "a new url"
101
+ @feed.update_from_feed(@updated_feed)
102
+ @feed.url.should == @updated_feed.url
103
+ end
104
+
105
+ it "should not update the url if not changed" do
106
+ @feed.update_from_feed(@updated_feed)
107
+ @feed.should_not be_updated
108
+ end
109
+ end
110
+
111
+ describe "updating entries" do
112
+ before(:each) do
113
+ # I'm using the Atom class when I know I should be using a different one. However, this update_from_feed
114
+ # method would only be called against a feed item.
115
+ @feed = Feedzirra::Atom.new
116
+ @feed.title = "A title"
117
+ @feed.url = "http://pauldix.net"
118
+ @feed.feed_url = "http://feeds.feedburner.com/PaulDixExplainsNothing"
119
+ @feed.updated = false
120
+ @updated_feed = @feed.dup
121
+ @old_entry = Feedzirra::AtomEntry.new
122
+ @old_entry.url = "http://pauldix.net/old.html"
123
+ @old_entry.published = Time.now.to_s
124
+ @new_entry = Feedzirra::AtomEntry.new
125
+ @new_entry.url = "http://pauldix.net/new.html"
126
+ @new_entry.published = (Time.now + 10).to_s
127
+ @feed.entries << @old_entry
128
+ @updated_feed.entries << @new_entry
129
+ @updated_feed.entries << @old_entry
130
+ end
131
+
132
+ it "should update last-modified from the latest entry date" do
133
+ @feed.update_from_feed(@updated_feed)
134
+ @feed.last_modified.should == @new_entry.published
135
+ end
136
+
137
+ it "should put new entries into new_entries" do
138
+ @feed.update_from_feed(@updated_feed)
139
+ @feed.new_entries.should == [@new_entry]
140
+ end
141
+
142
+ it "should also put new entries into the entries collection" do
143
+ @feed.update_from_feed(@updated_feed)
144
+ @feed.entries.should include(@new_entry)
145
+ @feed.entries.should include(@old_entry)
146
+ end
147
+ end
148
+ end
149
+ end