lucid-cumulus 0.11.2 → 0.11.3

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.
@@ -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