hootenanny 0.0.1 → 0.1.0
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.
- data/.gitignore +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +5 -7
- data/Gemfile.lock +46 -28
- data/app/controllers/hootenanny/notifications_controller.rb +31 -0
- data/app/controllers/hootenanny/parameters.rb +16 -0
- data/app/controllers/hootenanny/subscriptions_controller.rb +24 -3
- data/app/models/hootenanny/publish_notification.rb +90 -0
- data/app/models/hootenanny/subscription.rb +72 -23
- data/config/routes.rb +2 -1
- data/db/migrate/20130607182642_add_started_at_and_lease_duration_to_subscriptions.rb +15 -0
- data/db/migrate/20130608225621_add_hmac_secret_to_subscription.rb +5 -0
- data/db/migrate/20130611235218_add_publish_notifications.rb +9 -0
- data/db/migrate/20130612153138_add_timestamps_to_publish_notification.rb +5 -0
- data/db/migrate/20130705200729_add_processed_flag_to_publish_notifications.rb +6 -0
- data/db/migrate/20130711061329_switch_publish_notification_process_state_from_boolean_to_string.rb +11 -0
- data/db/migrate/20130711061558_add_index_to_notifications_state.rb +5 -0
- data/hootenanny.gemspec +6 -2
- data/lib/hootenanny/configuration.rb +43 -0
- data/lib/hootenanny/correspondent.rb +44 -0
- data/lib/hootenanny/errors.rb +42 -1
- data/lib/hootenanny/feed.rb +75 -0
- data/lib/hootenanny/feed/atom_feed.rb +18 -0
- data/lib/hootenanny/feed/atom_feed_item.rb +8 -0
- data/lib/hootenanny/feed/digest_feed.rb +14 -0
- data/lib/hootenanny/feed/digest_feed_item.rb +11 -0
- data/lib/hootenanny/feed/feed_item.rb +30 -0
- data/lib/hootenanny/feed/file.rb +66 -0
- data/lib/hootenanny/feed/json_feed.rb +48 -0
- data/lib/hootenanny/feed/json_feed_item.rb +8 -0
- data/lib/hootenanny/feed/null_feed.rb +27 -0
- data/lib/hootenanny/feed/rss_feed.rb +52 -0
- data/lib/hootenanny/feed/rss_feed_item.rb +8 -0
- data/lib/hootenanny/feed_store.rb +30 -0
- data/lib/hootenanny/feed_store/file_feed_store.rb +55 -0
- data/lib/hootenanny/feed_store/web_feed_store.rb +42 -0
- data/lib/hootenanny/hub.rb +116 -22
- data/lib/hootenanny/publish_notification_expiration_policy.rb +17 -0
- data/lib/hootenanny/request.rb +40 -0
- data/lib/hootenanny/request/publish_notification.rb +94 -0
- data/lib/hootenanny/request/subscription.rb +153 -0
- data/lib/hootenanny/subscription_delivery.rb +47 -0
- data/lib/hootenanny/topic.rb +38 -0
- data/lib/hootenanny/topic_synchronizer.rb +71 -0
- data/lib/hootenanny/uri.rb +53 -0
- data/lib/hootenanny/verification.rb +108 -0
- data/lib/hootenanny/version.rb +1 -1
- data/spec/dummy/config/database.yml +12 -6
- data/spec/dummy/db/migrate/20130607183149_add_started_at_and_lease_duration_to_subscriptions.hootenanny.rb +16 -0
- data/spec/dummy/db/migrate/20130608231253_add_hmac_secret_to_subscription.hootenanny.rb +6 -0
- data/spec/dummy/db/migrate/20130611235546_add_publish_notifications.hootenanny.rb +10 -0
- data/spec/dummy/db/migrate/20130612153353_add_timestamps_to_publish_notification.hootenanny.rb +6 -0
- data/spec/dummy/db/migrate/20130705200832_add_processed_flag_to_publish_notifications.hootenanny.rb +7 -0
- data/spec/dummy/db/migrate/20130711061518_switch_publish_notification_process_state_from_boolean_to_string.hootenanny.rb +12 -0
- data/spec/dummy/db/migrate/20130711061629_add_index_to_notifications_state.hootenanny.rb +6 -0
- data/spec/factories/publish_notification.rb +13 -0
- data/spec/factories/requests/publish_notification.rb +11 -0
- data/spec/factories/requests/subscription.rb +46 -0
- data/spec/factories/subscription.rb +14 -2
- data/spec/factories/verification.rb +15 -0
- data/spec/features/publishers/can_notify_the_hub_of_content_updates_spec.rb +58 -0
- data/spec/features/subscribers/are_protected_from_unwarranted_subscriptions_spec.rb +59 -0
- data/spec/features/subscribers/can_receive_distributions_of_topic_content_spec.rb +175 -0
- data/spec/features/subscribers/can_subscribe_to_a_topic_spec.rb +76 -7
- data/spec/fixtures/feeds/atom/97d79220f68b4bf27.atom +0 -0
- data/spec/fixtures/feeds/atom/sample_feed.atom +51 -0
- data/spec/fixtures/feeds/digest/97d79220f68b4bf27.digest +1 -0
- data/spec/fixtures/feeds/digest/complete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +6 -0
- data/spec/fixtures/feeds/digest/incomplete_broadcasted_items/5b187098da59f077f/97d79220f68b4bf27.digest +5 -0
- data/spec/fixtures/feeds/digest/sample_feed.digest +6 -0
- data/spec/fixtures/feeds/json/97d79220f68b4bf27.json +1 -0
- data/spec/fixtures/feeds/json/feed_with_one_item.json +21 -0
- data/spec/fixtures/feeds/json/sample_feed.json +30 -0
- data/spec/fixtures/feeds/rss/5b187098da59f077f/97d79220f68b4bf27.rss +21 -0
- data/spec/fixtures/feeds/rss/97d79220f68b4bf27.rss +0 -0
- data/spec/fixtures/feeds/rss/feed_with_one_item.rss +15 -0
- data/spec/fixtures/feeds/rss/minimal_feed.rss +12 -0
- data/spec/fixtures/feeds/rss/sample_feed.rss +22 -0
- data/spec/fixtures/feeds/rss/sample_feed_2.rss +22 -0
- data/spec/lib/hootenanny/configuration_spec.rb +7 -0
- data/spec/lib/hootenanny/correspondent_spec.rb +94 -0
- data/spec/lib/hootenanny/errors_spec.rb +21 -0
- data/spec/lib/hootenanny/feed/atom_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/atom_feed_spec.rb +40 -0
- data/spec/lib/hootenanny/feed/digest_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/digest_feed_spec.rb +40 -0
- data/spec/lib/hootenanny/feed/feed_item_spec.rb +49 -0
- data/spec/lib/hootenanny/feed/file_spec.rb +66 -0
- data/spec/lib/hootenanny/feed/json_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/json_feed_spec.rb +128 -0
- data/spec/lib/hootenanny/feed/null_feed_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/rss_feed_item_spec.rb +9 -0
- data/spec/lib/hootenanny/feed/rss_feed_spec.rb +143 -0
- data/spec/lib/hootenanny/feed_spec.rb +159 -0
- data/spec/lib/hootenanny/feed_store/file_feed_store_spec.rb +58 -0
- data/spec/lib/hootenanny/feed_store/web_feed_store_spec.rb +47 -0
- data/spec/lib/hootenanny/feed_store_spec.rb +27 -0
- data/spec/lib/hootenanny/hub_spec.rb +73 -0
- data/spec/lib/hootenanny/publish_notification_expiration_policy_spec.rb +35 -0
- data/spec/lib/hootenanny/request/publish_notification_spec.rb +43 -0
- data/spec/lib/hootenanny/request/subscription_spec.rb +89 -0
- data/spec/lib/hootenanny/request_spec.rb +21 -0
- data/spec/lib/hootenanny/subscription_delivery_spec.rb +54 -0
- data/spec/lib/hootenanny/topic_spec.rb +15 -0
- data/spec/lib/hootenanny/topic_synchronizer_spec.rb +98 -0
- data/spec/lib/hootenanny/uri_spec.rb +32 -0
- data/spec/lib/hootenanny/verification_spec.rb +92 -0
- data/spec/models/hootenanny/publish_notification_spec.rb +55 -0
- data/spec/models/hootenanny/subscription_spec.rb +58 -19
- data/spec/support/verification.rb +63 -0
- metadata +231 -14
- data/spec/controllers/hootenanny/hub_spec.rb +0 -15
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/feed'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
class Feed
|
6
|
+
describe RSSFeed do
|
7
|
+
let(:rss_content_path) { ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss') }
|
8
|
+
let(:rss_content) { ::File.read(rss_content_path).chomp }
|
9
|
+
let(:minimal_rss_content_path) { ::File.expand_path('./spec/fixtures/feeds/rss/minimal_feed.rss') }
|
10
|
+
let(:minimal_rss_content) { ::File.read(minimal_rss_content_path).chomp }
|
11
|
+
|
12
|
+
it 'can create itself from some RSS' do
|
13
|
+
allow(RSS::Parser).to receive(:parse)
|
14
|
+
.and_return('content')
|
15
|
+
|
16
|
+
adapter = RSSFeed.from_content(rss_content)
|
17
|
+
|
18
|
+
expect(adapter).to be_a Hootenanny::Feed::RSSFeed
|
19
|
+
expect(adapter.content).to eql 'content'
|
20
|
+
expect(RSS::Parser).to have_received(:parse)
|
21
|
+
.with(rss_content)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'throws an error if the content is not parsable' do
|
25
|
+
expect { RSSFeed.from_content('<asdf') }
|
26
|
+
.to raise_error(Hootenanny::Feed::ParseError)
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'can iterate over each of the items in the feed' do
|
30
|
+
adapter = RSSFeed.from_content(rss_content)
|
31
|
+
|
32
|
+
expect(adapter).to have(2).items
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'can wrap each item in a RSSFeedItem' do
|
36
|
+
adapter = RSSFeed.from_content(rss_content)
|
37
|
+
|
38
|
+
expect(adapter.items.first).to be_a Hootenanny::Feed::RSSFeedItem
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'it properly sets items to an empty array even if the parsed RSS is nil' do
|
42
|
+
feed = RSSFeed.from_content('')
|
43
|
+
|
44
|
+
expect(feed.items).to eql []
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'can properly set content for itself from a String' do
|
48
|
+
rss = RSS::Parser.parse(minimal_rss_content)
|
49
|
+
feed = RSSFeed.from_content(rss)
|
50
|
+
|
51
|
+
expect(feed.to_s).to eql minimal_rss_content
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'if it is not blank, it always returns a String containing items' do
|
55
|
+
rss = RSS::Parser.parse(minimal_rss_content)
|
56
|
+
feed = RSSFeed.from_content(rss)
|
57
|
+
|
58
|
+
expect(feed.to_s).to eql minimal_rss_content
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'if it is blank' do
|
62
|
+
feed = RSSFeed.from_content('')
|
63
|
+
|
64
|
+
expect(feed.to_s).to eql ''
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'it properly sets items to an empty array even if no items are in the content' do
|
68
|
+
rss = RSS::Parser.parse(minimal_rss_content)
|
69
|
+
feed = RSSFeed.from_content(rss)
|
70
|
+
|
71
|
+
expect(feed.items).to eql []
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'it properly sets items to an empty array even if the parsed RSS is nil' do
|
75
|
+
feed = RSSFeed.from_content('')
|
76
|
+
|
77
|
+
expect(feed.items).to eql []
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'can properly set content for itself from a String' do
|
81
|
+
rss = RSS::Parser.parse(minimal_rss_content)
|
82
|
+
feed = RSSFeed.from_content(rss)
|
83
|
+
|
84
|
+
expect(feed.content).to be_an RSS::Rss
|
85
|
+
expect(feed.content.to_s).to include rss.to_s
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'can properly set content for itself from an RSS feed' do
|
89
|
+
rss = RSS::Parser.parse(minimal_rss_content)
|
90
|
+
feed = RSSFeed.from_content(rss)
|
91
|
+
|
92
|
+
expect(feed.content).to be_an RSS::Rss
|
93
|
+
expect(feed.content.to_s).to include rss.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'raises an error if it does not know how to parse content' do
|
97
|
+
expect { RSSFeed.from_content(2) }.to raise_error Hootenanny::Feed::ParseError
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'can determine the difference between two feeds' do
|
101
|
+
first_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
102
|
+
first_content = ::File.read(first_content_path)
|
103
|
+
|
104
|
+
second_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/feed_with_one_item.rss')
|
105
|
+
second_content = ::File.read(second_content_path)
|
106
|
+
|
107
|
+
first_feed = Feed.infer(first_content, type: :rss)
|
108
|
+
second_feed = Feed.infer(second_content, type: :rss)
|
109
|
+
|
110
|
+
difference_feed = first_feed - second_feed
|
111
|
+
|
112
|
+
expect(difference_feed.items).to have(1).item
|
113
|
+
expect(difference_feed.items).to eql [first_feed.items[1]]
|
114
|
+
expect(difference_feed.content.channel.title).to eql 'Ruby Rogues'
|
115
|
+
expect(difference_feed.items.first.send(:content).title).to eql '108 RR Ruby Trends'
|
116
|
+
|
117
|
+
expect(difference_feed.to_s).to include 'Ruby Rogues'
|
118
|
+
expect(difference_feed.to_s).to include '108 RR Ruby Trends'
|
119
|
+
expect(difference_feed.to_s).not_to include '109 RR Extreme Programming with Will Read'
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'can determine the difference between two feeds of differing types' do
|
123
|
+
first_content_path = ::File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
124
|
+
first_content = ::File.read(first_content_path)
|
125
|
+
|
126
|
+
second_content = '{"items": ["01bfe038abb650189f78891be1581a52a7754a9be5fee7f8feeb0f7d617d3dd6"]}'
|
127
|
+
|
128
|
+
first_feed = Feed.infer(first_content, type: :rss)
|
129
|
+
second_feed = Feed.infer(second_content, type: :digest)
|
130
|
+
|
131
|
+
difference_feed = first_feed - second_feed
|
132
|
+
|
133
|
+
expect(difference_feed.items).to have(1).item
|
134
|
+
expect(difference_feed.items).to eql [first_feed.items[1]]
|
135
|
+
expect(difference_feed.content.channel.title).to eql 'Ruby Rogues'
|
136
|
+
|
137
|
+
expect(difference_feed.to_s).to include 'Ruby Rogues'
|
138
|
+
expect(difference_feed.to_s).to include '108 RR Ruby Trends'
|
139
|
+
expect(difference_feed.to_s).not_to include '109 RR Extreme Programming with Will Read'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/feed'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Feed do
|
6
|
+
context 'when the content passed in is to be parsed as an RSS document' do
|
7
|
+
let(:content_path) { File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss') }
|
8
|
+
let(:content) { File.read(content_path) }
|
9
|
+
let(:type) { :rss }
|
10
|
+
|
11
|
+
it 'can properly create a feed from that' do
|
12
|
+
feed = Feed.infer(content, type: type)
|
13
|
+
|
14
|
+
expect(feed).to be_a Hootenanny::Feed::RSSFeed
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'knows it is an RSS feed' do
|
18
|
+
feed = Feed.infer(content, type: type)
|
19
|
+
|
20
|
+
expect(feed.type).to eql :rss
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when the content passed in is to be parsed as an JSON document' do
|
25
|
+
let(:content_path) { File.expand_path('./spec/fixtures/feeds/json/sample_feed.json') }
|
26
|
+
let(:content) { File.read(content_path) }
|
27
|
+
let(:type) { :json }
|
28
|
+
|
29
|
+
it 'can properly create a feed from that' do
|
30
|
+
feed = Feed.infer(content, type: type)
|
31
|
+
|
32
|
+
expect(feed).to be_a Hootenanny::Feed::JSONFeed
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'knows it is a JSON feed' do
|
36
|
+
feed = Feed.infer(content, type: type)
|
37
|
+
|
38
|
+
expect(feed.type).to eql :json
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when the content passed in is to be parsed as an Atom document' do
|
43
|
+
let(:content_path) { File.expand_path('./spec/fixtures/feeds/atom/sample_feed.atom') }
|
44
|
+
let(:content) { File.read(content_path) }
|
45
|
+
let(:type) { :atom }
|
46
|
+
|
47
|
+
it 'can properly create a feed from that' do
|
48
|
+
feed = Feed.infer(content, type: type)
|
49
|
+
|
50
|
+
expect(feed).to be_a Hootenanny::Feed::AtomFeed
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'knows it is an Atom feed' do
|
54
|
+
feed = Feed.infer(content, type: type)
|
55
|
+
|
56
|
+
expect(feed.type).to eql :atom
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when the content passed in is to be parsed as an Digest document' do
|
61
|
+
let(:content_path) { File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest') }
|
62
|
+
let(:content) { File.read(content_path) }
|
63
|
+
let(:type) { :digest }
|
64
|
+
|
65
|
+
it 'can properly create a feed from that' do
|
66
|
+
feed = Feed.infer(content, type: type)
|
67
|
+
|
68
|
+
expect(feed).to be_a Hootenanny::Feed::DigestFeed
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'knows it is a digest feed' do
|
72
|
+
feed = Feed.infer(content, type: type)
|
73
|
+
|
74
|
+
expect(feed.type).to eql :digest
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when the content passed in cannot be parsed as any known type' do
|
79
|
+
let(:type) { :unknown }
|
80
|
+
|
81
|
+
it 'can properly create a feed from that' do
|
82
|
+
expect { Feed.infer('test content', type: type) }
|
83
|
+
.to raise_error(Hootenanny::Feed::UnknownContentTypeError)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'can combine two feeds of the same type together' do
|
88
|
+
first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
89
|
+
first_content = File.read(first_content_path)
|
90
|
+
|
91
|
+
second_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed_2.rss')
|
92
|
+
second_content = File.read(second_content_path)
|
93
|
+
|
94
|
+
first_feed = Feed.infer(first_content, type: :rss)
|
95
|
+
second_feed = Feed.infer(second_content, type: :rss)
|
96
|
+
|
97
|
+
expect(first_feed + second_feed).to have(4).items
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'eliminates duplicates when combining feeds' do
|
101
|
+
first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
102
|
+
first_content = File.read(first_content_path)
|
103
|
+
|
104
|
+
second_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
105
|
+
second_content = File.read(second_content_path)
|
106
|
+
|
107
|
+
first_feed = Feed.infer(first_content, type: :rss)
|
108
|
+
second_feed = Feed.infer(second_content, type: :rss)
|
109
|
+
|
110
|
+
expect(first_feed + second_feed).to have(2).items
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'cannot combine feeds of a different type' do
|
114
|
+
first_content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
115
|
+
first_content = File.read(first_content_path)
|
116
|
+
|
117
|
+
second_content_path = File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest')
|
118
|
+
second_content = File.read(second_content_path)
|
119
|
+
|
120
|
+
first_feed = Feed.infer(first_content, type: :rss)
|
121
|
+
second_feed = Feed.infer(second_content, type: :digest)
|
122
|
+
|
123
|
+
expect { first_feed + second_feed }.to raise_error Hootenanny::Feed::CombinationError
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'can convert an RSS feed into a Digest feed' do
|
127
|
+
content_path = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
128
|
+
content = File.read(content_path)
|
129
|
+
|
130
|
+
feed = Feed::RSSFeed.from_content(content)
|
131
|
+
|
132
|
+
expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["01bfe038abb650189f78891be1581a52a7754a9be5fee7f8feeb0f7d617d3dd6","a97acd993db7ef4cc74022f357954d3c4b52ab659218b89a59cdd355880d4ef6"]}}
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'can convert a JSON feed into a Digest feed' do
|
136
|
+
content_path = File.expand_path('./spec/fixtures/feeds/json/sample_feed.json')
|
137
|
+
content = File.read(content_path)
|
138
|
+
|
139
|
+
feed = Feed::JSONFeed.from_content(content)
|
140
|
+
|
141
|
+
expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["1a6ed1752c74c2b7fcc0f43ce39e53f8f220cd85f85e339b06eb75a0f16bbea8","6d557b1225129abebcb56d4c986dc63bf644ee865c3833f52ca69e75b4275052"]}}
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'can convert a Digest feed into a Digest feed' do
|
145
|
+
content_path = File.expand_path('./spec/fixtures/feeds/digest/sample_feed.digest')
|
146
|
+
content = File.read(content_path)
|
147
|
+
|
148
|
+
feed = Feed::DigestFeed.from_content(content)
|
149
|
+
|
150
|
+
expect(feed.to_digest_feed.to_s).to eql %Q{{"items":["aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"]}}
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'can convert an empty feed into a Digest feed' do
|
154
|
+
feed = Feed::RSSFeed.from_content('')
|
155
|
+
|
156
|
+
expect(feed.to_digest_feed.to_s).to eql %Q{{"items":[]}}
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/feed_store/file_feed_store'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Hootenanny
|
6
|
+
class FeedStore
|
7
|
+
describe FileFeedStore do
|
8
|
+
it 'can create a feed from the retrieved RSS content', :integration do
|
9
|
+
sample_feed_location = './spec/fixtures/feeds/rss'
|
10
|
+
|
11
|
+
store = FileFeedStore.new(location: sample_feed_location)
|
12
|
+
|
13
|
+
expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::RSSFeed
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can create a feed from the retrieved JSON content', :integration do
|
17
|
+
sample_feed_location = './spec/fixtures/feeds/json'
|
18
|
+
|
19
|
+
store = FileFeedStore.new(location: sample_feed_location)
|
20
|
+
|
21
|
+
expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::JSONFeed
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'can create a feed from the retrieved Atom content', :integration do
|
25
|
+
sample_feed_location = './spec/fixtures/feeds/atom'
|
26
|
+
|
27
|
+
store = FileFeedStore.new(location: sample_feed_location)
|
28
|
+
|
29
|
+
expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::AtomFeed
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'can create a feed from the retrieved Digest content', :integration do
|
33
|
+
sample_feed_location = './spec/fixtures/feeds/digest'
|
34
|
+
|
35
|
+
store = FileFeedStore.new(location: sample_feed_location)
|
36
|
+
|
37
|
+
expect(store.fetch(url: 'http://topic.com')).to be_an Hootenanny::Feed::DigestFeed
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'can store the contents of a feed into a file' do
|
41
|
+
feed = double(to_s: 'feed contents',
|
42
|
+
type: 'feedtype')
|
43
|
+
|
44
|
+
file_base_path = "./tmp/#{SecureRandom.hex(4)}"
|
45
|
+
|
46
|
+
store = FileFeedStore.new(location: file_base_path)
|
47
|
+
|
48
|
+
store.store(feed: feed,
|
49
|
+
url: 'http://example.com',
|
50
|
+
path: 'more_pathy')
|
51
|
+
|
52
|
+
stored_file_contents = ::File.read("#{file_base_path}/more_pathy/f0e6a6a97042a4f1f.feedtype")
|
53
|
+
|
54
|
+
expect(stored_file_contents).to eql 'feed contents'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/feed_store/web_feed_store'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
class FeedStore
|
6
|
+
describe WebFeedStore do
|
7
|
+
it 'can create a feed from the retrieved RSS content', :integration do
|
8
|
+
sample_feed = File.expand_path('./spec/fixtures/feeds/rss/sample_feed.rss')
|
9
|
+
sample_feed_content = File.read(sample_feed)
|
10
|
+
|
11
|
+
stub_request(:get, 'http://example.com')
|
12
|
+
.to_return(body: sample_feed_content,
|
13
|
+
headers: {'Content-Type' => 'application/rss+xml'})
|
14
|
+
|
15
|
+
store = WebFeedStore.new
|
16
|
+
|
17
|
+
expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::RSSFeed
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can create a feed from the retrieved JSON content', :integration do
|
21
|
+
sample_feed = File.expand_path('./spec/fixtures/feeds/json/sample_feed.json')
|
22
|
+
sample_feed_content = File.read(sample_feed)
|
23
|
+
|
24
|
+
stub_request(:get, 'http://example.com')
|
25
|
+
.to_return(body: sample_feed_content,
|
26
|
+
headers: {'Content-Type' => 'application/json'})
|
27
|
+
|
28
|
+
store = WebFeedStore.new
|
29
|
+
|
30
|
+
expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::JSONFeed
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'can create a feed from the retrieved Atom content', :integration do
|
34
|
+
sample_feed = File.expand_path('./spec/fixtures/feeds/atom/sample_feed.atom')
|
35
|
+
sample_feed_content = File.read(sample_feed)
|
36
|
+
|
37
|
+
stub_request(:get, 'http://example.com')
|
38
|
+
.to_return(body: sample_feed_content,
|
39
|
+
headers: {'Content-Type' => 'application/atom+xml'})
|
40
|
+
|
41
|
+
store = WebFeedStore.new
|
42
|
+
|
43
|
+
expect(store.fetch(url: 'http://example.com')).to be_an Hootenanny::Feed::AtomFeed
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/feed_store'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe FeedStore do
|
6
|
+
it 'can create the proper feed store based on the type requested' do
|
7
|
+
allow(Hootenanny::FeedStore::WebFeedStore).to receive(:new)
|
8
|
+
.and_return('feed store')
|
9
|
+
|
10
|
+
expect(FeedStore.infer(type: :web))
|
11
|
+
.to eql 'feed store'
|
12
|
+
expect(Hootenanny::FeedStore::WebFeedStore)
|
13
|
+
.to have_received(:new)
|
14
|
+
.with(type: :web)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'throws an error if the type is not supplied' do
|
18
|
+
expect { FeedStore.infer }
|
19
|
+
.to raise_error(Hootenanny::FeedStore::InferenceError)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'throws an error if the type is not valid' do
|
23
|
+
expect { FeedStore.infer(type: :unknown) }
|
24
|
+
.to raise_error(Hootenanny::FeedStore::UnknownStorageTypeError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|