fake_sqs 0.3.1 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb815a43a9c8361770d93419e8516519de9b0e65
4
- data.tar.gz: df4679c9d5b121a6b0a18d811d3839104ab4415b
3
+ metadata.gz: '063098d4ed74f09793d0bde5c6fb857256893bec'
4
+ data.tar.gz: 7ae4304c0a2d4d2a55c579730641d13a86a0656b
5
5
  SHA512:
6
- metadata.gz: 7326b4cc8acfcd59a6e08a84050bf14b3a30da1ce6e263ae44a0ec0cfcfd96acff1845675990dc74e9172617af1ff8506dc8b0a02f249ed65a492b31e8d6cf25
7
- data.tar.gz: 1898c50cfbb473f5b29100efdb99f8aa0deff88a4e9a8cbe37112af08266c51ca5de178fd907eb517c2240c76d7b6ae65b41d14b22d60c1414a106a2ea4f183c
6
+ metadata.gz: 9b19a220db375f3b500ede5450c91b51d9a4023e19253f0fe3f842867c25335d0a9977b176ad1589bd0d24f5d1e62d597ea70e9a584b624008f27ba246a48ddf
7
+ data.tar.gz: 5ef566736e06d3ebe61e43e7c2b680df2e59a008a253a0b71776ed534bb8921bea6affa5460da71a87b022f563df96be65b6c957b1ddcf0df8867bfd76376892
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .DS_Store
6
7
  Gemfile.lock
7
8
  InstalledFiles
8
9
  _yardoc
@@ -1,6 +1,4 @@
1
1
  sudo: false
2
2
  language: ruby
3
3
  rvm:
4
- - 1.9.3
5
- - 2.0.0
6
4
  - 2.2.2
@@ -0,0 +1,20 @@
1
+ # MIT License
2
+
3
+ Copyright (c) 2012 Iain Hecker
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,190 +1,28 @@
1
1
  # Fake SQS [![Build Status](https://secure.travis-ci.org/iain/fake_sqs.png)](http://travis-ci.org/iain/fake_sqs)
2
2
 
3
- Inspired by [Fake DynamoDB] [fake_dynamo], this is an AWS SQS compatible
4
- message queue that can be ran locally. This makes it ideal for integration
5
- testing, just like you would have a local database running. Fake SQS doesn't
6
- persist anything, not even the queues themselves. You'll have to create the
7
- queues everytime you start it.
3
+ Fake SQS is a lightweight server that mocks the Amazon SQS API.
8
4
 
9
- This implementation is **not complete** yet, but should be useful already.
5
+ It is extremely useful for testing SQS applications in a sandbox environment without actually
6
+ making calls to Amazon, which not only requires a network connection, but also costs
7
+ money.
10
8
 
11
- Done so far are:
9
+ Many features are supported and if you miss something, open a pull.
12
10
 
13
- * Creating queues
14
- * Deleting queues
15
- * Listing queues (with prefixes)
16
- * Get queue url via the name
17
- * Send messages (and in batch)
18
- * Receive messages (and in batch)
19
- * Deleting messages (and in batch)
20
- * Changing queue attributes (but not all, and no validation)
21
- * Setting visibility timeouts for messages
22
- * Purge Queue
23
-
24
- Certain bits are left off on purpose, to make it easier to work with, such as:
25
-
26
- * No checking on access keys or signatures
27
- * No 60 second delay between deleting a queue and recreating it.
28
-
29
- Other parts are just not done yet:
30
-
31
- * Permissions
32
- * Error handling
33
-
34
- So, actually, just the basics are implemented at this point.
35
-
36
- PS. There is also [Fake SNS] [fake_sns].
37
-
38
- ## Usage
39
-
40
- To install:
41
-
42
- ```
43
- $ gem install fake_sqs
44
- ```
45
-
46
- To start:
47
-
48
- ```
49
- $ fake_sqs
50
- ```
51
-
52
- To configure, see the options in the help:
53
-
54
- ```
55
- $ fake_sqs --help
56
- ```
57
-
58
- By default, FakeSQS uses an in-memory database (just a hash actually). To make
59
- it persistant, run with:
60
-
61
- ```
62
- $ fake_sqs --database /path/to/database.yml
63
- ```
64
-
65
- Messages are not persisted, just the queues.
66
-
67
- This is an example of how to configure the official [aws-sdk gem] [aws-sdk], to
68
- let it talk to Fake SQS.
69
-
70
- ``` ruby
71
- AWS.config(
72
- :use_ssl => false,
73
- :sqs_endpoint => "localhost",
74
- :sqs_port => 4568,
75
- :access_key_id => "access key id",
76
- :secret_access_key => "secret access key"
77
- )
78
- ```
79
-
80
- ```javascript
81
- var aws = require('aws-sdk');
82
- var sqs = new aws.SQS({
83
- endpoint: 'http://localhost:4568',
84
- apiVersion: '2012-11-05',
85
- accessKeyId: 'access key id',
86
- secretAccessKey: 'secret access key',
87
- region: 'region'
88
- });
89
- ```
90
-
91
- If you have the configuration options for other libraries, please give them to
92
- me.
93
-
94
- To reset the entire server, during tests for example, send a DELETE request to
95
- the server. For example:
96
-
97
- ```
98
- $ curl -X DELETE http://localhost:4568/
99
- ```
100
-
101
- Within SQS, after receiving, messages will be available again automatically
102
- after a certain time. While this is not implemented (for now at least), you can
103
- trigger this behavior at at will, with a PUT request.
11
+ ## Installation
104
12
 
