dispatch-rider 1.4.0 → 1.4.2

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.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +20 -0
  3. data/.hound.yml +2 -0
  4. data/.rubocop.yml +50 -0
  5. data/.travis.yml +6 -0
  6. data/CHANGELOG.md +363 -0
  7. data/Gemfile +25 -0
  8. data/LICENSE.txt +1 -1
  9. data/README.md +32 -3
  10. data/Rakefile +38 -0
  11. data/dispatch-rider.gemspec +46 -0
  12. data/lib/dispatch-rider/notification_services/aws_sns.rb +9 -0
  13. data/lib/dispatch-rider/version.rb +1 -1
  14. data/spec/fixtures/handlers/another_test_handler.rb +2 -0
  15. data/spec/fixtures/handlers/test_handler.rb +2 -0
  16. data/spec/lib/dispatch-rider/airbrake_error_handler_spec.rb +16 -0
  17. data/spec/lib/dispatch-rider/callbacks/access_spec.rb +62 -0
  18. data/spec/lib/dispatch-rider/callbacks/storage_spec.rb +43 -0
  19. data/spec/lib/dispatch-rider/configuration_spec.rb +74 -0
  20. data/spec/lib/dispatch-rider/default_error_handler_spec.rb +14 -0
  21. data/spec/lib/dispatch-rider/demultiplexer_spec.rb +117 -0
  22. data/spec/lib/dispatch-rider/dispatcher_spec.rb +69 -0
  23. data/spec/lib/dispatch-rider/handlers/base_spec.rb +81 -0
  24. data/spec/lib/dispatch-rider/handlers/inheritance_tracking_spec.rb +27 -0
  25. data/spec/lib/dispatch-rider/message_spec.rb +59 -0
  26. data/spec/lib/dispatch-rider/notification_services/aws_sns_spec.rb +28 -0
  27. data/spec/lib/dispatch-rider/notification_services/base_spec.rb +65 -0
  28. data/spec/lib/dispatch-rider/notification_services/file_system/channel_spec.rb +28 -0
  29. data/spec/lib/dispatch-rider/notification_services/file_system/notifier_spec.rb +14 -0
  30. data/spec/lib/dispatch-rider/notification_services/file_system_spec.rb +23 -0
  31. data/spec/lib/dispatch-rider/notification_services_spec.rb +4 -0
  32. data/spec/lib/dispatch-rider/publisher/base_spec.rb +79 -0
  33. data/spec/lib/dispatch-rider/publisher/configuration/destination_spec.rb +100 -0
  34. data/spec/lib/dispatch-rider/publisher/configuration/notification_service_spec.rb +53 -0
  35. data/spec/lib/dispatch-rider/publisher/configuration_reader_spec.rb +129 -0
  36. data/spec/lib/dispatch-rider/publisher/configuration_spec.rb +149 -0
  37. data/spec/lib/dispatch-rider/publisher/configuration_support_spec.rb +89 -0
  38. data/spec/lib/dispatch-rider/publisher_spec.rb +123 -0
  39. data/spec/lib/dispatch-rider/queue_services/aws_sqs_spec.rb +193 -0
  40. data/spec/lib/dispatch-rider/queue_services/base_spec.rb +147 -0
  41. data/spec/lib/dispatch-rider/queue_services/file_system_spec.rb +88 -0
  42. data/spec/lib/dispatch-rider/queue_services/received_message_spec.rb +23 -0
  43. data/spec/lib/dispatch-rider/queue_services/simple_spec.rb +63 -0
  44. data/spec/lib/dispatch-rider/queue_services_spec.rb +6 -0
  45. data/spec/lib/dispatch-rider/registrars/base_spec.rb +68 -0
  46. data/spec/lib/dispatch-rider/registrars/file_system_channel_spec.rb +12 -0
  47. data/spec/lib/dispatch-rider/registrars/handler_spec.rb +16 -0
  48. data/spec/lib/dispatch-rider/registrars/notification_service_spec.rb +13 -0
  49. data/spec/lib/dispatch-rider/registrars/publishing_destination_spec.rb +11 -0
  50. data/spec/lib/dispatch-rider/registrars/queue_service_spec.rb +13 -0
  51. data/spec/lib/dispatch-rider/registrars/sns_channel_spec.rb +14 -0
  52. data/spec/lib/dispatch-rider/registrars_spec.rb +4 -0
  53. data/spec/lib/dispatch-rider/runner_spec.rb +25 -0
  54. data/spec/lib/dispatch-rider/subscriber_spec.rb +140 -0
  55. data/spec/lib/dispatch-rider_spec.rb +27 -0
  56. data/spec/spec_helper.rb +21 -0
  57. metadata +107 -86
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::Publisher do
4
+
5
+ subject do
6
+ described_class.new
7
+ end
8
+
9
+ describe "#initialize" do
10
+ it "assigns the notification service registrar" do
11
+ subject.notification_service_registrar.store.should be_empty
12
+ end
13
+
14
+ it "assigns a publishing destination registrar" do
15
+ subject.publishing_destination_registrar.store.should be_empty
16
+ end
17
+
18
+ it "assigns a service channel mapper" do
19
+ subject.service_channel_mapper.destination_registrar.store.should be_empty
20
+ end
21
+
22
+ context "when not passing a configuration" do
23
+ it "loads the global configuration" do
24
+ DispatchRider::Publisher::ConfigurationReader.should_receive(:load_config).with(described_class.configuration, subject)
25
+ end
26
+ end
27
+
28
+ context "when passing a configuration" do
29
+ let(:configuration){ DispatchRider::Publisher::Configuration.new }
30
+
31
+ subject{ described_class.new(configuration) }
32
+
33
+ it "loads the configuration" do
34
+ DispatchRider::Publisher::ConfigurationReader.should_receive(:load_config).with(configuration, subject)
35
+ end
36
+ end
37
+ end
38
+
39
+ describe "#register_notification_service" do
40
+ it "registers a notification service" do
41
+ subject.register_notification_service(:aws_sns)
42
+ result = subject.notification_service_registrar.fetch(:aws_sns)
43
+ result.notifier.should respond_to(:topics)
44
+ result.channel_registrar.store.should be_empty
45
+ end
46
+
47
+ it "returns the publisher" do
48
+ subject.register_notification_service(:aws_sns).should eq(subject)
49
+ end
50
+ end
51
+
52
+ describe "#register_channel" do
53
+ before :each do
54
+ subject.register_notification_service(:aws_sns)
55
+ end
56
+
57
+ let(:notification_service) { subject.notification_service_registrar.fetch(:aws_sns) }
58
+
59
+ it "registers a channel for the notification service" do
60
+ subject.register_channel(:aws_sns, :foo, account: 123, region: "us-east-1", topic: "PlanOfAttack")
61
+ notification_service.channel_registrar.fetch(:foo).should eq('arn:aws:sns:us-east-1:123:PlanOfAttack')
62
+ end
63
+
64
+ it "returns the publisher" do
65
+ subject.register_channel(:aws_sns, :foo).should eq(subject)
66
+ end
67
+ end
68
+
69
+ describe "#register_destination" do
70
+ before :each do
71
+ subject.register_notification_service(:aws_sns)
72
+ end
73
+
74
+ it "registers the destination to be published to" do
75
+ subject.register_destination(:sns_foo, :aws_sns, :foo, account: 123, region: "us-east-1", topic: "PlanOfAttack")
76
+ result = subject.publishing_destination_registrar.fetch(:sns_foo)
77
+ result.service.should eq(:aws_sns)
78
+ result.channel.should eq(:foo)
79
+ end
80
+
81
+ it "returns the publisher" do
82
+ subject.register_destination(:sns_foo, :aws_sns, :foo, account: 123, region: "us-east-1", topic: "PlanOfAttack").should eq(subject)
83
+ end
84
+ end
85
+
86
+ describe "#publish" do
87
+ let :notifier do
88
+ subject.notification_service_registrar.fetch(:aws_sns).notifier
89
+ end
90
+
91
+ before :each do
92
+ subject.register_notification_service(:file_system)
93
+ subject.register_destination(:fs_foo, :file_system, :foo, path: "tmp/test_queue")
94
+ end
95
+
96
+ around do |ex|
97
+ begin
98
+ DispatchRider.config.debug = true
99
+ ex.call
100
+ ensure
101
+ DispatchRider.config.debug = false
102
+ end
103
+ end
104
+
105
+ it "publishes the message to the notification service" do
106
+ existing = Dir['tmp/test_queue/*']
107
+ expect {
108
+ subject.publish(:destinations => [:fs_foo], :message => {:subject => "bar_handler", :body => {"bar" => "baz"}})
109
+ }.to change { Dir['tmp/test_queue/*'].length }.by(1)
110
+ new_job = Dir['tmp/test_queue/*'] - existing
111
+ data = JSON.load(File.read(new_job.first))
112
+
113
+ expected_message = {
114
+ "subject" => "bar_handler",
115
+ "body" => {
116
+ "guid" => DispatchRider::Debug::PUBLISHER_MESSAGE_GUID,
117
+ "bar" => "baz",
118
+ },
119
+ }
120
+ expect(data).to eq(expected_message)
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,193 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::QueueServices::AwsSqs do
4
+
5
+ let(:visibility_timeout) { 100 }
6
+
7
+ let(:fake_response) do
8
+ AWS::SQS::Client.new.stub_for(:get_queue_url).tap { |response|
9
+ response.data[:queue_url] = "the.queue.url"
10
+ response.data[:attributes] = { "VisibilityTimeout" => visibility_timeout }
11
+ }
12
+ end
13
+
14
+ before do
15
+ AWS.config(stub_requests: true)
16
+ AWS::SQS::Client.any_instance.stub(:client_request).and_return(fake_response)
17
+ end
18
+
19
+ subject(:aws_sqs_queue) do
20
+ DispatchRider::QueueServices::AwsSqs.new(:name => "normal_priority")
21
+ end
22
+
23
+ describe "#assign_storage" do
24
+ context "when the aws gem is installed" do
25
+
26
+ context "when the name of the queue is passed in the options" do
27
+ it "should return an instance representing the aws sqs queue" do
28
+ aws_sqs_queue.assign_storage(:name => 'normal_priority')
29
+ aws_sqs_queue.queue.url.should eq('the.queue.url')
30
+ end
31
+ end
32
+
33
+ context "when the url of the queue is passed in the options" do
34
+ it "should return an instance representing the aws sqs queue" do
35
+ aws_sqs_queue.assign_storage(:url => 'https://sqs.us-east-1.amazonaws.com/12345/QueueName')
36
+ aws_sqs_queue.queue.url.should eq('the.queue.url')
37
+ end
38
+ end
39
+
40
+ context "when neither the name nor the url of the queue is assed in the options" do
41
+ it "should raise an exception" do
42
+ expect { aws_sqs_queue.assign_storage(:foo => 'bar') }.to raise_exception(DispatchRider::RecordInvalid)
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "#insert" do
49
+ it "should insert an item into the queue" do
50
+ obj = {'subject' => 'foo', 'body' => 'bar'}.to_json
51
+ aws_sqs_queue.queue.should_receive(:send_message).with(obj)
52
+ aws_sqs_queue.insert(obj)
53
+ end
54
+ end
55
+
56
+ describe "#pop" do
57
+ context "when the sqs queue has items in it" do
58
+ let(:response_attributes) do
59
+ {
60
+ "SenderId" => "123456789012",
61
+ "SentTimestamp" => Time.now.to_i.to_s,
62
+ "ApproximateReceivedCount" => "12",
63
+ "ApproximateFirstReceiveTimestamp" => (Time.now + 12).to_i.to_s,
64
+ }
65
+ end
66
+
67
+ let(:response_message) do
68
+ {
69
+ :message_id => 12345,
70
+ :md5_of_body => "mmmddd555",
71
+ :body => {:subject => "foo", :body => {:bar => "baz"}}.to_json,
72
+ :receipt_handle => "HANDLE",
73
+ :attributes => response_attributes,
74
+ }
75
+ end
76
+
77
+ before :each do
78
+ response = AWS::SQS::Client.new.stub_for(:receive_message)
79
+ response.data[:messages] = [response_message]
80
+ AWS::SQS::Client::V20121105.any_instance.stub(:receive_message).and_return(response)
81
+ AWS::SQS::Queue.any_instance.stub(:verify_receive_message_checksum).and_return([])
82
+ end
83
+
84
+ context "when the block runs faster than the timeout" do
85
+ it "should yield the first item in the queue" do
86
+ aws_sqs_queue.pop do |message|
87
+ message.subject.should eq('foo')
88
+ message.body.should eq({'bar' => 'baz'})
89
+ end
90
+ end
91
+ end
92
+
93
+ context "when the block runs slower than the timeout" do
94
+ let(:visibility_timeout) { 1 }
95
+
96
+ it "should raise" do
97
+ expect {
98
+ aws_sqs_queue.pop do |message|
99
+ sleep(1.1)
100
+ end
101
+ }.to raise_exception(/message: foo,.+ took .+ seconds while the timeout was 1/)
102
+ end
103
+ end
104
+
105
+ end
106
+
107
+ context "when the sqs queue is empty" do
108
+ before :each do
109
+ aws_sqs_queue.queue.stub(:receive_message).and_return(nil)
110
+ end
111
+
112
+ it "should not yield" do
113
+ expect { |b|
114
+ aws_sqs_queue.pop(&b)
115
+ }.not_to yield_control
116
+ end
117
+ end
118
+
119
+ end
120
+
121
+ describe "received message methods" do
122
+ let(:response_attributes) {{
123
+ "SenderId" => "123456789012",
124
+ "SentTimestamp" => Time.now.to_i.to_s,
125
+ "ApproximateReceivedCount" => "12",
126
+ "ApproximateFirstReceiveTimestamp" => (Time.now + 12).to_i.to_s,
127
+ }}
128
+
129
+ let(:response_message) { {
130
+ :message_id => 12345,
131
+ :md5_of_body => "mmmddd555",
132
+ :body => {:subject => "foo", :body => {:bar => "baz"}}.to_json,
133
+ :receipt_handle => "HANDLE",
134
+ :attributes => response_attributes,
135
+ } }
136
+
137
+ before :each do
138
+ response = AWS::SQS::Client.new.stub_for(:receive_message)
139
+ response.data[:messages] = [response_message]
140
+ AWS::SQS::Client::V20121105.any_instance.stub(:receive_message).and_return(response)
141
+ AWS::SQS::Queue.any_instance.stub(:verify_receive_message_checksum).and_return([])
142
+ end
143
+
144
+ it "should set the visibility timeout when extend is called" do
145
+ AWS::SQS::ReceivedMessage.any_instance.should_receive(:visibility_timeout=).with(10)
146
+ AWS::SQS::ReceivedMessage.any_instance.should_receive(:visibility_timeout=).with(0)
147
+ aws_sqs_queue.pop do |message|
148
+ message.extend_timeout(10)
149
+ message.total_timeout.should eq(10)
150
+ message.return_to_queue
151
+ message.total_timeout.should eq(10)
152
+ end
153
+ end
154
+ end
155
+
156
+ describe "#construct_message_from" do
157
+ context "when the item is directly published to AWS::SQS" do
158
+ let(:sqs_message){ OpenStruct.new(:body => {'subject' => 'foo', 'body' => 'bar'}.to_json) }
159
+
160
+ it "should return a message" do
161
+ result = aws_sqs_queue.construct_message_from(sqs_message)
162
+ result.subject.should eq('foo')
163
+ result.body.should eq('bar')
164
+ end
165
+ end
166
+
167
+ context "when the item is published through AWS::SNS" do
168
+ let(:sqs_message){ OpenStruct.new(:body => {"Type" => "Notification", "Message" => {'subject' => 'foo', 'body' => 'bar'}.to_json}.to_json) }
169
+
170
+ it "should return a message" do
171
+ result = aws_sqs_queue.construct_message_from(sqs_message)
172
+ result.subject.should eq('foo')
173
+ result.body.should eq('bar')
174
+ end
175
+ end
176
+ end
177
+
178
+ describe "#delete" do
179
+ let(:item_in_queue){ Object.new }
180
+
181
+ it "should delete the first message from the queue" do
182
+ item_in_queue.should_receive(:delete)
183
+ aws_sqs_queue.delete(item_in_queue)
184
+ end
185
+ end
186
+
187
+ describe "#size" do
188
+ it "should return the size of the aws queue" do
189
+ aws_sqs_queue.queue.should_receive(:approximate_number_of_messages)
190
+ aws_sqs_queue.size
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,147 @@
1
+ require 'spec_helper'
2
+
3
+ describe DispatchRider::QueueServices::Base do
4
+ subject(:base_queue) do
5
+ DispatchRider::QueueServices::Base.any_instance.stub(:assign_storage).and_return([])
6
+ DispatchRider::QueueServices::Base.new
7
+ end
8
+
9
+ describe "#initialize" do
10
+ it "should initiate a queue" do
11
+ base_queue.queue.should eq([])
12
+ end
13
+ end
14
+
15
+ describe "#push" do
16
+ subject(:simple_queue) { DispatchRider::QueueServices::Simple.new }
17
+
18
+ it "should push the serialized object to the queue" do
19
+ simple_queue.push(DispatchRider::Message.new(:subject => "foo", :body => "bar"))
20
+ result = JSON.parse(simple_queue.queue.first)
21
+ result['subject'].should eq('foo')
22
+ result['body'].should eq('bar')
23
+ end
24
+ end
25
+
26
+ describe "#insert" do
27
+ it "should raise an exception" do
28
+ expect { base_queue.insert(DispatchRider::Message.new(:subject => "foo", :body => "bar")) }.to raise_exception(NotImplementedError)
29
+ end
30
+ end
31
+
32
+ describe "#pop" do
33
+ subject(:simple_queue) { DispatchRider::QueueServices::Simple.new }
34
+
35
+ before :each do
36
+ simple_queue.queue.push(DispatchRider::Message.new(:subject => "foo", :body => "bar").to_json)
37
+ end
38
+
39
+ context "when the block passed to process the popped message returns true" do
40
+ it "should return the first message in the queue" do
41
+ simple_queue.pop {|msg| true}.should eq(DispatchRider::Message.new(:subject => 'foo', :body => 'bar'))
42
+ end
43
+ end
44
+
45
+ context "when the block passed to process the popped message returns false" do
46
+ it "should return the first message in the queue" do
47
+ simple_queue.pop {|msg| false}.should eq(DispatchRider::Message.new(:subject => 'foo', :body => 'bar'))
48
+ end
49
+
50
+ it "should not remove the first message from the queue" do
51
+ simple_queue.pop do |msg|
52
+ msg.body = {:bar => "baz"}
53
+ false
54
+ end
55
+ simple_queue.should_not be_empty
56
+ end
57
+ end
58
+
59
+ context "when the queue is empty" do
60
+ before :each do
61
+ simple_queue.queue = []
62
+ end
63
+
64
+ it "should return nil" do
65
+ simple_queue.pop do |msg|
66
+ msg.body = {:bar => "baz"}
67
+ true
68
+ end.should be_nil
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#head" do
74
+ before { base_queue.stub(:raw_head).and_return(new_item) }
75
+
76
+ context "when there is no new item" do
77
+ let(:new_item){ nil }
78
+
79
+ it "should raise an exception" do
80
+ base_queue.head.should be_nil
81
+ end
82
+ end
83
+
84
+ context "when a new item exists" do
85
+ before { base_queue.stub(:construct_message_from){|item| item.message} }
86
+
87
+ let(:new_item){ OpenStruct.new(:message => new_message) }
88
+ let(:new_message){ :the_message }
89
+
90
+ it "should return the expected message" do
91
+ received_head = base_queue.head
92
+ received_head.item.should == new_item
93
+ received_head.to_sym == new_message
94
+ end
95
+ end
96
+ end
97
+
98
+ describe "#raw_head" do
99
+ it "should raise an exception" do
100
+ expect { base_queue.raw_head }.to raise_exception(NotImplementedError)
101
+ end
102
+ end
103
+
104
+ describe "#construct_message_from" do
105
+ it "should raise an exception" do
106
+ expect { base_queue.construct_message_from({:subject => 'foo', :body => {:bar => 'baz'}}.to_json) }.to raise_exception(NotImplementedError)
107
+ end
108
+ end
109
+
110
+ describe "#delete" do
111
+ it "should raise an exception" do
112
+ expect { base_queue.delete({:subject => 'foo', :body => {:bar => 'baz'}}.to_json) }.to raise_exception(NotImplementedError)
113
+ end
114
+ end
115
+
116
+ describe "#empty?" do
117
+ before :each do
118
+ base_queue.stub(:size) { base_queue.queue.size }
119
+ end
120
+
121
+ context "when the queue is empty" do
122
+ before :each do
123
+ base_queue.queue = []
124
+ end
125
+
126
+ it "should return true" do
127
+ base_queue.should be_empty
128
+ end
129
+ end
130
+
131
+ context "when the queue is not empty" do
132
+ before :each do
133
+ base_queue.queue << {:subject => 'foo', :body => 'bar'}.to_json
134
+ end
135
+
136
+ it "should return false" do
137
+ base_queue.should_not be_empty
138
+ end
139
+ end
140
+ end
141
+
142
+ describe "#size" do
143
+ it "should raise an exception" do
144
+ expect { base_queue.size }.to raise_exception(NotImplementedError)
145
+ end
146
+ end
147
+ end