fake_sns 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +2 -0
  4. data/.simplecov +4 -0
  5. data/.travis.yml +5 -0
  6. data/Gemfile +2 -0
  7. data/README.md +167 -0
  8. data/Rakefile +24 -0
  9. data/bin/fake_sns +79 -0
  10. data/config.ru +3 -0
  11. data/fake_sns.gemspec +34 -0
  12. data/lib/fake_sns/action.rb +24 -0
  13. data/lib/fake_sns/actions/create_topic.rb +41 -0
  14. data/lib/fake_sns/actions/delete_topic.rb +13 -0
  15. data/lib/fake_sns/actions/get_topic_attributes.rb +32 -0
  16. data/lib/fake_sns/actions/list_subscriptions.rb +24 -0
  17. data/lib/fake_sns/actions/list_subscriptions_by_topic.rb +23 -0
  18. data/lib/fake_sns/actions/list_topics.rb +13 -0
  19. data/lib/fake_sns/actions/publish.rb +37 -0
  20. data/lib/fake_sns/actions/set_topic_attributes.rb +19 -0
  21. data/lib/fake_sns/actions/subscribe.rb +43 -0
  22. data/lib/fake_sns/database.rb +76 -0
  23. data/lib/fake_sns/deliver_message.rb +100 -0
  24. data/lib/fake_sns/error.rb +34 -0
  25. data/lib/fake_sns/error_response.rb +46 -0
  26. data/lib/fake_sns/message.rb +28 -0
  27. data/lib/fake_sns/message_collection.rb +40 -0
  28. data/lib/fake_sns/response.rb +16 -0
  29. data/lib/fake_sns/server.rb +76 -0
  30. data/lib/fake_sns/show_output.rb +20 -0
  31. data/lib/fake_sns/storage.rb +75 -0
  32. data/lib/fake_sns/subscription.rb +17 -0
  33. data/lib/fake_sns/subscription_collection.rb +34 -0
  34. data/lib/fake_sns/test_integration.rb +110 -0
  35. data/lib/fake_sns/topic.rb +13 -0
  36. data/lib/fake_sns/topic_collection.rb +41 -0
  37. data/lib/fake_sns/version.rb +3 -0
  38. data/lib/fake_sns/views/create_topic.xml.erb +8 -0
  39. data/lib/fake_sns/views/delete_topic.xml.erb +5 -0
  40. data/lib/fake_sns/views/error.xml.erb +8 -0
  41. data/lib/fake_sns/views/get_topic_attributes.xml.erb +15 -0
  42. data/lib/fake_sns/views/list_subscriptions.xml.erb +18 -0
  43. data/lib/fake_sns/views/list_subscriptions_by_topic.xml.erb +18 -0
  44. data/lib/fake_sns/views/list_topics.xml.erb +14 -0
  45. data/lib/fake_sns/views/publish.xml.erb +8 -0
  46. data/lib/fake_sns/views/set_topic_attributes.xml.erb +5 -0
  47. data/lib/fake_sns/views/subscribe.xml.erb +8 -0
  48. data/lib/fake_sns.rb +51 -0
  49. data/spec/fake_sns/drain_spec.rb +91 -0
  50. data/spec/fake_sns/publish_spec.rb +28 -0
  51. data/spec/fake_sns/replace_spec.rb +14 -0
  52. data/spec/fake_sns/subscribing_spec.rb +42 -0
  53. data/spec/fake_sns/topics_spec.rb +44 -0
  54. data/spec/spec_helper.rb +54 -0
  55. metadata +271 -0
