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,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
|