fake_sqs 0.0.2
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.
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.travis.yml +3 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE.txt +22 -0
- data/README.md +89 -0
- data/Rakefile +6 -0
- data/bin/fake_sqs +41 -0
- data/fake_sqs.gemspec +32 -0
- data/lib/fake_sqs.rb +35 -0
- data/lib/fake_sqs/message.rb +21 -0
- data/lib/fake_sqs/queue.rb +67 -0
- data/lib/fake_sqs/queue_factory.rb +16 -0
- data/lib/fake_sqs/queues.rb +57 -0
- data/lib/fake_sqs/responder.rb +22 -0
- data/lib/fake_sqs/server.rb +172 -0
- data/lib/fake_sqs/show_output.rb +18 -0
- data/lib/fake_sqs/version.rb +3 -0
- data/lib/fake_sqs/web_interface.rb +45 -0
- data/spec/acceptance/message_actions_spec.rb +59 -0
- data/spec/acceptance/queue_actions_spec.rb +44 -0
- data/spec/support/aws.rb +99 -0
- data/spec/unit/message_spec.rb +36 -0
- data/spec/unit/queue_factory_spec.rb +13 -0
- data/spec/unit/queue_spec.rb +147 -0
- data/spec/unit/queues_spec.rb +102 -0
- data/spec/unit/responder_spec.rb +44 -0
- data/spec/unit/show_output_spec.rb +22 -0
- metadata +243 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/MIT-LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
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.
|
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# Fake SQS [](http://travis-ci.org/iain/fake_sqs)
|
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.
|
8
|
+
|
9
|
+
This implementation is **not complete** yet.
|
10
|
+
|
11
|
+
Done so far are:
|
12
|
+
|
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
|
+
|
21
|
+
Certain bits are left off on purpose, to make it easier to work with, such as:
|
22
|
+
|
23
|
+
* No checking on access keys or signatures
|
24
|
+
* No 60 second delay between deleting a queue and recreating it.
|
25
|
+
* No visibility timeouts (see below about special hooks)
|
26
|
+
|
27
|
+
Other parts are just not done yet:
|
28
|
+
|
29
|
+
* Permissions
|
30
|
+
* Changing queue attributes
|
31
|
+
* Changing message visibility
|
32
|
+
* Error handling
|
33
|
+
|
34
|
+
So, actually, just the basics are implemented at this point.
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
To install:
|
39
|
+
|
40
|
+
```
|
41
|
+
$ gem install fake_sqs
|
42
|
+
```
|
43
|
+
|
44
|
+
To start:
|
45
|
+
|
46
|
+
```
|
47
|
+
$ fake_sqs
|
48
|
+
```
|
49
|
+
|
50
|
+
To configure, see the options in the help:
|
51
|
+
|
52
|
+
```
|
53
|
+
$ fake_sqs --help
|
54
|
+
```
|
55
|
+
|
56
|
+
This is an example of how to configure the official [aws-sdk gem] [aws-sdk], to
|
57
|
+
let it talk to Fake SQS.
|
58
|
+
|
59
|
+
``` ruby
|
60
|
+
AWS.config(
|
61
|
+
:use_ssl => false,
|
62
|
+
:sqs_endpoint => "localhost",
|
63
|
+
:sqs_port => 4567,
|
64
|
+
:access_key_id => "access key id",
|
65
|
+
:secret_access_key => "secret access key"
|
66
|
+
)
|
67
|
+
```
|
68
|
+
|
69
|
+
If you have the configuration options for other libraries, please give them to
|
70
|
+
me.
|
71
|
+
|
72
|
+
To reset the entire server, during tests for example, send a DELETE request to
|
73
|
+
the server. For example:
|
74
|
+
|
75
|
+
```
|
76
|
+
$ curl -X DELETE http://localhost:4567/
|
77
|
+
```
|
78
|
+
|
79
|
+
Within SQS, after receiving, messages will be available again automatically
|
80
|
+
after a certain time. While this is not implemented (for now at least), you can
|
81
|
+
trigger this behavior at at will, with a PUT request.
|
82
|
+
|
83
|
+
```
|
84
|
+
$ curl -X PUT http://localhost:4567/
|
85
|
+
```
|
86
|
+
|
87
|
+
|
88
|
+
[fake_dynamo]: https://github.com/ananthakumaran/fake_dynamo
|
89
|
+
[aws-sdk]: https://github.com/amazonwebservices/aws-sdk-for-ruby
|
data/Rakefile
ADDED
data/bin/fake_sqs
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'fake_sqs'
|
4
|
+
require 'fake_sqs/web_interface'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
app = FakeSQS::WebInterface
|
8
|
+
|
9
|
+
parser = OptionParser.new do |o|
|
10
|
+
|
11
|
+
o.on "-p", "--port PORT", Integer, "Port to use (default: 4567)" do |port|
|
12
|
+
app.set :port, port
|
13
|
+
end
|
14
|
+
|
15
|
+
o.on "-o", "--bind HOST", "Host to bind to (default: 0.0.0.0)" do |bind|
|
16
|
+
app.set :bind, bind
|
17
|
+
end
|
18
|
+
|
19
|
+
o.on "-s", "--server SERVER", ['thin', 'mongrel', 'webrick'], "Server to use: thin, mongrel or webrick" do |server|
|
20
|
+
app.set :server, server
|
21
|
+
end
|
22
|
+
|
23
|
+
o.on "-v", "--verbose", "Shows input parameters and output XML" do
|
24
|
+
require 'fake_sqs/show_output'
|
25
|
+
app.use FakeSQS::ShowOutput
|
26
|
+
end
|
27
|
+
|
28
|
+
o.on_tail "--version", "Shows the version" do
|
29
|
+
puts "fake_sqs version #{FakeSQS::VERSION}"
|
30
|
+
exit
|
31
|
+
end
|
32
|
+
|
33
|
+
o.on_tail "-h", "--help", "Shows this help page" do
|
34
|
+
puts o
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
parser.parse!
|
41
|
+
app.run!
|
data/fake_sqs.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fake_sqs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "fake_sqs"
|
8
|
+
gem.version = FakeSQS::VERSION
|
9
|
+
gem.authors = ["iain"]
|
10
|
+
gem.email = ["iain@iain.nl"]
|
11
|
+
gem.description = %q{Provides a fake SQS server that you can run locally to test against}
|
12
|
+
gem.summary = %q{Provides a fake SQS server that you can run locally to test against}
|
13
|
+
gem.homepage = "https://github.com/iain/fake_sqs"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_dependency "sinatra"
|
21
|
+
gem.add_dependency "builder"
|
22
|
+
|
23
|
+
gem.add_development_dependency "rspec"
|
24
|
+
gem.add_development_dependency "rake"
|
25
|
+
gem.add_development_dependency "aws-sdk"
|
26
|
+
gem.add_development_dependency "webmock"
|
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"
|
31
|
+
|
32
|
+
end
|
data/lib/fake_sqs.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'fake_sqs/version'
|
2
|
+
require 'fake_sqs/server'
|
3
|
+
require 'fake_sqs/queues'
|
4
|
+
require 'fake_sqs/responder'
|
5
|
+
require 'fake_sqs/queue'
|
6
|
+
require 'fake_sqs/queue_factory'
|
7
|
+
require 'fake_sqs/message'
|
8
|
+
|
9
|
+
module FakeSQS
|
10
|
+
|
11
|
+
def self.server(options = {})
|
12
|
+
Server.new(options.merge(queues: queues, responder: responder))
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.queues
|
16
|
+
Queues.new(queue_factory: queue_factory)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.responder
|
20
|
+
Responder.new
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.queue_factory
|
24
|
+
QueueFactory.new(message_factory: message_factory, queue: queue)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.message_factory
|
28
|
+
Message
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.queue
|
32
|
+
Queue
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module FakeSQS
|
4
|
+
class Message
|
5
|
+
|
6
|
+
attr_reader :body
|
7
|
+
|
8
|
+
def initialize(options = {})
|
9
|
+
@body = options.fetch("MessageBody")
|
10
|
+
end
|
11
|
+
|
12
|
+
def id
|
13
|
+
@id ||= SecureRandom.uuid
|
14
|
+
end
|
15
|
+
|
16
|
+
def md5
|
17
|
+
@md5 ||= Digest::MD5.hexdigest(body)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module FakeSQS
|
4
|
+
|
5
|
+
ReadCountOutOfRange = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
class Queue
|
8
|
+
|
9
|
+
attr_reader :name, :messages, :message_factory, :messages_in_flight
|
10
|
+
|
11
|
+
def initialize(options = {})
|
12
|
+
@name = options.fetch("QueueName")
|
13
|
+
@message_factory = options.fetch(:message_factory)
|
14
|
+
reset
|
15
|
+
end
|
16
|
+
|
17
|
+
def send_message(options = {})
|
18
|
+
message = message_factory.new(options)
|
19
|
+
messages << message
|
20
|
+
message
|
21
|
+
end
|
22
|
+
|
23
|
+
def receive_message(options = {})
|
24
|
+
amount = Integer options.fetch("MaxNumberOfMessages") { "1" }
|
25
|
+
|
26
|
+
fail ReadCountOutOfRange, amount if amount > 10
|
27
|
+
|
28
|
+
return {} if messages.empty?
|
29
|
+
|
30
|
+
result = {}
|
31
|
+
|
32
|
+
actual_amount = amount > size ? size : amount
|
33
|
+
|
34
|
+
actual_amount.times do
|
35
|
+
message = messages.delete_at(rand(size))
|
36
|
+
receipt = generate_receipt
|
37
|
+
messages_in_flight[receipt] = message
|
38
|
+
result[receipt] = message
|
39
|
+
end
|
40
|
+
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
def delete_message(receipt)
|
45
|
+
message = messages_in_flight.delete(receipt)
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset
|
49
|
+
@messages = []
|
50
|
+
@messages_in_flight = {}
|
51
|
+
end
|
52
|
+
|
53
|
+
def expire
|
54
|
+
@messages += messages_in_flight.values
|
55
|
+
@messages_in_flight = {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def size
|
59
|
+
messages.size
|
60
|
+
end
|
61
|
+
|
62
|
+
def generate_receipt
|
63
|
+
SecureRandom.hex
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FakeSQS
|
2
|
+
class QueueFactory
|
3
|
+
|
4
|
+
attr_reader :message_factory, :queue
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@message_factory = options.fetch(:message_factory)
|
8
|
+
@queue = options.fetch(:queue)
|
9
|
+
end
|
10
|
+
|
11
|
+
def new(options)
|
12
|
+
queue.new(options.merge(:message_factory => message_factory))
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module FakeSQS
|
2
|
+
|
3
|
+
QueueNameExists = Class.new(RuntimeError)
|
4
|
+
NonExistentQueue = Class.new(RuntimeError)
|
5
|
+
|
6
|
+
class Queues
|
7
|
+
|
8
|
+
attr_reader :queues, :queue_factory
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
@queue_factory = options.fetch(:queue_factory)
|
12
|
+
reset
|
13
|
+
end
|
14
|
+
|
15
|
+
def create(name, options = {})
|
16
|
+
if queues[name]
|
17
|
+
fail QueueNameExists, name
|
18
|
+
else
|
19
|
+
queue = queue_factory.new(options)
|
20
|
+
queues[name] = queue
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(name, options = {})
|
25
|
+
if queues[name]
|
26
|
+
queues.delete(name)
|
27
|
+
else
|
28
|
+
fail NonExistentQueue, name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def list(options = {})
|
33
|
+
if (prefix = options["QueueNamePrefix"])
|
34
|
+
queues.select { |name, queue| name =~ /^#{prefix}/ }.values
|
35
|
+
else
|
36
|
+
queues.values
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def get(name, options = {})
|
41
|
+
if queues[name]
|
42
|
+
queues[name]
|
43
|
+
else
|
44
|
+
fail NonExistentQueue, name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def reset
|
49
|
+
@queues = {}
|
50
|
+
end
|
51
|
+
|
52
|
+
def expire
|
53
|
+
queues.each { |name, queue| queue.expire }
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'securerandom'
|
3
|
+
|
4
|
+
module FakeSQS
|
5
|
+
class Responder
|
6
|
+
|
7
|
+
def call(name, &block)
|
8
|
+
xml = Builder::XmlMarkup.new(:indent => 4)
|
9
|
+
xml.tag! "#{name}Response" do
|
10
|
+
if block
|
11
|
+
xml.tag! "#{name}Result" do
|
12
|
+
yield xml
|
13
|
+
end
|
14
|
+
end
|
15
|
+
xml.ResponseMetadata do
|
16
|
+
xml.RequestId SecureRandom.uuid
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|