105
13
  ```
106
- $ curl -X PUT http://localhost:4568/
107
- ```
108
-
109
-
110
- ### Test Integration
111
-
112
- When making integration tests for your app, you can easily include Fake SQS.
113
-
114
- Here are the methods you need to run FakeSQS programmatically.
115
-
116
- ``` ruby
117
- require "fake_sqs/test_integration"
118
-
119
- # globally, before the test suite starts:
120
- AWS.config(
121
- use_ssl: false,
122
- sqs_endpoint: "localhost",
123
- sqs_port: 4568,
124
- access_key_id: "fake access key",
125
- secret_access_key: "fake secret key",
126
- )
127
- fake_sqs = FakeSQS::TestIntegration.new
128
-
129
- # before each test that requires SQS:
130
- fake_sqs.start
131
-
132
- # at the end of the suite:
133
- at_exit {
134
- fake_sqs.stop
135
- }
14
+ gem install fakesqs
136
15
  ```
137
16
 
138
- By starting it like this it will start when needed, and reset between each test.
139
-
140
- Here's an example for RSpec to put in `spec/spec_helper.rb`:
141
-
142
- ``` ruby
143
- AWS.config(
144
- use_ssl: false,
145
- sqs_endpoint: "localhost",
146
- sqs_port: 4568,
147
- access_key_id: "fake access key",
148
- secret_access_key: "fake secret key",
149
- )
17
+ ## Running
150
18
 
151
- RSpec.configure do |config|
152
- config.treat_symbols_as_metadata_keys_with_true_values = true
153
- config.before(:suite) { $fake_sqs = FakeSQS::TestIntegration.new }
154
- config.before(:each, :sqs) { $fake_sqs.start }
155
- config.after(:suite) { $fake_sqs.stop }
156
- end
157
19
  ```
158
-
159
- Now you can use the `:sqs metadata to enable SQS integration:
160
-
161
- ``` ruby
162
- describe "something with sqs", :sqs do
163
- it "should work" do
164
- queue = AWS::SQS.new.queues.create("my-queue")
165
- end
166
- end
20
+ fakesqs --database /path/to/database.yml
167
21
  ```
168
22
 
169
23
  ## Development
170
24
 
171
- Run all the specs:
172
-
173
25
  ```
