lucid-cumulus 0.11.2 → 0.11.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ module Cumulus
2
+ module Test
3
+ module MockedLoader
4
+ # keep a map of the paths we've stubbed to the arrays of json they
5
+ # should return
6
+ @@stubbed_directories = {}
7
+
8
+ # keep a map of the files we've stubbed to the contents of those
9
+ # files
10
+ @@stubbed_files = {}
11
+
12
+ def self.included(base)
13
+ base.instance_eval do
14
+ def stub_directory(path, json)
15
+ @@stubbed_directories[path] = json
16
+ end
17
+
18
+ def stub_file(path, json)
19
+ @@stubbed_files[path] = json
20
+ end
21
+
22
+ def resources(dir, json = true, &individual_loader)
23
+ @@stubbed_directories[dir].map do |json|
24
+ individual_loader.call(json[:name], json[:value])
25
+ end
26
+ end
27
+
28
+ def resource(file, dir, json = true, &loader)
29
+ path = File.join(dir, file)
30
+ contents = @@stubbed_files[path]
31
+ loader.call(path, if json then JSON.parse(contents) else contents end)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,17 @@
1
+ require "util/StatusCodes"
2
+
3
+ module Cumulus
4
+ module Test
5
+ module MockedStatusCodes
6
+ def self.included(base)
7
+ base.instance_eval do
8
+ def set_status(status)
9
+ if status == StatusCodes::EXCEPTION
10
+ @@CURRENT_STATUS = StatusCodes::EXCEPTION
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,5 @@
1
+ RSpec.configure do |c|
2
+ c.fail_fast = true
3
+ c.color = true
4
+ c.formatter = "documentation"
5
+ end
@@ -0,0 +1,231 @@
1
+ require "conf/Configuration"
2
+ require "mocks/MockedConfiguration"
3
+ Cumulus::Configuration.send :include, Cumulus::Test::MockedConfiguration
4
+
5
+ require "common/BaseLoader"
6
+ require "mocks/MockedLoader"
7
+ Cumulus::Common::BaseLoader.send :include, Cumulus::Test::MockedLoader
8
+
9
+ require "common/manager/Manager"
10
+ require "util/ManagerUtil"
11
+ Cumulus::Common::Manager.send :include, Cumulus::Test::ManagerUtil
12
+
13
+ require "util/StatusCodes"
14
+ require "mocks/MockedStatusCodes"
15
+ Cumulus::StatusCodes.send :include, Cumulus::Test::MockedStatusCodes
16
+
17
+ require "aws-sdk"
18
+ require "json"
19
+ require "sqs/manager/Manager"
20
+ require "sqs/SQS"
21
+ require "util/DeepMerge"
22
+
23
+ module Cumulus
24
+ module Test
25
+ # Monkey patch Cumulus::SQS such that the cached values from the AWS client
26
+ # can be reset between tests.
27
+ module ResetSQS
28
+ def self.included(base)
29
+ base.instance_eval do
30
+ def reset_queue_urls
31
+ @queue_urls = nil
32
+ end
33
+
34
+ def reset_queue_attributes
35
+ @queue_attributes = nil
36
+ end
37
+
38
+ def client=(client)
39
+ @@client = client
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+ # Monkey patch Cumulus::SQS such that the queue_arns contain the ARN for the
46
+ # default dead letter target
47
+ module DeadLetterArn
48
+ def self.included(base)
49
+ base.instance_eval do
50
+ singleton_class.send(:alias_method, :original_queue_arns, :queue_arns)
51
+
52
+ def queue_arns
53
+ arns = original_queue_arns
54
+ arns[SQS::DEFAULT_DEAD_LETTER_TARGET] = SQS::DEFAULT_DEAD_LETTER_TARGET
55
+ arns[SQS::DEFAULT_DEAD_LETTER_TARGET + "a"] = SQS::DEFAULT_DEAD_LETTER_TARGET + "a"
56
+ arns
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ module SQS
63
+ @queue_directory = "/mocked/sqs/queues"
64
+ @policy_directory = "/mocked/sqs/policies"
65
+ @default_policy_name = "example-policy"
66
+
67
+ DEFAULT_QUEUE_DELAY = 0
68
+ DEFAULT_QUEUE_MESSAGE_SIZE = 262144
69
+ DEFAULT_QUEUE_MESSAGE_RETENTION = 345600
70
+ DEFAULT_QUEUE_WAIT_TIME = 0
71
+ DEFAULT_QUEUE_VISIBILITY = 30
72
+ DEFAULT_QUEUE_POLICY = "{}"
73
+ DEFAULT_DEAD_LETTER_TARGET = "queue-2"
74
+ DEFAULT_DEAD_LETTER_RECEIVES = 3
75
+
76
+ @default_queue_attributes = {
77
+ "delay" => DEFAULT_QUEUE_DELAY,
78
+ "max-message-size" => DEFAULT_QUEUE_MESSAGE_SIZE,
79
+ "message-retention" => DEFAULT_QUEUE_MESSAGE_RETENTION,
80
+ "policy" => @default_policy_name,
81
+ "receive-wait-time" => DEFAULT_QUEUE_WAIT_TIME,
82
+ "visibility-timeout" => DEFAULT_QUEUE_VISIBILITY,
83
+ "dead-letter" => {
84
+ "target" => DEFAULT_DEAD_LETTER_TARGET,
85
+ "max-receives" => DEFAULT_DEAD_LETTER_RECEIVES
86
+ },
87
+ }
88
+
89
+ @default_aws_queue_attributes = {
90
+ "DelaySeconds" => "#{DEFAULT_QUEUE_DELAY}",
91
+ "MaximumMessageSize" => "#{DEFAULT_QUEUE_MESSAGE_SIZE}",
92
+ "MessageRetentionPeriod" => "#{DEFAULT_QUEUE_MESSAGE_RETENTION}",
93
+ "ReceiveMessageWaitTimeSeconds" => "#{DEFAULT_QUEUE_WAIT_TIME}",
94
+ "VisibilityTimeout" => "#{DEFAULT_QUEUE_VISIBILITY}",
95
+ "Policy" => DEFAULT_QUEUE_POLICY,
96
+ "RedrivePolicy" => JSON.generate({
97
+ "deadLetterTargetArn" => DEFAULT_DEAD_LETTER_TARGET,
98
+ "maxReceiveCount" => "#{DEFAULT_DEAD_LETTER_RECEIVES}"
99
+ }),
100
+ }
101
+
102
+ # Public: Reset the SQS module in between tests
103
+ def self.reset
104
+ Cumulus::Configuration.stub
105
+
106
+ if !Cumulus::SQS.respond_to? :reset_queue_urls
107
+ Cumulus::SQS.send :include, Cumulus::Test::ResetSQS
108
+ end
109
+
110
+ if !Cumulus::SQS.respond_to? :original_queue_arns
111
+ Cumulus::SQS.send :include, Cumulus::Test::DeadLetterArn
112
+ end
113
+
114
+ Cumulus::SQS::reset_queue_urls
115
+ Cumulus::SQS::reset_queue_attributes
116
+ end
117
+
118
+ # Public: Returns the String path of the "directory" that contains the
119
+ # policies.
120
+ def self.policy_directory
121
+ @policy_directory
122
+ end
123
+
124
+ # Public: Returns a Hash containing default queue attributes for a local
125
+ # queue definition with values overridden by the Hash passed in.
126
+ #
127
+ # overrides - optionally provide a Hash that will override default
128
+ # attributes
129
+ def self.default_queue_attributes(overrides = nil)
130
+ Util::DeepMerge.deep_merge(@default_queue_attributes, overrides)
131
+ end
132
+
133
+ # Public: Returns a Hash containing default queue attributes for an AWS
134
+ # queue definition with values overridden by the Hash passed in.
135
+ #
136
+ # overrides - optionally provide a Hash that will override default
137
+ # attributes
138
+ def self.default_aws_queue_attributes(overrides = nil)
139
+ Util::DeepMerge.deep_merge(@default_aws_queue_attributes, overrides)
140
+ end
141
+
142
+ # Public: Returns a fake queue url for a queue name
143
+ def self.queue_url(queue_name)
144
+ "http://sqs.us-east-1.amazonaws.com/123456789012/#{queue_name}"
145
+ end
146
+
147
+ # Public: Diff stubbed local configuration and stubbed AWS configuration.
148
+ #
149
+ # config - a Hash that contains two values, :local and :aws, which contain
150
+ # the values to stub out.
151
+ # :local contains :queues which is an Array of queues to stub the
152
+ # directory with, and :policies, which is an Array of policy files
153
+ # to stub out.
154
+ # :aws is a hash of method names from the AWS Client to stub mapped
155
+ # to the value the AWS Client should return
156
+ # test - a block that tests the diffs returned by the Manager class
157
+ def self.do_diff(config, &test)
158
+ self.prepare_test(config)
159
+
160
+ # get the diffs and call the tester to determine the result of the test
161
+ manager = Cumulus::SQS::Manager.new
162
+ diffs = manager.diff_strings
163
+ test.call(diffs)
164
+ end
165
+
166
+ # Public: Sync stubbed local configuration and stubbed AWS configuration.
167
+ #
168
+ # config - a Hash that contains two values, :local and :aws, which contain
169
+ # the values to stub out.
170
+ # :local contains :queues which is an Array of queues to stub the
171
+ # directory with, and :policies, which is an Array of policy files
172
+ # to stub out.
173
+ # :aws is a hash of method names from the AWS Client to stub mapped
174
+ # to the value the AWS Client should return
175
+ # test - a block that tests the AWS Client after the syncing has been done
176
+ def self.do_sync(config, &test)
177
+ self.prepare_test(config)
178
+
179
+ # get the diffs and call the tester to determine the result of the test
180
+ manager = Cumulus::SQS::Manager.new
181
+ manager.sync
182
+ test.call(Cumulus::SQS::client)
183
+ end
184
+
185
+ private
186
+
187
+ def self.prepare_test(config)
188
+ self.reset
189
+ # we'll always just stub out the default policy
190
+ Cumulus::Common::BaseLoader.stub_file(
191
+ File.join(@policy_directory, @default_policy_name),
192
+ DEFAULT_QUEUE_POLICY
193
+ )
194
+
195
+ # stub out local queues
196
+ if config[:local][:queues]
197
+ Cumulus::Common::BaseLoader.stub_directory(
198
+ @queue_directory, config[:local][:queues]
199
+ )
200
+ end
201
+
202
+ # stub out local policies
203
+ if config[:local][:policies]
204
+ config[:local][:policies].each do |policy|
205
+ Cumulus::Common::BaseLoader.stub_file(
206
+ File.join(SQS::policy_directory, policy[:name]),
207
+ policy[:value]
208
+ )
209
+ end
210
+ end
211
+
212
+ # stub out aws responses
213
+ config[:aws].map do |call, value|
214
+ if value
215
+ Cumulus::SQS::client.stub_responses(call, value)
216
+ else
217
+ Cumulus::SQS::client.stub_responses(call)
218
+ end
219
+ end
220
+ end
221
+
222
+ def self.client_spy
223
+ if !Cumulus::SQS::client.respond_to? :have_received
224
+ Cumulus::SQS::client = ClientSpy.new(Cumulus::SQS::client)
225
+ end
226
+ Cumulus::SQS::client.clear_spy
227
+ end
228
+
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,101 @@
1
+ require "mocks/ClientSpy"
2
+ require "sqs/SQSUtil"
3
+ require "sqs/models/QueueDiff"
4
+
5
+ module Cumulus
6
+ module Test
7
+ module SQS
8
+ # Public: A helper that makes defining a test for changing a single queue
9
+ # value as simple as defining some attributes. Automatically stubs local
10
+ # and AWS config and then tests that expected values are returned by the
11
+ # diffing function. The following attributes are available.
12
+ #
13
+ # Required
14
+ # @path - the path in the local configuration to the value to override.
15
+ # Each level in the JSON should be separated with "."
16
+ # @new_value - the value to change to in the local configuration
17
+ # @previous_value - the value to change from
18
+ # @attribute_name - (for sync) the name of the attribute in AWS
19
+ #
20
+ # Optional
21
+ # @queue_name - the name of the queue
22
+ # @policy - if you need to stub out a policy, provide a Hash that contains
23
+ # :name (the name of the policy file), and :value (the String contents
24
+ # of the file)
25
+ # @test_value - supply if the correct test value should be different than
26
+ # @new_value
27
+ class SingleChangeTest
28
+ include ::RSpec::Matchers
29
+ include ::RSpec::Mocks::ExampleMethods
30
+ include Cumulus::SQS::QueueChange
31
+ include Cumulus::SQS::DeadLetterChange
32
+
33
+ def initialize(&init)
34
+ @queue_name = "queue-name"
35
+ instance_eval(&init)
36
+ end
37
+
38
+ def self.execute(&init)
39
+ test = new(&init)
40
+ test.execute
41
+ end
42
+
43
+ def execute
44
+ SQS::do_diff(get_config) do |diffs|
45
+ diff_strings = diffs.map(&:to_s).join("\n").split("\n").map(&:strip)
46
+ expect(diff_strings).to eq @message
47
+ end
48
+ end
49
+
50
+ def self.execute_sync(&init)
51
+ test = new(&init)
52
+ test.execute_sync
53
+ end
54
+
55
+ def execute_sync
56
+ SQS::client_spy
57
+ SQS::do_sync(get_config) do |client|
58
+ set_attributes = client.spied_method(:set_queue_attributes)
59
+ expect(set_attributes.num_calls).to eq 1
60
+ arguments = set_attributes.arguments[0]
61
+ expect(arguments[:queue_url]).to eq Test::SQS::queue_url(@queue_name)
62
+
63
+ if @full_test_value
64
+ expect(arguments[:attributes]).to eq @full_test_value
65
+ else
66
+ expect(arguments[:attributes]).to eq ({ @attribute_name => @test_value || @new_value })
67
+ end
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def get_config
74
+ keys = @path.split('.')
75
+ overrides = {}
76
+ keys.reduce(overrides) do |h, k|
77
+ h[k] = if k == keys.last then @new_value else {} end
78
+ end
79
+
80
+ {
81
+ local: {
82
+ queues: [{
83
+ name: @queue_name,
84
+ value: SQS::default_queue_attributes(overrides)
85
+ }],
86
+ policies: if @policy then [{
87
+ name: @policy[:name],
88
+ value: @policy[:value],
89
+ }] end
90
+ },
91
+ aws: {
92
+ list_queues: {queue_urls: [SQS::queue_url(@queue_name)]},
93
+ get_queue_attributes: {attributes: SQS::default_aws_queue_attributes}
94
+ }
95
+ }
96
+ end
97
+
98
+ end
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,152 @@
1
+ require "sqs/SQSUtil"
2
+ require "sqs/SingleChangeTest"
3
+
4
+ module Cumulus
5
+ module Test
6
+ module SQS
7
+ describe Cumulus::SQS::Manager do
8
+ context "The SQS module's diffing functionality" do
9
+ it "should detect new queues defined locally" do
10
+ queue_name = "not-in-aws"
11
+ SQS::do_diff({
12
+ local: {queues: [{name: queue_name, value: {}}]},
13
+ aws: {list_queues: nil},
14
+ }) do |diffs|
15
+ expect(diffs.size).to eq 1
16
+ expect(diffs[0].to_s).to eq "Queue #{queue_name} will be created."
17
+ end
18
+ end
19
+
20
+ it "should detect new queues added in AWS" do
21
+ queue_name = "only-in-aws"
22
+ SQS::do_diff({
23
+ local: {queues: []},
24
+ aws: {
25
+ list_queues: {queue_urls: [SQS::queue_url(queue_name)]},
26
+ get_queue_attributes: {},
27
+ }
28
+ }) do |diffs|
29
+ expect(diffs.size).to eq 1
30
+ expect(diffs[0].to_s).to eq "Queue #{queue_name} is not managed by Cumulus."
31
+ end
32
+ end
33
+
34
+ it "should detect changes in delay" do
35
+ SingleChangeTest.execute do
36
+ @path = "delay"
37
+ @new_value = SQS::DEFAULT_QUEUE_DELAY - 1
38
+ @previous_value = SQS::DEFAULT_QUEUE_DELAY
39
+ @message = [
40
+ "Delay",
41
+ "AWS - #{@previous_value} seconds",
42
+ "Local - #{@new_value} seconds"
43
+ ]
44
+ end
45
+ end
46
+
47
+ it "should detect changes in max-message-size" do
48
+ SingleChangeTest.execute do
49
+ @path = "max-message-size"
50
+ @new_value = SQS::DEFAULT_QUEUE_MESSAGE_SIZE - 1
51
+ @previous_value = SQS::DEFAULT_QUEUE_MESSAGE_SIZE
52
+ @message = [
53
+ "Max Message Size",
54
+ "AWS - #{@previous_value} bytes",
55
+ "Local - #{@new_value} bytes"
56
+ ]
57
+ end
58
+ end
59
+
60
+ it "should detect changes in message-retention" do
61
+ SingleChangeTest.execute do
62
+ @path = "message-retention"
63
+ @new_value = SQS::DEFAULT_QUEUE_MESSAGE_RETENTION - 1
64
+ @previous_value = SQS::DEFAULT_QUEUE_MESSAGE_RETENTION
65
+ @message = [
66
+ "Message Retention Period",
67
+ "AWS - #{@previous_value} seconds",
68
+ "Local - #{@new_value} seconds"
69
+ ]
70
+ end
71
+ end
72
+
73
+ it "should detect changes in receive-wait-time" do
74
+ SingleChangeTest.execute do
75
+ @path = "receive-wait-time"
76
+ @new_value = SQS::DEFAULT_QUEUE_WAIT_TIME - 1
77
+ @previous_value = SQS::DEFAULT_QUEUE_WAIT_TIME
78
+ @message = [
79
+ "Receive Wait Time",
80
+ "AWS - #{@previous_value} seconds",
81
+ "Local - #{@new_value} seconds"
82
+ ]
83
+ end
84
+ end
85
+
86
+ it "should detect changes in visibility-timeout" do
87
+ SingleChangeTest.execute do
88
+ @path = "visibility-timeout"
89
+ @new_value = SQS::DEFAULT_QUEUE_VISIBILITY - 1
90
+ @previous_value = SQS::DEFAULT_QUEUE_VISIBILITY
91
+ @message = [
92
+ "Message Visibility",
93
+ "AWS - #{@previous_value} seconds",
94
+ "Local - #{@new_value} seconds"
95
+ ]
96
+ end
97
+ end
98
+
99
+ it "should detect changes in policy" do
100
+ new_value = "example-policy-2"
101
+ new_contents = "{\"a\":\"b\"}"
102
+ SingleChangeTest.execute do
103
+ @path = "policy"
104
+ @policy = {
105
+ name: new_value,
106
+ value: new_contents,
107
+ }
108
+ @new_value = new_value
109
+ @previous_value = JSON.parse(SQS::DEFAULT_QUEUE_POLICY)
110
+ test_value = JSON.pretty_generate(JSON.parse(new_contents)).split("\n").map(&:strip)
111
+ @message = [
112
+ "Policy:",
113
+ "Removing:",
114
+ JSON.pretty_generate(@previous_value).split("\n"),
115
+ "Adding:",
116
+ test_value
117
+ ].flatten
118
+ end
119
+ end
120
+
121
+ it "should detect changes in dead-letter target" do
122
+ SingleChangeTest.execute do
123
+ @path = "dead-letter.target"
124
+ @new_value = SQS::DEFAULT_DEAD_LETTER_TARGET + "a"
125
+ @previous_value = SQS::DEFAULT_DEAD_LETTER_TARGET
126
+ @message = [
127
+ "Dead Letter Queue",
128
+ "Target:",
129
+ "AWS - #{@previous_value}",
130
+ "Local - #{@new_value}"
131
+ ]
132
+ end
133
+ end
134
+
135
+ it "should detect changes in dead-letter max-receives" do
136
+ SingleChangeTest.execute do
137
+ @path = "dead-letter.max-receives"
138
+ @new_value = SQS::DEFAULT_DEAD_LETTER_RECEIVES + 1
139
+ @previous_value = SQS::DEFAULT_DEAD_LETTER_RECEIVES
140
+ @message = [
141
+ "Dead Letter Queue",
142
+ "Max Receive Count:",
143
+ "AWS - #{@previous_value}",
144
+ "Local - #{@new_value}"
145
+ ]
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end