@@ -0,0 +1,41 @@
1
+ module FakeSNS
2
+ class TopicCollection
3
+
4
+ include Enumerable
5
+
6
+ def initialize(store)
7
+ @store = store
8
+ @store["topics"] ||= []
9
+ end
10
+
11
+ def collection
12
+ @store["topics"]
13
+ end
14
+
15
+ def reset
16
+ @store["topics"] = []
17
+ end
18
+
19
+ def each(*args, &block)
20
+ p @store.to_yaml
21
+ collection.map { |item| Topic.new(item) }.each(*args, &block)
22
+ end
23
+
24
+ def fetch(arn, &default)
25
+ default ||= -> { raise InvalidParameterValue, "Unknown topic #{arn}" }
26
+ found = collection.find do |topic|
27
+ topic["arn"] == arn
28
+ end
29
+ found || default.call
30
+ end
31
+
32
+ def create(attributes)
33
+ collection << attributes
34
+ end
35
+
36
+ def delete(arn)
37
+ collection.delete(fetch(arn))
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ module FakeSNS
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,8 @@
1
+ <CreateTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <CreateTopicResult>
3
+ <TopicArn><%= arn %></TopicArn>
4
+ </CreateTopicResult>
5
+ <ResponseMetadata>
6
+ <RequestId><%= request_id %></RequestId>
7
+ </ResponseMetadata>
8
+ </CreateTopicResponse>
@@ -0,0 +1,5 @@
1
+ <DeleteTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <ResponseMetadata>
3
+ <RequestId><%= request_id %></RequestId>
4
+ </ResponseMetadata>
5
+ </DeleteTopicResponse>
@@ -0,0 +1,8 @@
1
+ <ErrorResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <Error>
3
+ <Type><%= type %></Type>
4
+ <Code><%= code %></Code>
5
+ <Message><%= message %></Message>
6
+ </Error>
7
+ <RequestId><%= request_id %></RequestId>
8
+ </ErrorResponse>
@@ -0,0 +1,15 @@
1
+ <GetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <GetTopicAttributesResult>
3
+ <Attributes>
4
+ <% each_attribute do |key, value| %>
5
+ <entry>
6
+ <key><%= key %></key>
7
+ <value><%= value %></value>
8
+ </entry>
9
+ <% end %>
10
+ </Attributes>
11
+ </GetTopicAttributesResult>
12
+ <ResponseMetadata>
13
+ <RequestId><%= request_id %></RequestId>
14
+ </ResponseMetadata>
15
+ </GetTopicAttributesResponse>
@@ -0,0 +1,18 @@
1
+ <ListSubscriptionsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <ListSubscriptionsResult>
3
+ <Subscriptions>
4
+ <% each_subscription do |subscription| %>
5
+ <member>
6
+ <TopicArn><%= subscription.topic_arn %></TopicArn>
7
+ <Protocol><%= subscription.protocol %></Protocol>
8
+ <SubscriptionArn><%= subscription.arn %></SubscriptionArn>
9
+ <Owner><%= subscription.owner %></Owner>
10
+ <Endpoint><%= subscription.endpoint %></Endpoint>
11
+ </member>
12
+ <% end %>
13
+ </Subscriptions>
14
+ </ListSubscriptionsResult>
15
+ <ResponseMetadata>
16
+ <RequestId><%= request_id %></RequestId>
17
+ </ResponseMetadata>
18
+ </ListSubscriptionsResponse>
@@ -0,0 +1,18 @@
1
+ <ListSubscriptionsByTopicResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <ListSubscriptionsByTopicResult>
3
+ <Subscriptions>
4
+ <% each_subscription do |subscription| %>
5
+ <member>
6
+ <TopicArn><%= subscription.topic_arn %></TopicArn>
7
+ <Protocol><%= subscription.protocol %></Protocol>
8
+ <SubscriptionArn><%= subscription.arn %></SubscriptionArn>
9
+ <Owner><%= subscription.owner %></Owner>
10
+ <Endpoint><%= subscription.endpoint %></Endpoint>
11
+ </member>
12
+ <% end %>
13
+ </Subscriptions>
14
+ </ListSubscriptionsByTopicResult>
15
+ <ResponseMetadata>
16
+ <RequestId><%= request_id %></RequestId>
17
+ </ResponseMetadata>
18
+ </ListSubscriptionsByTopicResponse>
@@ -0,0 +1,14 @@
1
+ <ListTopicsResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <ListTopicsResult>
3
+ <Topics>
4
+ <% each_topic do |topic| %>
5
+ <member>
6
+ <TopicArn><%= topic.arn %></TopicArn>
7
+ </member>
8
+ <% end %>
9
+ </Topics>
10
+ </ListTopicsResult>
11
+ <ResponseMetadata>
12
+ <RequestId><%= request_id %></RequestId>
13
+ </ResponseMetadata>
14
+ </ListTopicsResponse>
@@ -0,0 +1,8 @@
1
+ <PublishResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <PublishResult>
3
+ <MessageId><%= message_id %></MessageId>
4
+ </PublishResult>
5
+ <ResponseMetadata>
6
+ <RequestId><%= request_id %></RequestId>
7
+ </ResponseMetadata>
8
+ </PublishResponse>
@@ -0,0 +1,5 @@
1
+ <SetTopicAttributesResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <ResponseMetadata>
3
+ <RequestId><%= request_id %></RequestId>
4
+ </ResponseMetadata>
5
+ </SetTopicAttributesResponse>
@@ -0,0 +1,8 @@
1
+ <SubscribeResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
2
+ <SubscribeResult>
3
+ <SubscriptionArn><%= subscription_arn %></SubscriptionArn>
4
+ </SubscribeResult>
5
+ <ResponseMetadata>
6
+ <RequestId><%= request_id %></RequestId>
7
+ </ResponseMetadata>
8
+ </SubscribeResponse>
data/lib/fake_sns.rb ADDED
@@ -0,0 +1,51 @@
1
+ Encoding.default_external = Encoding::UTF_8
2
+ Encoding.default_internal = Encoding::UTF_8
3
+
4
+ require "virtus"
5
+ require "verbose_hash_fetch"
6
+
7
+ require "fake_sns/error"
8
+ require "fake_sns/error_response"
9
+
10
+ require "fake_sns/database"
11
+ require "fake_sns/storage"
12
+
13
+ require "fake_sns/topic_collection"
14
+ require "fake_sns/topic"
15
+ require "fake_sns/subscription_collection"
16
+ require "fake_sns/subscription"
17
+
18
+ require "fake_sns/message_collection"
19
+ require "fake_sns/message"
20
+ require "fake_sns/deliver_message"
21
+
22
+ require "fake_sns/response"
23
+ require "fake_sns/action"
24
+ require "fake_sns/server"
25
+
26
+ # load all the actions
27
+ action_files = File.expand_path("../fake_sns/actions/*.rb", __FILE__)
28
+ Dir.glob(action_files).each do |file|
29
+ require file
30
+ end
31
+
32
+ module FakeSNS
33
+
34
+ def self.server(options)
35
+ app = Server
36
+ if log = options[:log]
37
+ $stdout.reopen(log, "w:utf-8")
38
+ $stderr.reopen(log, "w:utf-8")
39
+ app.enable :logging
40
+ end
41
+ if options[:verbose]
42
+ require "fake_sns/show_output"
43
+ app.use FakeSNS::ShowOutput
44
+ end
45
+ options.each do |key, value|
46
+ app.set key, value
47
+ end
48
+ app
49
+ end
50
+
51
+ end
@@ -0,0 +1,91 @@
1
+ require "spec_helper"
2
+ require "sinatra/base"
3
+ require 'json_expressions/rspec'
4
+
5
+ describe "Drain messages", :sqs do
6
+
7
+ it "works for SQS" do
8
+ topic = sns.topics.create("my-topic")
9
+ queue = sqs.queues.create("my-queue")
10
+ topic.subscribe(queue)
11
+
12
+ topic.publish("X")
13
+
14
+ $fake_sns.drain
15
+ expect(queue.visible_messages).to eq 1
16
+ end
17
+
18
+ it "works for SQS with a single message" do
19
+ topic = sns.topics.create("my-topic")
20
+ queue = sqs.queues.create("my-queue")
21
+ topic.subscribe(queue)
22
+
23
+ message_id = topic.publish("X")
24
+ topic.publish("Y")
25
+
26
+ $fake_sns.drain(message_id)
27
+ expect(queue.visible_messages).to eq 1
28
+ end
29
+
30
+ it "works for HTTP" do
31
+ requests = []
32
+ _headers = []
33
+ target_app = Class.new(Sinatra::Base) do
34
+ get("/") { 200 } # check if server started
35
+ post("/endpoint") do
36
+ requests << request.body.read
37
+ _headers << request.env
38
+ 200
39
+ end
40
+ end
41
+
42
+ app_runner = Thread.new do
43
+ target_app.set :port, 5051
44
+ target_app.run!
45
+ end
46
+
47
+ topic = sns.topics.create("my-topic")
48
+ subscription = topic.subscribe("http://localhost:5051/endpoint")
49
+
50
+ message_id = topic.publish("X")
51
+
52
+ wait_for { Faraday.new("http://localhost:5051").get("/").success? rescue false }
53
+
54
+ $fake_sns.drain
55
+
56
+ app_runner.kill
57
+
58
+ expect(requests.size).to eq 1
59
+ expect(requests.first).to match_json_expression(
60
+ "Type" => "Notification",
61
+ "Message" => "X",
62
+ "MessageId" => message_id,
63
+ "Signature" => "Fake",
64
+ "SignatureVersion" => "1",
65
+ "SigningCertURL" => "https://sns.us-east-1.amazonaws.com/SimpleNotificationService-f3ecfb7224c7233fe7bb5f59f96de52f.pem",
66
+ "Subject" => nil,
67
+ "Timestamp" => anything,
68
+ "TopicArn" => topic.arn,
69
+ "Type" => "Notification",
70
+ "UnsubscribeURL" => "",
71
+ )
72
+
73
+ expect(_headers.size).to eq 1
74
+ expect(_headers.first).to match_json_expression({
75
+ "HTTP_X_AMZ_SNS_MESSAGE_TYPE" => "Notification",
76
+ "HTTP_X_AMZ_SNS_MESSAGE_ID" => message_id,
77
+ "HTTP_X_AMZ_SNS_TOPIC_ARN" => topic.arn,
78
+ "HTTP_X_AMZ_SNS_SUBSCRIPTION_ARN" => subscription.arn,
79
+ }.ignore_extra_keys!)
80
+
81
+ end
82
+
83
+ def wait_for(&condition)
84
+ Timeout.timeout 1 do
85
+ until condition.call
86
+ sleep 0.01
87
+ end
88
+ end
89
+ end
90
+
91
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe "Publishing" do
4
+
5
+ let(:existing_topic) { sns.topics.create("my-topic") }
6
+
7
+ it "remembers published messages" do
8
+ message_id = existing_topic.publish("hallo")
9
+ messages = $fake_sns.data.fetch("messages")
10
+ expect(messages.size).to eq 1
11
+ message = messages.first
12
+ expect(message.fetch(:id)).to eq message_id
13
+ end
14
+
15
+ it "needs an existing topic" do
16
+ topic = sns.topics["arn:aws:sns:us-east-1:5068edfd0f7ee3ea9ccc1e73cbb17569:not-exist"]
17
+ expect {
18
+ topic.publish("hallo")
19
+ }.to raise_error AWS::SNS::Errors::InvalidParameterValue
20
+ end
21
+
22
+ it "doesn't allow messages that are too big" do
23
+ expect {
24
+ existing_topic.publish("A" * 262145)
25
+ }.to raise_error AWS::SNS::Errors::InvalidParameterValue
26
+ end
27
+
28
+ end
@@ -0,0 +1,14 @@
1
+ require "spec_helper"
2
+
3
+ describe "Replacing the data" do
4
+
5
+ it "works by submitting new YAML" do
6
+ topic = sns.topics.create("my-topic")
7
+ previous_data = $fake_sns.data.to_yaml
8
+ apply_change = lambda { |data| data.gsub("my-topic", "your-topic") }
9
+ new_data = apply_change.call(previous_data)
10
+ $fake_sns.connection.put("/", new_data)
11
+ expect(sns.topics.map(&:arn)).to eq [apply_change.call(topic.arn)]
12
+ end
13
+
14
+ end
@@ -0,0 +1,42 @@
1
+ require "spec_helper"
2
+
3
+ describe "Subscribing", :sqs do
4
+
5
+ it "lists subscriptions globally" do
6
+ topic = sns.topics.create("my-topic")
7
+ subscription = topic.subscribe("http://example.com")
8
+ expect(sns.subscriptions.map(&:topic_arn)).to eq [topic.arn]
9
+ expect(sns.subscriptions.map(&:arn)).to eq [subscription.arn]
10
+ end
11
+
12
+ it "filters by topic" do
13
+ topic = sns.topics.create("my-topic")
14
+ other_topic = sns.topics.create("my-topic-2")
15
+ subscription = topic.subscribe("http://example.com")
16
+ expect(topic.subscriptions.map(&:topic_arn)).to eq [topic.arn]
17
+ expect(topic.subscriptions.map(&:arn)).to eq [subscription.arn]
18
+ expect(other_topic.subscriptions.map(&:arn)).to eq []
19
+ end
20
+
21
+ it "needs an existing topic" do
22
+ topic = sns.topics["arn:aws:sns:us-east-1:5068edfd0f7ee3ea9ccc1e73cbb17569:not-exist"]
23
+ expect {
24
+ topic.subscribe("http://example.com")
25
+ }.to raise_error AWS::SNS::Errors::InvalidParameterValue
26
+ end
27
+
28
+ it "can subscribe to a SQS queue" do
29
+ queue = AWS::SQS.new.queues.create("my-queue")
30
+ topic = sns.topics.create("my-topic")
31
+ topic.subscribe(queue)
32
+ end
33
+
34
+ it "won't subscribe twice to the same endpoint" do
35
+ queue = AWS::SQS.new.queues.create("my-queue")
36
+ topic = sns.topics.create("my-topic")
37
+ topic.subscribe(queue)
38
+ topic.subscribe(queue)
39
+ expect(sns.subscriptions.to_a.size).to eq 1
40
+ end
41
+
42
+ end
@@ -0,0 +1,44 @@
1
+ require "spec_helper"
2
+
3
+ describe "Topics" do
4
+
5
+ it "rejects invalid characters in topic names" do
6
+ expect {
7
+ sns.topics.create("dot.dot")
8
+ }.to raise_error(AWS::SNS::Errors::InvalidParameterValue)
9
+ end
10
+
11
+ it "creates a new topic" do
12
+ topic = sns.topics.create("my-topic")
13
+ expect(topic.arn).to match(/arn:aws:sns:us-east-1:(\w+):my-topic/)
14
+
15
+ new_topic = sns.topics.create("other-topic")
16
+ expect(new_topic.arn).not_to eq topic.arn
17
+
18
+ existing_topic = sns.topics.create("my-topic")
19
+ expect(existing_topic.arn).to eq topic.arn
20
+ end
21
+
22
+ it "lists topics" do
23
+ topic1 = sns.topics.create("my-topic-1")
24
+ topic2 = sns.topics.create("my-topic-2")
25
+
26
+ expect(sns.topics.map(&:arn)).to match_array [topic1.arn, topic2.arn]
27
+ end
28
+
29
+ it "deletes topics" do
30
+ topic = sns.topics.create("my-topic")
31
+ expect(sns.topics.map(&:arn)).to eq [topic.arn]
32
+ sns.topics[topic.arn].delete
33
+ expect(sns.topics.map(&:arn)).to eq []
34
+ end
35
+
36
+ it "can set and read attributes" do
37
+ topic = sns.topics.create("my-topic")
38
+ expect(topic.display_name).to eq "my-topic"
39
+ topic.display_name = "the display name"
40
+ expect(topic.display_name).to eq "the display name"
41
+ expect(topic.name).to eq "my-topic"
42
+ end
43
+
44
+ end
@@ -0,0 +1,54 @@
1
+ ENV["RACK_ENV"] ||= 'test'
2
+
3
+ require "bundler/setup"
4
+ Bundler.setup
5
+
6
+ require "verbose_hash_fetch"
7
+ require "aws-sdk"
8
+ require "pry"
9
+ require "fake_sns/test_integration"
10
+ require "fake_sqs/test_integration"
11
+
12
+
13
+ AWS.config(
14
+ use_ssl: false,
15
+ sqs_endpoint: "localhost",
16
+ sqs_port: 4568,
17
+ sns_endpoint: "localhost",
18
+ sns_port: 9293,
19
+ access_key_id: "fake access key",
20
+ secret_access_key: "fake secret key",
21
+ )
22
+
23
+ db = ENV["SNS_DATABASE"]
24
+ db = ":memory:" if ENV["SNS_DATABASE"].to_s == ""
25
+
26
+ puts "Running tests with database stored in #{db}"
27
+
28
+ $fake_sns = FakeSNS::TestIntegration.new(database: db)
29
+ $fake_sqs = FakeSQS::TestIntegration.new(database: ":memory:")
30
+
31
+ module SpecHelper
32
+ def sns
33
+ AWS::SNS.new
34
+ end
35
+ def sqs
36
+ AWS::SQS.new
37
+ end
38
+ end
39
+
40
+ RSpec.configure do |config|
41
+
42
+ config.treat_symbols_as_metadata_keys_with_true_values = true
43
+ config.expect_with :rspec do |rspec|
44
+ rspec.syntax = :expect
45
+ end
46
+
47
+ config.before(:each) { $fake_sns.start }
48
+ config.after(:suite) { $fake_sns.stop }
49
+ config.include SpecHelper
50
+
51
+ config.before(:each, :sqs) { $fake_sqs.start }
52
+ config.after(:suite) { $fake_sqs.stop }
53
+
54
+ end