174
- $ rake
26
+ bundle install
27
+ rake
175
28
  ```
176
-
177
- This will run the unit tests, then the acceptance tests for both types of
178
- storage (in-memory and on disk).
179
-
180
- When debugging an acceptance test, you can run it like this, which will redirect
181
- output to the console:
182
-
183
- ```
184
- $ DEBUG=true SQS_DATABASE=tmp/sqs.yml rspec spec/acceptance
185
- ```
186
-
187
-
188
- [fake_dynamo]: https://github.com/ananthakumaran/fake_dynamo
189
- [aws-sdk]: https://github.com/amazonwebservices/aws-sdk-for-ruby
190
- [fake_sns]: https://github.com/yourkarma/fake_sns
@@ -8,7 +8,6 @@ Gem::Specification.new do |gem|
8
8
  gem.version = FakeSQS::VERSION
9
9
  gem.authors = ["iain"]
10
10
  gem.email = ["iain@iain.nl"]
11
- gem.description = %q{Provides a fake SQS server that you can run locally to test against}
12
11
  gem.summary = %q{Provides a fake SQS server that you can run locally to test against}
13
12
  gem.homepage = "https://github.com/iain/fake_sqs"
14
13
 
@@ -18,15 +17,14 @@ Gem::Specification.new do |gem|
18
17
  gem.require_paths = ["lib"]
19
18
  gem.license = "MIT"
20
19
 
21
- gem.add_dependency "sinatra"
22
- gem.add_dependency "builder"
23
-
24
- gem.add_development_dependency "rspec"
25
- gem.add_development_dependency "rake"
26
- gem.add_development_dependency "aws-sdk", "~> 2.0"
27
- gem.add_development_dependency "faraday"
28
- gem.add_development_dependency "thin"
29
- gem.add_development_dependency "verbose_hash_fetch"
30
- gem.add_development_dependency "activesupport"
20
+ gem.add_dependency "sinatra", "~> 2.0"
21
+ gem.add_dependency "builder", "~> 3.2"
31
22
 
23
+ gem.add_development_dependency "rspec", "~> 3.6"
24
+ gem.add_development_dependency "rake", "~> 12.0"
25
+ gem.add_development_dependency "rack-test", "~> 0.7"
26
+ gem.add_development_dependency "aws-sdk", "~> 2.10"
27
+ gem.add_development_dependency "thin", "~> 1.7"
28
+ gem.add_development_dependency "verbose_hash_fetch", "~> 0.0"
29
+ gem.add_development_dependency "activesupport", "~> 5.1"
32
30
  end
@@ -9,8 +9,8 @@ require 'fake_sqs/queues'
9
9
  require 'fake_sqs/responder'
10
10
  require 'fake_sqs/server'
11
11
  require 'fake_sqs/version'
12
- require 'fake_sqs/memory_database'
13
- require 'fake_sqs/file_database'
12
+ require 'fake_sqs/databases/file'
13
+ require 'fake_sqs/databases/memory'
14
14
 
15
15
  module FakeSQS
16
16
 
@@ -3,7 +3,6 @@ module FakeSQS
3
3
  class ChangeMessageVisibility
4
4
 
5
5
  def initialize(options = {})
6
- @server = options.fetch(:server)
7
6
  @queues = options.fetch(:queues)
8
7
  @responder = options.fetch(:responder)
9
8
  end
@@ -0,0 +1,38 @@
1
+ module FakeSQS
2
+ module Actions
3
+ class ChangeMessageVisibilityBatch
4
+
5
+ def initialize(options = {})
6
+ @queues = options.fetch(:queues)
7
+ @responder = options.fetch(:responder)
8
+ end
9
+
10
+ def call(queue, params)
11
+ keys = params.keys.map do |key|
12
+ case key
13
+ when /^ChangeMessageVisibilityBatchRequestEntry\.(\w+)\.Id$/
14
+ $1
15
+ end
16
+ end.compact
17
+
18
+ messages = keys.map do |key|
19
+ receipt = params.fetch("ChangeMessageVisibilityBatchRequestEntry.#{key}.ReceiptHandle")
20
+ timeout = params.fetch("ChangeMessageVisibilityBatchRequestEntry.#{key}.VisibilityTimeout").to_i
21
+ @queues.get(queue).change_message_visibility(receipt, timeout)
22
+ params.fetch("ChangeMessageVisibilityBatchRequestEntry.#{key}.Id")
23
+ end
24
+
25
+ @responder.call :ChangeMessageVisibilityBatch do |xml|
26
+ xml.ChangeMessageVisibilityBatchResult do
27
+ messages.each do |message|
28
+ xml.ChangeMessageVisibilityBatchResultEntry do
29
+ xml.Id message
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,25 @@
1
+ module FakeSQS
2
+ module Actions
3
+ class ListDeadLetterSourceQueues
4
+
5
+ def initialize(options = {})
6
+ @server = options.fetch(:server)
7
+ @queues = options.fetch(:queues)
8
+ @responder = options.fetch(:responder)
9
+ end
10
+
11
+ def call(name, params)
12
+ queue_arn = @queues.get(name).arn
13
+ queue_urls = @queues.list.select do |queue|
14
+ redrive_policy = queue.attributes.fetch("RedrivePolicy", nil)
15
+ redrive_policy && redrive_policy =~ /deadLetterTargetArn\":\"#{queue_arn}/
16
+ end
17
+ @responder.call :ListDeadLetterSourceQueues do |xml|
18
+ queue_urls.each do |queue|
19
+ xml.QueueUrl @server.url_for(queue.name)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -10,7 +10,11 @@ module FakeSQS
10
10
 
11
11
  def call(name, params)
12
12
  queue = @queues.get(name)
13
- messages = queue.receive_message(params)
13
+ filtered_attribute_names = []
14
+ params.select{|k,v | k =~ /AttributeName\.\d+/}.each do |key, value|
15
+ filtered_attribute_names << value
16
+ end
17
+ messages = queue.receive_message(params.merge(queues: @queues))
14
18
  @responder.call :ReceiveMessage do |xml|
15
19
  messages.each do |receipt, message|
16
20
  xml.Message do
@@ -18,11 +22,18 @@ module FakeSQS
18
22
  xml.ReceiptHandle receipt
19
23
  xml.MD5OfBody message.md5
20
24
  xml.Body message.body
25
+ message.attributes.each do |name, value|
26
+ if filtered_attribute_names.include?("All") || filtered_attribute_names.include?(name)
27
+ xml.Attribute do
28
+ xml.Name name
29
+ xml.Value value
30
+ end
31
+ end
32
+ end
21
33
  end
22
34
  end
23
35
  end
24
36
  end
25
-
26
37
  end
27
38
  end
28
- end
39
+ end
@@ -1,4 +1,5 @@
1
1
  require 'fake_sqs/actions/change_message_visibility'
2
+ require 'fake_sqs/actions/change_message_visibility_batch'
2
3
  require 'fake_sqs/actions/create_queue'
3
4
  require 'fake_sqs/actions/delete_queue'
4
5
  require 'fake_sqs/actions/list_queues'
@@ -11,6 +12,7 @@ require 'fake_sqs/actions/purge_queue'
11
12
  require 'fake_sqs/actions/send_message_batch'
12
13
  require 'fake_sqs/actions/get_queue_attributes'
13
14
  require 'fake_sqs/actions/set_queue_attributes'
15
+ require 'fake_sqs/actions/list_dead_letter_source_queues'
14
16
 
15
17
  module FakeSQS
16
18
 
@@ -3,19 +3,29 @@ require 'securerandom'
3
3
  module FakeSQS
4
4
  class Message
5
5
 
6
- attr_reader :body, :id, :md5
6
+ attr_reader :body, :id, :md5, :delay_seconds, :approximate_receive_count,
7
+ :sender_id, :approximate_first_receive_timestamp, :sent_timestamp
7
8
  attr_accessor :visibility_timeout
8
9
 
9
10
  def initialize(options = {})
10
11
  @body = options.fetch("MessageBody")
11
12
  @id = options.fetch("Id") { SecureRandom.uuid }
12
13
  @md5 = options.fetch("MD5") { Digest::MD5.hexdigest(@body) }
14
+ @sender_id = options.fetch("SenderId") { SecureRandom.uuid.delete('-').upcase[0...21] }
15
+ @approximate_receive_count = 0
16
+ @sent_timestamp = Time.now.to_i * 1000
17
+ @delay_seconds = options.fetch("DelaySeconds", 0).to_i
13
18
  end
14
19
 
15
20
  def expire!
16
21
  self.visibility_timeout = nil
17
22
  end
18
23
 
24
+ def receive!
25
+ @approximate_first_receive_timestamp ||= Time.now.to_i * 1000
26
+ @approximate_receive_count += 1
27
+ end
28
+
19
29
  def expired?( limit = Time.now )
20
30
  self.visibility_timeout.nil? || self.visibility_timeout < limit
21
31
  end
@@ -24,11 +34,21 @@ module FakeSQS
24
34
  self.visibility_timeout = Time.now + seconds
25
35
  end
26
36
 
37
+ def published?
38
+ if self.delay_seconds && self.delay_seconds > 0
39
+ elapsed_seconds = Time.now.to_i - (self.sent_timestamp.to_i / 1000)
40
+ elapsed_seconds >= self.delay_seconds
41
+ else
42
+ true
43
+ end
44
+ end
45
+
27
46
  def attributes
28
47
  {
29
- "MessageBody" => body,
30
- "Id" => id,
31
- "MD5" => md5,
48
+ "SenderId" => sender_id,
49
+ "ApproximateFirstReceiveTimestamp" => approximate_first_receive_timestamp,
50
+ "ApproximateReceiveCount"=> approximate_receive_count,
51
+ "SentTimestamp"=> sent_timestamp
32
52
  }
33
53
  end
34
54
 
@@ -1,6 +1,6 @@
1
1
  require 'securerandom'
2
2
  require 'fake_sqs/collection_view'
3
-
3
+ require 'json'
4
4
  module FakeSQS
5
5
 
6
6
  MessageNotInflight = Class.new(RuntimeError)
@@ -38,14 +38,14 @@ module FakeSQS
38
38
  def attributes
39
39
  queue_attributes.merge(
40
40
  "QueueArn" => arn,
41
- "ApproximateNumberOfMessages" => @messages.size,
41
+ "ApproximateNumberOfMessages" => published_size,
42
42
  "ApproximateNumberOfMessagesNotVisible" => @messages_in_flight.size,
43
43
  )
44
44
  end
45
45
 
46
46
  def send_message(options = {})
47
47
  with_lock do
48
- message = message_factory.new(options)
48
+ message = options.fetch(:message){ message_factory.new(options) }
49
49
  @messages << message
50
50
  message
51
51
  end
@@ -53,6 +53,7 @@ module FakeSQS
53
53
 
54
54
  def receive_message(options = {})
55
55
  amount = Integer options.fetch("MaxNumberOfMessages") { "1" }
56
+ visibility_timeout = Integer options.fetch("VisibilityTimeout") { default_visibility_timeout }
56
57
 
57
58
  fail ReadCountOutOfRange, amount if amount > 10
58
59
 
@@ -61,14 +62,19 @@ module FakeSQS
61
62
  result = {}
62
63
 
63
64
  with_lock do
64
- actual_amount = amount > size ? size : amount
65
+ actual_amount = amount > published_size ? published_size : amount
66
+ published_messages = @messages.select { |m| m.published? }
65
67
 
66
68
  actual_amount.times do
67
- message = @messages.delete_at(rand(size))
68
- message.expire_at(default_visibility_timeout)
69
- receipt = generate_receipt
70
- @messages_in_flight[receipt] = message
71
- result[receipt] = message
69
+ message = published_messages.delete_at(rand(published_size))
70
+ @messages.delete(message)
71
+ unless check_message_for_dlq(message, options)
72
+ message.expire_at(visibility_timeout)
73
+ message.receive!
74
+ receipt = generate_receipt
75
+ @messages_in_flight[receipt] = message
76
+ result[receipt] = message
77
+ end
72
78
  end
73
79
  end
74
80
 
@@ -111,7 +117,17 @@ module FakeSQS
111
117
  else
112
118
  message.expire_at(visibility)
113
119
  end
120
+ end
121
+ end
114
122
 
123
+ def check_message_for_dlq(message, options={})
124
+ if redrive_policy = queue_attributes["RedrivePolicy"] && JSON.parse(queue_attributes["RedrivePolicy"])
125
+ dlq = options[:queues].list.find{|queue| queue.arn == redrive_policy["deadLetterTargetArn"]}
126
+ if dlq && message.approximate_receive_count >= redrive_policy["maxReceiveCount"].to_i
127
+ dlq.send_message(message: message)
128
+ message.expire!
129
+ true
130
+ end
115
131
  end
116
132
  end
117
133
 
@@ -155,6 +171,10 @@ module FakeSQS
155
171
  messages.size
156
172
  end
157
173
 
174
+ def published_size
175
+ messages.select { |m| m.published? }.size
176
+ end
177
+
158
178
  def generate_receipt
159
179
  SecureRandom.hex
160
180
  end
@@ -55,7 +55,7 @@ module FakeSQS
55
55
  end
56
56
 
57
57
  def up?
58
- @pid && connection.get("/").code.to_s == "200"
58
+ @pid && connection.get("/ping").code.to_s == "200"
59
59
  rescue Errno::ECONNREFUSED
60
60
  false
61
61
  end
@@ -1,3 +1,3 @@
1
1
  module FakeSQS
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,21 +1,27 @@
1
1
  require 'sinatra/base'
2
+ require 'fake_sqs/catch_errors'
3
+ require 'fake_sqs/error_response'
2
4
 
3
5
  module FakeSQS
4
6
  class WebInterface < Sinatra::Base
5
7
 
8
+ def self.handle(path, verbs, &block)
9
+ verbs.each do |verb|
10
+ send(verb, path, &block)
11
+ end
12
+ end
13
+
6
14
  configure do
7
15
  use FakeSQS::CatchErrors, response: ErrorResponse
8
16
  end
9
17
 
10
18
  helpers do
11
-
12
19
  def action
13
20
  params.fetch("Action")
14
21
  end
15
-
16
22
  end
17
23
 
18
- get "/" do
24
+ get "/ping" do
19
25
  200
20
26
  end
21
27
 
@@ -29,7 +35,7 @@ module FakeSQS
29
35
  200
30
36
  end
31
37
 
32
- post "/" do
38
+ handle "/", [:get, :post] do
33
39
  params['logger'] = logger
34
40
  if params['QueueUrl']
35
41
  queue = URI.parse(params['QueueUrl']).path.gsub(/\//, '')
@@ -39,9 +45,8 @@ module FakeSQS
39
45
  settings.api.call(action, params)
40
46
  end
41
47
 
42
- post "/:queue" do |queue|
48
+ handle "/:queue", [:get, :post] do |queue|
43
49
  settings.api.call(action, queue, params)
44
50
  end
45
-
46
51
  end
47
52
  end
@@ -35,14 +35,55 @@ RSpec.describe "Actions for Messages", :sqs do
35
35
  )
36
36
 
37
37
  response = sqs.receive_message(
38
- queue_url: queue_url,
38
+ queue_url: queue_url
39
39
  )
40
40
 
41
41
  expect(response.messages.size).to eq 1
42
-
43
42
  expect(response.messages.first.body).to eq body
44
43
  end
45
44
 
45
+ specify "ReceiveMessage with attribute_names parameters" do
46
+ body = "test 123"
47
+
48
+ sqs.send_message(
49
+ queue_url: queue_url,
50
+ message_body: body
51
+ )
52
+
53
+ sent_time = Time.now.to_i * 1000
54
+
55
+ response = sqs.receive_message(
56
+ queue_url: queue_url,
57
+ attribute_names: ["All"]
58
+ )
59
+
60
+ received_time = Time.now.to_i * 1000
61
+
62
+ expect(response.messages.first.attributes.reject{|k,v| k == "SenderId"}).to eq({
63
+ "SentTimestamp" => sent_time.to_s,
64
+ "ApproximateReceiveCount" => "1",
65
+ "ApproximateFirstReceiveTimestamp" => received_time.to_s
66
+ })
67
+ expect(response.messages.first.attributes["SenderId"]).to be_kind_of(String)
68
+ expire_message(response.messages.first)
69
+
70
+ response = sqs.receive_message(
71
+ queue_url: queue_url
72
+ )
73
+ expect(response.messages.first.attributes).to eq({})
74
+ expire_message(response.messages.first)
75
+
76
+ response = sqs.receive_message(
77
+ queue_url: queue_url,
78
+ attribute_names: ["SentTimestamp", "ApproximateReceiveCount", "ApproximateFirstReceiveTimestamp"]
79
+ )
80
+ expect(response.messages.first.attributes).to eq({
81
+ "SentTimestamp" => sent_time.to_s,
82
+ "ApproximateReceiveCount" => "3",
83
+ "ApproximateFirstReceiveTimestamp" => received_time.to_s
84
+ })
85
+ end
86
+
46
87
  specify "DeleteMessage" do
47
88
  sqs.send_message(
48
89
  queue_url: queue_url,
@@ -121,6 +162,26 @@ RSpec.describe "Actions for Messages", :sqs do
121
162
  expect(response.messages.size).to eq 0
122
163
  end
123
164
 
165
+ specify "DeleteQueue" do
166
+ sqs.send_message(
167
+ queue_url: queue_url,
168
+ message_body: "test1"
169
+ )
170
+
171
+ response = sqs.receive_message(
172
+ queue_url: queue_url,
173
+ )
174
+ expect(response.messages.size).to eq 1
175
+
176
+ sqs.delete_queue(queue_url: queue_url)
177
+ sqs.create_queue(queue_name: QUEUE_NAME)
178
+
179
+ response = sqs.receive_message(
180
+ queue_url: queue_url,
181
+ )
182
+ expect(response.messages.size).to eq 0
183
+ end
184
+
124
185
  specify "SendMessageBatch" do
125
186
  bodies = %w(a b c)
126
187
 
@@ -169,7 +230,6 @@ RSpec.describe "Actions for Messages", :sqs do
169
230
  end
170
231
 
171
232
  specify 'set message timeout and wait for message to come' do
172
-
173
233
  body = 'some-sample-message'
174
234
 
175
235
  sqs.send_message(
@@ -193,7 +253,9 @@ RSpec.describe "Actions for Messages", :sqs do
193
253
  )
194
254
  expect(nothing.messages.size).to eq 0
195
255
 
196
- sleep(5)
256
+ # Changed from sleep 5 to sleep 7 due to race conditions in Travis build
257
+ # see https://github.com/iain/fake_sqs/pull/32
258
+ sleep(7)
197
259
 
198
260
  same_message = sqs.receive_message(
199
261
  queue_url: queue_url,
@@ -228,8 +290,85 @@ RSpec.describe "Actions for Messages", :sqs do
228
290
  }.to raise_error(Aws::SQS::Errors::MessageNotInflight)
229
291
  end
230
292
 
293
+ specify 'ChangeMessageVisibilityBatch' do
294
+ bodies = (1..10).map { |n| n.to_s }
295
+ sqs.send_message_batch(
296
+ queue_url: queue_url,
297
+ entries: bodies.map { |bd|
298
+ {
299
+ id: SecureRandom.uuid,
300
+ message_body: bd,
301
+ }
302
+ }
303
+ )
304
+ message = sqs.receive_message(
305
+ queue_url: queue_url,
306
+ max_number_of_messages: 10,
307
+ visibility_timeout: 0,
308
+ )
309
+
310
+ expect(message.messages.size).to eq(10)
311
+
312
+ sqs.change_message_visibility_batch(
313
+ queue_url: queue_url,
314
+ entries: message.messages.map { |m|
315
+ {
316
+ id: m.message_id,
317
+ receipt_handle: m.receipt_handle,
318
+ visibility_timeout: 10,
319
+ }
320
+ }
321
+ )
322
+
323
+ message = sqs.receive_message(
324
+ queue_url: queue_url,
325
+ max_number_of_messages: 10,
326
+ )
327
+
328
+ expect(message.messages.size).to eq(0)
329
+ end
330
+
331
+ specify 'should be moved to configured DLQ after maxReceiveCount if RedrivePolicy is set' do
332
+ dlq_queue_url = sqs.create_queue(queue_name: "TestSourceQueueDLQ").queue_url
333
+
334
+ dlq_arn = sqs.get_queue_attributes(queue_url: dlq_queue_url).attributes.fetch("QueueArn")
335
+ sqs.set_queue_attributes(
336
+ queue_url: queue_url,
337
+ attributes: {
338
+ "RedrivePolicy" => "{\"deadLetterTargetArn\":\"#{dlq_arn}\",\"maxReceiveCount\":2}"
339
+ }
340
+ )
341
+
342
+ message_id = sqs.send_message(
343
+ queue_url: queue_url,
344
+ message_body: "test",
345
+ ).message_id
346
+
347
+
348
+ 2.times do
349
+ message = sqs.receive_message(queue_url: queue_url)
350
+ expect(message.messages.size).to eq(1)
351
+ expect(message.messages.first.message_id).to eq(message_id)
352
+ expire_message(message.messages.first)
353
+ end
354
+
355
+ expect(sqs.receive_message(queue_url: queue_url).messages.size).to eq(0)
356
+
357
+ message = sqs.receive_message(queue_url: dlq_queue_url)
358
+ expect(message.messages.size).to eq(1)
359
+ expect(message.messages.first.message_id).to eq(message_id)
360
+ end
361
+
231
362
  def let_messages_in_flight_expire
232
363
  $fake_sqs.expire
233
364
  end
234
365
 
366
+ def expire_message(message)
367
+ sqs.change_message_visibility(
368
+ queue_url: queue_url,
369
+ receipt_handle: message.receipt_handle,
370
+ visibility_timeout: 0
371
+ )
372
+ end
373
+
235
374
  end
@@ -39,6 +39,22 @@ RSpec.describe "Actions for Queues", :sqs do
39
39
  ]
40
40
  end
41
41
 
42
+ specify "ListDeadLetterSourceQueues" do
43
+ dlq_queue_url = sqs.create_queue(queue_name: "source-list-DLQ").queue_url
44
+ dlq_arn = sqs.get_queue_attributes(queue_url: dlq_queue_url).attributes.fetch("QueueArn")
45
+ source_queue_urls = []
46
+ 2.times do |time|
47
+ queue_url = sqs.create_queue(queue_name: "source-list-#{time}").queue_url
48
+ sqs.set_queue_attributes(queue_url: queue_url,
49
+ attributes: {
50
+ "RedrivePolicy" => "{\"deadLetterTargetArn\":\"#{dlq_arn}\",\"maxReceiveCount\":10}"
51
+ }
52
+ )
53
+ source_queue_urls << queue_url
54
+ end
55
+ expect(sqs.list_dead_letter_source_queues(queue_url: dlq_queue_url).queue_urls).to eq source_queue_urls
56
+ end
57
+
42
58
  specify "DeleteQueue" do
43
59
  url = sqs.create_queue(queue_name: "test-delete").queue_url
44
60
  expect(sqs.list_queues.queue_urls.size).to eq 1
@@ -1,13 +1,6 @@
1
1
  require "aws-sdk"
2
2
  require "fake_sqs/test_integration"
3
3
 
4
- # Aws.config[:credentials] = {
5
- # :use_ssl => false,
6
- # :sqs_endpoint => "localhost",
7
- # :sqs_port => 4568,
8
- # :access_key_id => "fake access key",
9
- # :secret_access_key => "fake secret key",
10
- # }
11
4
  Aws.config.update(
12
5
  region: "us-east-1",
13
6
  credentials: Aws::Credentials.new("fake", "fake"),
@@ -29,6 +29,15 @@ RSpec.describe FakeSQS::Message do
29
29
 
30
30
  end
31
31
 
32
+ describe "#delay_seconds" do
33
+
34
+ it "is generated" do
35
+ message = create_message({"DelaySeconds" => 10})
36
+ expect(message.delay_seconds).to eq 10
37
+ end
38
+
39
+ end
40
+
32
41
  describe 'visibility_timeout' do
33
42
 
34
43
  let :message do
@@ -32,6 +32,15 @@ RSpec.describe FakeSQS::Queue do
32
32
  send_message(options)
33
33
  end
34
34
 
35
+ it "should set the message's SentTimestamp attribute" do
36
+ expect(send_message.attributes["SentTimestamp"]).to eq (Time.now.to_i * 1000)
37
+ end
38
+
39
+ it "should set the SenderId of the sender" do
40
+ sender_id = send_message.attributes["SenderId"]
41
+ expect(sender_id).to be_a String
42
+ expect(sender_id.length).to eq 21
43
+ end
35
44
  end
36
45
 
37
46
  describe "#receive_message" do
@@ -42,6 +51,17 @@ RSpec.describe FakeSQS::Queue do
42
51
  expect(received.values.first).to eq sent
43
52
  end
44
53
 
54
+ it "gets the message with 'DelaySeconds' option" do
55
+ delay_seconds = 3
56
+ sent = send_message({ "DelaySeconds" => delay_seconds })
57
+ received = receive_message
58
+ expect(received.values.first).to be_nil
59
+
60
+ allow(Time).to receive(:now).and_return(Time.now + delay_seconds)
61
+ received = receive_message
62
+ expect(received.values.first).to eq sent
63
+ end
64
+
45
65
  it "gets you a random message" do
46
66
  indexes = { :first => 0, :second => 0 }
47
67
  sample_group = 1_000
@@ -116,6 +136,25 @@ RSpec.describe FakeSQS::Queue do
116
136
  expect(receive_message).to eq({})
117
137
  end
118
138
 
139
+ it "should increment the ApproximateReceiveCount" do
140
+ sent_message = send_message
141
+ expect(sent_message.attributes["ApproximateReceiveCount"]).to eq 0
142
+ queue.change_message_visibility(receive_message.keys.first, 0)
143
+ expect(sent_message.attributes["ApproximateReceiveCount"]).to eq 1
144
+ receive_message
145
+ expect(sent_message.attributes["ApproximateReceiveCount"]).to eq 2
146
+ end
147
+
148
+ it "should set the ApproximateFirstReceiveTimestamp only when the message is first received" do
149
+ sent_message = send_message
150
+ expect(sent_message.attributes["ApproximateFirstReceiveTimestamp"]).to eq nil
151
+ receive_time = (Time.now.to_i * 1000)
152
+ queue.change_message_visibility(receive_message.keys.first, 0)
153
+ expect(sent_message.attributes["ApproximateFirstReceiveTimestamp"]).to eq receive_time
154
+ sleep 1
155
+ receive_message
156
+ expect(sent_message.attributes["ApproximateFirstReceiveTimestamp"]).to eq receive_time
157
+ end
119
158
  end
120
159
 
121
160
  describe "#delete_message" do
@@ -1,5 +1,5 @@
1
1
  require 'fake_sqs/queues'
2
- require 'fake_sqs/memory_database'
2
+ require 'fake_sqs/databases/memory'
3
3
 
4
4
  RSpec.describe FakeSQS::Queues do
5
5
 
@@ -0,0 +1,15 @@
1
+ require 'fake_sqs/web_interface'
2
+ require 'rack/test'
3
+
4
+ RSpec.describe FakeSQS::WebInterface do
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ FakeSQS::WebInterface
9
+ end
10
+
11
+ it "responds to GET /ping" do
12
+ get "/ping"
13
+ expect(last_response).to be_ok
14
+ end
15
+ end
metadata CHANGED
@@ -1,142 +1,142 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fake_sqs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - iain
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-11-02 00:00:00.000000000 Z
11
+ date: 2017-07-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sinatra
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '2.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '2.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: builder
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: '3.2'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ">="
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '3.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.6'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.6'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rake
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
- - - ">="
59
+ - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '12.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - ">="
66
+ - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '12.0'
69
69
  - !ruby/object:Gem::Dependency
70
- name: aws-sdk
70
+ name: rack-test
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '2.0'
75
+ version: '0.7'
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '2.0'
82
+ version: '0.7'
83
83
  - !ruby/object:Gem::Dependency
84
- name: faraday
84
+ name: aws-sdk
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - ">="
87
+ - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '0'
89
+ version: '2.10'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ">="
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '0'
96
+ version: '2.10'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: thin
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
- - - ">="
101
+ - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '0'
103
+ version: '1.7'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - ">="
108
+ - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '0'
110
+ version: '1.7'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: verbose_hash_fetch
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
- - - ">="
115
+ - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '0'
117
+ version: '0.0'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - ">="
122
+ - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '0'
124
+ version: '0.0'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: activesupport
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - ">="
129
+ - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: '0'
131
+ version: '5.1'
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
- - - ">="
136
+ - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: '0'
139
- description: Provides a fake SQS server that you can run locally to test against
138
+ version: '5.1'
139
+ description:
140
140
  email:
141
141
  - iain@iain.nl
142
142
  executables:
@@ -148,19 +148,21 @@ files:
148
148
  - ".rspec"
149
149
  - ".travis.yml"
150
150
  - Gemfile
151
- - MIT-LICENSE.txt
151
+ - LICENSE.md
152
152
  - README.md
153
153
  - Rakefile
154
154
  - bin/fake_sqs
155
155
  - fake_sqs.gemspec
156
156
  - lib/fake_sqs.rb
157
157
  - lib/fake_sqs/actions/change_message_visibility.rb
158
+ - lib/fake_sqs/actions/change_message_visibility_batch.rb
158
159
  - lib/fake_sqs/actions/create_queue.rb
159
160
  - lib/fake_sqs/actions/delete_message.rb
160
161
  - lib/fake_sqs/actions/delete_message_batch.rb
161
162
  - lib/fake_sqs/actions/delete_queue.rb
162
163
  - lib/fake_sqs/actions/get_queue_attributes.rb
163
164
  - lib/fake_sqs/actions/get_queue_url.rb
165
+ - lib/fake_sqs/actions/list_dead_letter_source_queues.rb
164
166
  - lib/fake_sqs/actions/list_queues.rb
165
167
  - lib/fake_sqs/actions/purge_queue.rb
166
168
  - lib/fake_sqs/actions/receive_message.rb
@@ -171,10 +173,10 @@ files:
171
173
  - lib/fake_sqs/catch_errors.rb
172
174
  - lib/fake_sqs/collection_view.rb
173
175
  - lib/fake_sqs/daemonize.rb
176
+ - lib/fake_sqs/databases/file.rb
177
+ - lib/fake_sqs/databases/memory.rb
174
178
  - lib/fake_sqs/error_response.rb
175
179
  - lib/fake_sqs/error_responses.yml
176
- - lib/fake_sqs/file_database.rb
177
- - lib/fake_sqs/memory_database.rb
178
180
  - lib/fake_sqs/message.rb
179
181
  - lib/fake_sqs/queue.rb
180
182
  - lib/fake_sqs/queue_factory.rb
@@ -199,6 +201,7 @@ files:
199
201
  - spec/unit/queues_spec.rb
200
202
  - spec/unit/responder_spec.rb
201
203
  - spec/unit/show_output_spec.rb
204
+ - spec/unit/web_interface_spec.rb
202
205
  homepage: https://github.com/iain/fake_sqs
203
206
  licenses:
204
207
  - MIT
@@ -219,7 +222,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
219
222
  version: '0'
220
223
  requirements: []
221
224
  rubyforge_project:
222
- rubygems_version: 2.4.5.1
225
+ rubygems_version: 2.6.12
223
226
  signing_key:
224
227
  specification_version: 4
225
228
  summary: Provides a fake SQS server that you can run locally to test against
@@ -238,4 +241,4 @@ test_files:
238
241
  - spec/unit/queues_spec.rb
239
242
  - spec/unit/responder_spec.rb
240
243
  - spec/unit/show_output_spec.rb
241
- has_rdoc:
244
+ - spec/unit/web_interface_spec.rb
@@ -1,22 +0,0 @@
1
- Copyright (c) 2012 iain
2
-
3
- MIT License
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining
6
- a copy of this software and associated documentation files (the
7
- "Software"), to deal in the Software without restriction, including
8
- without limitation the rights to use, copy, modify, merge, publish,
9
- distribute, sublicense, and/or sell copies of the Software, and to
10
- permit persons to whom the Software is furnished to do so, subject to
11
- the following conditions:
12
-
13
- The above copyright notice and this permission notice shall be
14
- included in all copies or substantial portions of the Software.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.