fake_sns 0.0.1

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