fake_sqs 0.3.1 → 0.4.0

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