dispatch-rider 1.4.0 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
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