hootenanny 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,73 @@
|
|
1
|
+
require 'rspectacular/spec_helpers/active_record_basic'
|
2
|
+
require 'hootenanny/hub'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Hub do
|
6
|
+
it 'can apply the request' do
|
7
|
+
my_request = double
|
8
|
+
|
9
|
+
allow(my_request).to receive(:apply)
|
10
|
+
|
11
|
+
Hub.subscribe(my_request)
|
12
|
+
|
13
|
+
expect(my_request).to(have_received(:apply))
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'can generate a request' do
|
17
|
+
allow(Request).to receive(:build)
|
18
|
+
|
19
|
+
Hub.request(:my_attrs => :my_value)
|
20
|
+
|
21
|
+
expect(Request).to have_received(:build)
|
22
|
+
.with(:my_attrs => :my_value)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can broadcast all notifications' do
|
26
|
+
create(:publish_notification, :unprocessed,
|
27
|
+
:topic => 'http://topic1.com')
|
28
|
+
create(:publish_notification, :unprocessed,
|
29
|
+
:topic => 'http://topic2.com')
|
30
|
+
|
31
|
+
allow(Hootenanny::Correspondent).to receive(:broadcast).
|
32
|
+
and_return true
|
33
|
+
|
34
|
+
Hub.broadcast
|
35
|
+
|
36
|
+
expect(Hootenanny::Correspondent).to have_received(:broadcast).
|
37
|
+
with('http://topic1.com')
|
38
|
+
|
39
|
+
expect(Hootenanny::Correspondent).to have_received(:broadcast).
|
40
|
+
with('http://topic2.com')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'removes the notification if it has been broadcast successfully' do
|
44
|
+
notification_the_first = create(:publish_notification, :unprocessed,
|
45
|
+
:topic => 'http://topic1.com')
|
46
|
+
notification_the_second = create(:publish_notification, :unprocessed,
|
47
|
+
:topic => 'http://topic2.com')
|
48
|
+
|
49
|
+
allow(Hootenanny::Correspondent).to receive(:broadcast).
|
50
|
+
and_return true
|
51
|
+
|
52
|
+
Hub.broadcast
|
53
|
+
|
54
|
+
expect(notification_the_first.reload).to be_processed
|
55
|
+
expect(notification_the_second.reload).to be_processed
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'does not remove the notification if it has not been broadcast successfully' do
|
59
|
+
notification_the_first = create(:publish_notification, :unprocessed,
|
60
|
+
:topic => 'http://topic1.com')
|
61
|
+
notification_the_second = create(:publish_notification, :unprocessed,
|
62
|
+
:topic => 'http://topic2.com')
|
63
|
+
|
64
|
+
allow(Hootenanny::Correspondent).to receive(:broadcast).
|
65
|
+
and_return true
|
66
|
+
|
67
|
+
Hub.broadcast
|
68
|
+
|
69
|
+
expect { notification_the_first.reload }.to be_true
|
70
|
+
expect { notification_the_second.reload }.to be_true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/publish_notification_expiration_policy'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe PublishNotificationExpirationPolicy, :time_mock do
|
6
|
+
let(:one_day_ago) { Time.now - 86_400 }
|
7
|
+
|
8
|
+
it 'will expire the item if it has been at least a day since it has been updated' do
|
9
|
+
notification = double(:updated_at => (one_day_ago - 1),
|
10
|
+
:expire => true)
|
11
|
+
|
12
|
+
PublishNotificationExpirationPolicy.apply(notification)
|
13
|
+
|
14
|
+
expect(notification).to have_received(:expire)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'will not expire the item if it has been exactly a day since it has been updated' do
|
18
|
+
notification = double(:updated_at => one_day_ago,
|
19
|
+
:expire => true)
|
20
|
+
|
21
|
+
PublishNotificationExpirationPolicy.apply(notification)
|
22
|
+
|
23
|
+
expect(notification).not_to have_received(:expire)
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'will not expire the item if it has been less than a day since it has been updated' do
|
27
|
+
notification = double(:updated_at => (one_day_ago + 1),
|
28
|
+
:expire => true)
|
29
|
+
|
30
|
+
PublishNotificationExpirationPolicy.apply(notification)
|
31
|
+
|
32
|
+
expect(notification).not_to have_received(:expire)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'rspectacular/spec_helpers/active_record_basic'
|
2
|
+
require 'hootenanny/request/publish_notification'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Request::PublishNotification do
|
6
|
+
it 'can properly build itself given valid attributes' do
|
7
|
+
my_request = Request::PublishNotification.build(topics: %w{mytopic myothertopic})
|
8
|
+
|
9
|
+
expect(my_request.topics).to eql %w{mytopic myothertopic}
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'requires at least one topic to be buillt' do
|
13
|
+
expect { Request::PublishNotification.build }
|
14
|
+
.to raise_error ::Hootenanny::Request::BuildError
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'generates a Publish Notification for a topic' do
|
18
|
+
allow(PublishNotification).to receive(:notify)
|
19
|
+
.and_return(:publish_notification)
|
20
|
+
|
21
|
+
my_request = build( :publish_notification_request,
|
22
|
+
:topics => 'mytopic')
|
23
|
+
|
24
|
+
expect(my_request.apply).to have(1).items
|
25
|
+
expect(PublishNotification).to have_received(:notify)
|
26
|
+
.with('mytopic')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'generates a Publish Notification for each of the topics' do
|
30
|
+
allow(PublishNotification).to receive(:notify)
|
31
|
+
.and_return(:publish_notification)
|
32
|
+
|
33
|
+
my_request = build( :publish_notification_request,
|
34
|
+
:topics => %w{mytopic myothertopic})
|
35
|
+
|
36
|
+
expect(my_request.apply).to have(2).items
|
37
|
+
expect(PublishNotification).to have_received(:notify)
|
38
|
+
.with('mytopic')
|
39
|
+
expect(PublishNotification).to have_received(:notify)
|
40
|
+
.with('myothertopic')
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'rspectacular/spec_helpers/active_record_basic'
|
2
|
+
require 'hootenanny/request/subscription'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Request::Subscription do
|
6
|
+
let(:verification_class) { double }
|
7
|
+
|
8
|
+
it 'can properly build itself given valid attributes' do
|
9
|
+
my_request = Request::Subscription.build(callback: 'http://mycallback',
|
10
|
+
topic: 'http://mytopic',)
|
11
|
+
|
12
|
+
expect(my_request.subscriber).to eql 'http://mycallback'
|
13
|
+
expect(my_request.topic).to eql 'http://mytopic'
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'requires a callback to be buillt' do
|
17
|
+
expect { Request::Subscription.build(topic: 'http://mytopic') }
|
18
|
+
.to raise_error ::Hootenanny::Request::BuildError
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'requires a topic to be buillt' do
|
22
|
+
expect { Request::Subscription.build(callback: 'http://mycallback') }
|
23
|
+
.to raise_error ::Hootenanny::Request::BuildError
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'generates a Subscription when the request is verified' do
|
27
|
+
allow(Subscription).to receive(:subscribe)
|
28
|
+
.and_return(:subscription)
|
29
|
+
|
30
|
+
my_request = build( :subscription_request,
|
31
|
+
:verified,
|
32
|
+
:subscriber => 'http://mycallback',
|
33
|
+
:topic => 'http://mytopic',
|
34
|
+
:digest_secret => 'secret',
|
35
|
+
:lease_duration => 600)
|
36
|
+
|
37
|
+
expect(my_request.apply).to eql :subscription
|
38
|
+
expect(Subscription).to( have_received(:subscribe)
|
39
|
+
.with( :subscriber => 'http://mycallback',
|
40
|
+
:to => 'http://mytopic',
|
41
|
+
:lease_duration => 600,
|
42
|
+
:digest_secret => 'secret'))
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'defaults the lease duration to 1 hour if none is specified' do
|
46
|
+
allow(Subscription).to receive(:subscribe)
|
47
|
+
.and_return(:subscription)
|
48
|
+
|
49
|
+
my_request = build( :subscription_request,
|
50
|
+
:verified,
|
51
|
+
:subscriber => 'http://mycallback',
|
52
|
+
:topic => 'http://mytopic',
|
53
|
+
:digest_secret => 'my big secret')
|
54
|
+
|
55
|
+
expect(my_request.apply).to eql :subscription
|
56
|
+
expect(Subscription).to( have_received(:subscribe)
|
57
|
+
.with( :subscriber => 'http://mycallback',
|
58
|
+
:to => 'http://mytopic',
|
59
|
+
:digest_secret => 'my big secret',
|
60
|
+
:lease_duration => 1_577_880_000))
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'can create a verification with the proper information' do
|
64
|
+
allow(verification_class).to receive(:new)
|
65
|
+
.and_return(double :verified? => true)
|
66
|
+
|
67
|
+
my_request = build( :subscription_request,
|
68
|
+
subscriber: 'http://mycallback',
|
69
|
+
topic: 'http://mytopic',
|
70
|
+
requester_token: '12345',
|
71
|
+
verification_class: verification_class)
|
72
|
+
|
73
|
+
my_request.apply
|
74
|
+
|
75
|
+
expect(verification_class).to have_received(:new)
|
76
|
+
.with(
|
77
|
+
url: 'http://mycallback',
|
78
|
+
mode: :subscribe,
|
79
|
+
topic: 'http://mytopic',
|
80
|
+
requester_token: '12345')
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'does not generate a Subscription when the request is unverified' do
|
84
|
+
my_request = build(:subscription_request, :unverified)
|
85
|
+
|
86
|
+
expect { my_request.apply }.to raise_error(Hootenanny::SubscriptionError, 'Request could not be verified.')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/request'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Request do
|
6
|
+
it 'can build the proper request based on the options passed in' do
|
7
|
+
allow(Request::Subscription).to(receive(:build))
|
8
|
+
|
9
|
+
Request.build(type: :subscription,
|
10
|
+
other_attr: true)
|
11
|
+
|
12
|
+
expect(Request::Subscription).to(have_received(:build)
|
13
|
+
.with( type: :subscription,
|
14
|
+
other_attr: true))
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'raises a wrapped exception whenever no type is passed in' do
|
18
|
+
expect { Request.build({}) }.to raise_error(Request::BuildError, 'key not found: :type')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/subscription_delivery'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe SubscriptionDelivery do
|
6
|
+
it 'can deliver a feed to a subscriber' do
|
7
|
+
feed = double(to_s: 'my_feed_content',
|
8
|
+
content_type: 'application/whatever')
|
9
|
+
|
10
|
+
subscription_stub = stub_request(:post, 'http://example.com').
|
11
|
+
with(:headers => {'Content-Type' => 'application/whatever'}).
|
12
|
+
to_return(:status => 200)
|
13
|
+
|
14
|
+
SubscriptionDelivery.deliver(subscriber: 'http://example.com',
|
15
|
+
feed: feed)
|
16
|
+
|
17
|
+
expect(subscription_stub).to have_been_requested
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'will add a verification digest along with the content if a secret was specified' do
|
21
|
+
feed = double(to_s: 'my_feed_content',
|
22
|
+
content_type: 'application/whatever')
|
23
|
+
|
24
|
+
subscription_stub = stub_request(:post, 'http://example.com').
|
25
|
+
with(:headers => {'Content-Type' => 'application/whatever',
|
26
|
+
'X-Hub-Signature' => 'sha1=dde90de8a0b89aff446c53ff63ec68b4087b0d3b'}).
|
27
|
+
to_return(:status => 200)
|
28
|
+
|
29
|
+
SubscriptionDelivery.deliver(subscriber: 'http://example.com',
|
30
|
+
digest_secret: 'secret',
|
31
|
+
feed: feed)
|
32
|
+
|
33
|
+
expect(subscription_stub).to have_been_requested
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'is true if successful' do
|
37
|
+
stub_request(:post, 'http://example.com').
|
38
|
+
to_return(:status => 200)
|
39
|
+
|
40
|
+
expect(SubscriptionDelivery.deliver(subscriber: 'http://example.com',
|
41
|
+
feed: double.as_null_object)).to \
|
42
|
+
be_true
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'is false if unsuccessful' do
|
46
|
+
stub_request(:post, 'http://example.com').
|
47
|
+
to_return(:status => 300)
|
48
|
+
|
49
|
+
expect(SubscriptionDelivery.deliver(subscriber: 'http://example.com',
|
50
|
+
feed: double.as_null_object)).to \
|
51
|
+
be_false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/topic'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe Topic do
|
6
|
+
it 'can create itself from a topic' do
|
7
|
+
expect(Topic.from_url('http://example.com')).to be_a Topic
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'cannot create itself if the URI is invalid' do
|
11
|
+
expect {Topic.from_url('http://#^&@*.com')}.
|
12
|
+
to raise_error Hootenanny::URI::InvalidError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/topic_synchronizer'
|
3
|
+
require 'hootenanny/errors'
|
4
|
+
require 'hootenanny/feed_store/file_feed_store'
|
5
|
+
|
6
|
+
module Hootenanny
|
7
|
+
describe TopicSynchronizer do
|
8
|
+
let(:subscription_delivery) { double }
|
9
|
+
let(:subscriber_url) { 'http://subscriber.com' }
|
10
|
+
let(:topic_url) { 'http://topic.com' }
|
11
|
+
let(:local_feed_store) { FeedStore::FileFeedStore.new(
|
12
|
+
location: local_feed_file_path) }
|
13
|
+
let(:remote_feed_store) { FeedStore::FileFeedStore.new(
|
14
|
+
location: remote_feed_file_path) }
|
15
|
+
|
16
|
+
let(:topic_synchronizer) { TopicSynchronizer.new(
|
17
|
+
subscriber: subscriber_url,
|
18
|
+
topic: topic_url,
|
19
|
+
local_feed_store: local_feed_store,
|
20
|
+
remote_feed_store: remote_feed_store,
|
21
|
+
subscription_delivery: subscription_delivery) }
|
22
|
+
|
23
|
+
before { allow(subscription_delivery).to receive(:deliver) }
|
24
|
+
|
25
|
+
context 'if there are no new items to deliver' do
|
26
|
+
let(:local_feed_file_path) { './spec/fixtures/feeds/digest/complete_broadcasted_items' }
|
27
|
+
let(:remote_feed_file_path) { './spec/fixtures/feeds/rss' }
|
28
|
+
|
29
|
+
it 'does not try to deliver anything' do
|
30
|
+
expect(topic_synchronizer.sync).to be_true
|
31
|
+
expect(subscription_delivery).not_to have_received(:deliver)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'if there are items to deliver' do
|
36
|
+
let(:local_feed_file_path) { './spec/fixtures/feeds/digest/incomplete_broadcasted_items' }
|
37
|
+
let(:remote_feed_file_path) { './spec/fixtures/feeds/rss' }
|
38
|
+
|
39
|
+
context 'and the unsynchronized items were delivered successfully' do
|
40
|
+
before { allow(subscription_delivery).to receive(:deliver).
|
41
|
+
and_return true }
|
42
|
+
|
43
|
+
it 'stores the freshly synchronized feed items' do
|
44
|
+
allow(local_feed_store).to receive(:store)
|
45
|
+
|
46
|
+
topic_synchronizer.sync
|
47
|
+
|
48
|
+
expect(local_feed_store).to have_received(:store)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'and the unsynchronized items were not delivered successfully' do
|
53
|
+
before { allow(subscription_delivery).to receive(:deliver).
|
54
|
+
and_return false }
|
55
|
+
|
56
|
+
it 'returns false' do
|
57
|
+
expect(topic_synchronizer.sync).to be_a FalseClass
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'retrieves the local feed store from the global configuration if none is passed in' do
|
63
|
+
allow(Hootenanny::Configuration.instance).to receive(:default_local_feed_store)
|
64
|
+
|
65
|
+
TopicSynchronizer.new(subscriber: 'http://subscriber.com',
|
66
|
+
topic: 'http://topic.com')
|
67
|
+
|
68
|
+
expect(Hootenanny::Configuration.instance).to have_received(:default_local_feed_store)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'retrieves the remote feed store from the global configuration if none is passed in' do
|
72
|
+
allow(Hootenanny::Configuration.instance).to receive(:default_remote_feed_store)
|
73
|
+
|
74
|
+
TopicSynchronizer.new(subscriber: 'http://subscriber.com',
|
75
|
+
topic: 'http://topic.com')
|
76
|
+
|
77
|
+
expect(Hootenanny::Configuration.instance).to have_received(:default_remote_feed_store)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'retrieves the remote feed store from the global configuration if none is passed in' do
|
81
|
+
allow(Hootenanny::Configuration.instance).to receive(:default_remote_feed_store)
|
82
|
+
|
83
|
+
TopicSynchronizer.new(subscriber: 'http://subscriber.com',
|
84
|
+
topic: 'http://topic.com')
|
85
|
+
|
86
|
+
expect(Hootenanny::Configuration.instance).to have_received(:default_remote_feed_store)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'retrieves the subscription delivery from the global configuration if none is passed in' do
|
90
|
+
allow(Hootenanny::Configuration.instance).to receive(:default_subscription_delivery)
|
91
|
+
|
92
|
+
TopicSynchronizer.new(subscriber: 'http://subscriber.com',
|
93
|
+
topic: 'http://topic.com')
|
94
|
+
|
95
|
+
expect(Hootenanny::Configuration.instance).to have_received(:default_subscription_delivery)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rspectacular'
|
2
|
+
require 'hootenanny/uri'
|
3
|
+
|
4
|
+
module Hootenanny
|
5
|
+
describe URI do
|
6
|
+
it 'removes the anchor portion when parsing a URI' do
|
7
|
+
uri = URI.parse('http://example.com#anchor')
|
8
|
+
|
9
|
+
expect(uri.to_s).to eql 'http://example.com'
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'properly handles the error when the URI is invalid' do
|
13
|
+
expect { URI.parse('http://example.com!!!!!!!!') }.to raise_error Hootenanny::URI::InvalidError
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'allows HTTP URIs' do
|
17
|
+
uri = URI.parse('http://example.com')
|
18
|
+
|
19
|
+
expect(uri.to_s).to eql 'http://example.com'
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'allows HTTPS URIs' do
|
23
|
+
uri = URI.parse('https://example.com')
|
24
|
+
|
25
|
+
expect(uri.to_s).to eql 'https://example.com'
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'does not allow URIs other than HTTP or HTTPS' do
|
29
|
+
expect { URI.parse('ftp://example.com') }.to raise_error Hootenanny::URI::InvalidSchemeError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|