anmo2 0.0.31

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: c7310ced86159b67bd2c3341222a7e33b2a135f0
4
+ data.tar.gz: bfb7d3107fc3e36ddd6f65daaa84e91ae8ae8474
5
+ SHA512:
6
+ metadata.gz: 88d83b15443d8152dd009b7501a3e4c2f0b13dd1dfcb418826236a474b30184cf7bf1a9339b166c9e48e616c049e5d7a6a79f3269adac3bf342bc60069d76bb0
7
+ data.tar.gz: 00e8186c8d03d2fd29fb3b8fa7dab14a56ab7d9a54007726083a76cec88c3349b286a604707ddddd38f0080d31bbd64a545534abbf888b785fd84277a980363d
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .vimlocal
data/.rubocop.yml ADDED
@@ -0,0 +1,112 @@
1
+ Lint/SpaceBeforeFirstArg:
2
+ Severity: fatal
3
+ Enabled: true
4
+
5
+ Lint/RescueException:
6
+ Severity: fatal
7
+ Enabled: true
8
+
9
+ Lint/UnusedBlockArgument:
10
+ Severity: fatal
11
+ Enabled: true
12
+
13
+ Metrics/LineLength:
14
+ Severity: fatal
15
+ Enabled: true
16
+
17
+ Metrics/MethodLength:
18
+ Severity: convention
19
+ Enabled: true
20
+ Max: 5
21
+
22
+ Style/AlignHash:
23
+ Severity: fatal
24
+ Enabled: true
25
+ EnforcedHashRocketStyle: table
26
+
27
+ Style/AlignParameters:
28
+ Severity: fatal
29
+ Enabled: true
30
+ EnforcedStyle: with_fixed_indentation
31
+
32
+ Style/BracesAroundHashParameters:
33
+ Severity: fatal
34
+ Enabled: true
35
+
36
+ Style/ConstantName:
37
+ Severity: fatal
38
+ Enabled: true
39
+
40
+ Style/Documentation:
41
+ Enabled: false
42
+
43
+ Style/EmptyLinesAroundClassBody:
44
+ Severity: fatal
45
+ Enabled: true
46
+
47
+ Style/ExtraSpacing:
48
+ Enabled: false
49
+
50
+ Style/FileName:
51
+ Enabled: false
52
+
53
+ Style/HashSyntax:
54
+ Severity: fatal
55
+ Enabled: true
56
+ EnforcedStyle: hash_rockets
57
+
58
+ Style/IndentationConsistency:
59
+ Severity: fatal
60
+ Enabled: true
61
+
62
+ Style/IndentationWidth:
63
+ Severity: fatal
64
+ Enabled: true
65
+
66
+ Style/IndentHash:
67
+ Severity: fatal
68
+ Enabled: true
69
+
70
+ Style/InlineComment:
71
+ Severity: fatal
72
+ Enabled: true
73
+
74
+ Style/LineEndConcatenation:
75
+ Severity: fatal
76
+ Enabled: true
77
+
78
+ Style/MethodName:
79
+ Severity: fatal
80
+ Enabled: true
81
+
82
+ Style/MultilineTernaryOperator:
83
+ Enabled: false
84
+
85
+ Style/NegatedIf:
86
+ Severity: fatal
87
+ Enabled: true
88
+
89
+ Style/Not:
90
+ Severity: fatal
91
+ Enabled: true
92
+
93
+ Style/SpaceInsideHashLiteralBraces:
94
+ Severity: fatal
95
+ Enabled: true
96
+
97
+ Style/StringLiterals:
98
+ Severity: fatal
99
+ Enabled: true
100
+ EnforcedStyle: double_quotes
101
+
102
+ Style/TrailingBlankLines:
103
+ Severity: fatal
104
+ Enabled: true
105
+
106
+ Style/TrailingWhitespace:
107
+ Severity: fatal
108
+ Enabled: true
109
+
110
+ Style/VariableName:
111
+ Severity: fatal
112
+ Enabled: true
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.2
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm ruby-1.9.2-p318
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ rvm:
2
+ - 1.8.7
3
+ - 1.9.2
4
+ - 1.9.3
data/Dockerfile ADDED
@@ -0,0 +1,9 @@
1
+ FROM ruby
2
+
3
+ RUN gem install anmo
4
+ ADD run.sh /run.sh
5
+ RUN chmod u+x /run.sh
6
+
7
+ EXPOSE 9999
8
+
9
+ CMD ["./run.sh"]
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in anmo.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,41 @@
1
+ Anmo
2
+ ====
3
+
4
+ [![Build status](https://secure.travis-ci.org/sthulbourn/anmo.png)](https://travis-ci.org/sthulbourn/anmo)
5
+
6
+ What?
7
+ -----
8
+ Anmo acts as a mock api and can be used to store arbitrary pieces of data for integration testing flaky APIs.
9
+ This is generally *not a good idea*, but where you can't use [VCR](https://github.com/myronmarston/vcr) anmo is now an option.
10
+
11
+ How?
12
+ ----
13
+
14
+ ```
15
+ require "anmo"
16
+
17
+ Thread.new { Anmo.launch_server }
18
+
19
+ Anmo.create_request({
20
+ :path => "/lookatmyhorse",
21
+ :body => "my horse is amazing"
22
+ })
23
+ ```
24
+
25
+ ```
26
+ curl http://localhost:8787/lookatmyhorse
27
+ my horse is amazing
28
+ ```
29
+
30
+ Docker
31
+ ----
32
+
33
+ To run using docker containers, start memcached and then start anmo.
34
+
35
+ ```sh
36
+
37
+ docker run --name memcache -d memcached
38
+
39
+ docker run --link memcache:memcache -d -p 9999:9999 bbcnews/anmo
40
+
41
+ ```
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ require "bundler/gem_tasks"
2
+ require "cucumber"
3
+ require "cucumber/rake/task"
4
+
5
+ task :default => [:test, :features]
6
+
7
+ task :test do
8
+ Dir.glob("test/**/*_test.rb").each do |f|
9
+ require File.expand_path(File.join(File.dirname(__FILE__), f))
10
+ end
11
+ end
12
+
13
+ task :run do
14
+ require "anmo"
15
+ Anmo.launch_server 1234
16
+ end
17
+
18
+ Cucumber::Rake::Task.new(:features) do |t|
19
+ t.cucumber_opts = "features --format pretty"
20
+ end
data/anmo.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $LOAD_PATH.push File.expand_path("../lib", __FILE__)
3
+ require "anmo/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "anmo2"
7
+ s.version = Anmo::VERSION
8
+ s.authors = ["Andrew Vos"]
9
+ s.email = ["andrew.vos@gmail.com"]
10
+ s.homepage = ""
11
+ s.summary = ""
12
+ s.description = ""
13
+
14
+ s.rubyforge_project = "anmo"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ s.add_runtime_dependency "rack"
22
+ s.add_runtime_dependency "thin", "1.6.3"
23
+ s.add_runtime_dependency "httparty"
24
+ s.add_runtime_dependency "dalli"
25
+
26
+ s.add_development_dependency "rake"
27
+ s.add_development_dependency "minitest"
28
+ s.add_development_dependency "rack-test"
29
+ s.add_development_dependency "cucumber"
30
+ s.add_development_dependency "rspec"
31
+ end
@@ -0,0 +1,204 @@
1
+ Feature: API
2
+ In order to test applications against stable APIs
3
+ As a developer
4
+ I want to be able to mock http endpoints
5
+
6
+ Scenario: Launch a server
7
+ When I execute the following code in a new thread
8
+ """
9
+ Anmo.launch_server
10
+ """
11
+ Then I see an anmo server on port 8787
12
+
13
+ Scenario: Launch a server with a port
14
+ When I execute the following code in a new thread
15
+ """
16
+ Anmo.launch_server 9393
17
+ """
18
+ Then I see an anmo server on port 9393
19
+
20
+ Scenario: Create a request
21
+ Given an anmo server
22
+ When I execute the code
23
+ """
24
+ Anmo.server = "http://localhost:8787"
25
+ Anmo.create_request({
26
+ :path => "/lollll",
27
+ :body => "can't think of anything",
28
+ :required_headers => {"meh" => "bleh", "blah" => "blah"}
29
+ })
30
+ """
31
+ And I request the uri "http://localhost:8787/lollll" with the headers
32
+ """
33
+ {
34
+ "meh": "bleh",
35
+ "blah": "blah"
36
+ }
37
+ """
38
+ Then I see the response body "can't think of anything"
39
+
40
+ Scenario: Save object
41
+ Given an anmo server
42
+ When I execute the code
43
+ """
44
+ Anmo.server = "http://localhost:8787"
45
+ Anmo.create_request({
46
+ :path => "/some/object",
47
+ :body => "some data"
48
+ })
49
+ """
50
+ And I request the uri "http://localhost:8787/some/object"
51
+ Then I see the response body "some data"
52
+
53
+ Scenario: Request only returns object if it has correct query parameters
54
+ Given an anmo server
55
+ When I execute the code
56
+ """
57
+ Anmo.server = "http://localhost:8787"
58
+ Anmo.create_request({
59
+ :path => "/path?monkeys=12",
60
+ :body => "some data"
61
+ })
62
+ """
63
+ When I request the uri "http://localhost:8787/path?monkeys=12"
64
+ Then I see the response code 200
65
+ And I see the response body "some data"
66
+
67
+ Scenario: Save object with http status code
68
+ Given an anmo server
69
+ When I execute the code
70
+ """
71
+ Anmo.server = "http://localhost:8787"
72
+ Anmo.create_request({
73
+ :path => "/some/object",
74
+ :status => 123
75
+ })
76
+ """
77
+ And I request the uri "http://localhost:8787/some/object"
78
+ Then I see the response code 123
79
+
80
+ Scenario: Save object with specific method
81
+ Given an anmo server
82
+ When I execute the code
83
+ """
84
+ Anmo.server = "http://localhost:8787"
85
+ Anmo.create_request({
86
+ :path => "/some/object",
87
+ :body => "some data",
88
+ :method => :put
89
+ })
90
+ """
91
+ And I issue a put request to the uri "http://localhost:8787/some/object"
92
+ Then I see the response body "some data"
93
+ And I issue a get request to the uri "http://localhost:8787/some/object"
94
+ Then I see the response code 404
95
+
96
+ Scenario: Request without required headers 404s
97
+ Given an anmo server
98
+ When I execute the code
99
+ """
100
+ Anmo.server = "http://localhost:8787"
101
+ Anmo.create_request({
102
+ :path => "/some/object",
103
+ :required_headers => {:meh => "bleh", :durp => "derp"}
104
+ })
105
+ """
106
+ And I request the uri "http://localhost:8787/some/object"
107
+ Then I see the response code 404
108
+
109
+ Scenario: Request with required headers
110
+ Given an anmo server
111
+ When I execute the code
112
+ """
113
+ Anmo.server = "http://localhost:8787"
114
+ Anmo.create_request({
115
+ :path => "/some/object",
116
+ :body => "helloow",
117
+ :required_headers => {:meh => "bleh", :durp => "derp"}
118
+ })
119
+ """
120
+ And I request the uri "http://localhost:8787/some/object" with the headers
121
+ """
122
+ {
123
+ "meh": "bleh",
124
+ "durp": "derp"
125
+ }
126
+ """
127
+ Then I see the response body "helloow"
128
+ And I see the response code 200
129
+
130
+ Scenario: Delete all saved objects
131
+ Given an anmo server
132
+ When I execute the code
133
+ """
134
+ Anmo.server = "http://localhost:8787"
135
+ Anmo.create_request({
136
+ :path => "/some/object",
137
+ :body => "some data"
138
+ })
139
+ """
140
+ And I execute the code
141
+ """
142
+ Anmo.delete_all
143
+ """
144
+ And I request the uri "http://localhost:8787/some/object"
145
+ Then I see the response code 404
146
+
147
+ Scenario: Store all requests
148
+ Given an anmo server
149
+ When I request the uri "http://localhost:8787/some/object"
150
+ Then that request should be stored
151
+
152
+ Scenario: Delete all requests
153
+ Given an anmo server
154
+ When I request the uri "http://localhost:8787/some/object"
155
+ And I execute the code
156
+ """
157
+ Anmo.server = "http://localhost:8787"
158
+ Anmo.delete_all_requests
159
+ """
160
+ Then there should be no stored requests
161
+
162
+ Scenario: List all saved objects
163
+ Given an anmo server
164
+ When I execute the code
165
+ """
166
+ Anmo.server = "http://localhost:8787"
167
+ Anmo.create_request({
168
+ :path => "/some/object",
169
+ :body => "some data"
170
+ })
171
+
172
+ @result = Anmo.stored_objects
173
+ """
174
+ Then I should see the value
175
+ """
176
+ [
177
+ {"path" => "/some/object", "body" => "some data"}
178
+ ]
179
+ """
180
+
181
+ Scenario: Anmo knows if a server is not running
182
+ When I execute the code
183
+ """
184
+ Anmo.server = "http://localhost:8459"
185
+ @result = Anmo.running?
186
+ """
187
+ Then I see that the anmo server is not running
188
+
189
+ Scenario: Anmo knows if a server is running
190
+ Given an anmo server
191
+ When I execute the code
192
+ """
193
+ Anmo.server = "http://localhost:8787"
194
+ @result = Anmo.running?
195
+ """
196
+ Then I see that the anmo server is running
197
+
198
+ Scenario: Anmo knows what version the server is running
199
+ Given an anmo server
200
+ When I execute the code
201
+ """
202
+ Anmo.server_version
203
+ """
204
+ Then I see the anmo version
@@ -0,0 +1,87 @@
1
+ require "stringio"
2
+
3
+ When /^I execute the following code in a new thread$/ do |code|
4
+ Thread.new do
5
+ eval code
6
+ end
7
+ end
8
+
9
+ When /^I execute the code$/ do |code|
10
+ @result = eval code
11
+ end
12
+
13
+ Then /^I see an anmo server on port (\d+)$/ do |port|
14
+ response = nil
15
+
16
+ timeout 5 do
17
+ while response.nil?
18
+ response = HTTParty.get("http://localhost:#{port}") rescue nil
19
+ end
20
+ end
21
+
22
+ response.body.should include "Not Found"
23
+ end
24
+
25
+ Given /^an anmo server$/ do
26
+ Thread.new do
27
+ Anmo.launch_server
28
+ end
29
+
30
+ timeout 5 do
31
+ response = nil
32
+ while response.nil?
33
+ response = HTTParty.get("http://localhost:8787") rescue nil
34
+ end
35
+ sleep 0.1
36
+ end
37
+ end
38
+
39
+ Then /^I issue a get request to the uri "([^"]*)"$/ do |uri|
40
+ @response = HTTParty.get(uri)
41
+ end
42
+
43
+ When /^I issue a put request to the uri "([^"]*)"$/ do |uri|
44
+ @response = HTTParty.put(uri)
45
+ end
46
+
47
+ When /^I request the uri "([^"]*)"$/ do |uri|
48
+ @requested_uri = uri
49
+ @response = HTTParty.get(uri)
50
+ end
51
+
52
+ When /^I request the uri "([^"]*)" with the headers$/ do |uri, headers|
53
+ headers = JSON.parse(headers)
54
+ @response = HTTParty.get(uri, :headers => headers)
55
+ end
56
+
57
+ Then /^I see the response body "([^"]*)"$/ do |body|
58
+ @response.body.should == body
59
+ end
60
+
61
+ Then /^I see the response code (\d+)$/ do |code|
62
+ @response.code.should == code.to_i
63
+ end
64
+
65
+ Then /^that request should be stored$/ do
66
+ Anmo.requests.last["PATH_INFO"].should == @requested_uri.gsub("http://localhost:8787", "")
67
+ end
68
+
69
+ Then /^there should be no stored requests$/ do
70
+ Anmo.requests.size.should == 0
71
+ end
72
+
73
+ Then /^I should see the value$/ do |code|
74
+ @result.should == eval(code)
75
+ end
76
+
77
+ Then /^I see that the anmo server is not running$/ do
78
+ @result.should == false
79
+ end
80
+
81
+ Then /^I see that the anmo server is running$/ do
82
+ @result.should == true
83
+ end
84
+
85
+ Then /^I see the anmo version$/ do
86
+ @result.should == Anmo::VERSION
87
+ end
@@ -0,0 +1,13 @@
1
+ require "rack"
2
+ require "rack/test"
3
+ require "httparty"
4
+
5
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), "../../lib")))
6
+ require "anmo"
7
+ require "anmo/application"
8
+
9
+ World(Rack::Test::Methods)
10
+
11
+ def app
12
+ Anmo::Application.new
13
+ end
data/lib/anmo.rb ADDED
@@ -0,0 +1,57 @@
1
+ require "anmo/version"
2
+ require "anmo/application"
3
+ require "thin"
4
+ require "httparty"
5
+ require "json"
6
+
7
+ module Anmo
8
+ def self.server
9
+ @server ||= "http://localhost:#{@port}"
10
+ end
11
+
12
+ class << self
13
+ attr_writer :server
14
+ end
15
+
16
+ def self.server_version
17
+ HTTParty.get(anmo_url("__VERSION__")).body
18
+ end
19
+
20
+ def self.launch_server(port = 8787)
21
+ @port = port
22
+ Thin::Server.start("0.0.0.0", port, Anmo::Application.new)
23
+ end
24
+
25
+ def self.create_request(options)
26
+ HTTParty.post(anmo_url("__CREATE_OBJECT__"), :body => options.to_json, :headers => { "Content-Type" => "application/json" })
27
+ end
28
+
29
+ def self.delete_all
30
+ HTTParty.post(anmo_url("__DELETE_ALL_OBJECTS__"))
31
+ end
32
+
33
+ def self.requests
34
+ json = HTTParty.get(anmo_url("__REQUESTS__"))
35
+ JSON.parse(json.body)
36
+ end
37
+
38
+ def self.delete_all_requests
39
+ HTTParty.post(anmo_url("__DELETE_ALL_REQUESTS__"))
40
+ end
41
+
42
+ def self.stored_objects
43
+ json = HTTParty.get(anmo_url("__OBJECTS__"))
44
+ JSON.parse(json.body)
45
+ end
46
+
47
+ def self.running?
48
+ HTTParty.get(anmo_url("__ALIVE__")).code == 200 rescue false
49
+ end
50
+
51
+ private
52
+
53
+ def self.anmo_url(path)
54
+ "#{server}/#{path}"
55
+ # URI.join(server, path).to_s
56
+ end
57
+ end
@@ -0,0 +1,212 @@
1
+ require "dalli"
2
+ require "uri"
3
+
4
+ module Anmo
5
+ class ApplicationDataStore
6
+ TTL = 1_000_000
7
+ def self.objects
8
+ cached_objects = server.get("objects")
9
+ return JSON.load(cached_objects) if cached_objects
10
+ []
11
+ end
12
+
13
+ def self.requests
14
+ cached_requests = server.get("requests")
15
+ return JSON.load(cached_requests) if cached_requests
16
+ []
17
+ end
18
+
19
+ def self.insert_object(object)
20
+ all_objects = objects
21
+ all_objects.unshift(object)
22
+ server.set("objects", JSON.dump(all_objects), TTL)
23
+ end
24
+
25
+ def self.save_request(request)
26
+ all_requests = requests
27
+ all_requests << request
28
+ server.set("requests", JSON.dump(all_requests), TTL)
29
+ end
30
+
31
+ def self.clear_objects
32
+ server.delete("objects")
33
+ end
34
+
35
+ def self.clear_requests
36
+ server.delete("requests")
37
+ end
38
+
39
+ def self.find(path, query_string)
40
+ objects.each do |object|
41
+ object_path = object["path"].gsub(/(\?.*)$/, "")
42
+ if path == object_path
43
+ object_query_string = Rack::Utils.parse_query(object["path"].gsub(/(.*?)\?/, ""))
44
+
45
+ if query_string == object_query_string
46
+ p object
47
+ return object
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def self.server
56
+ @server ||= if ENV["MEMCACHE_SERVERS"]
57
+ Dalli::Client.new(ENV["MEMCACHE_SERVERS"],
58
+ :username => ENV["MEMCACHE_USERNAME"],
59
+ :password => ENV["MEMCACHE_PASSWORD"],
60
+ :namespace => Process.pid.to_s
61
+ )
62
+ else
63
+ Dalli::Client.new "127.0.0.1:11211", :namespace => Process.pid.to_s
64
+ end
65
+ end
66
+ end
67
+
68
+ class Application
69
+ def initialize
70
+ ApplicationDataStore.clear_objects
71
+ ApplicationDataStore.clear_requests
72
+ end
73
+
74
+ def call(env)
75
+ request = Rack::Request.new(env)
76
+
77
+ if request.request_method.upcase == "OPTIONS"
78
+ return [
79
+ 200,
80
+ {
81
+ "Access-Control-Allow-Origin" => "*",
82
+ "Access-Control-Allow-Methods" => "*",
83
+ "Access-Control-Allow-Headers" => "X-Requested-With,Content-Type,Authorization"
84
+ },
85
+ ""
86
+ ]
87
+ end
88
+
89
+ controller_methods = [
90
+ :alive,
91
+ :version,
92
+ :create_object,
93
+ :objects,
94
+ :requests,
95
+ :delete_all_objects,
96
+ :delete_all_requests
97
+ ]
98
+
99
+ method = controller_methods.find { |m| request.path_info =~ /\/?__#{m.to_s.upcase}__\/?/ }
100
+ method ||= :process_normal_request
101
+ send(method, request)
102
+ end
103
+
104
+ private
105
+
106
+ def text(text, status = 200)
107
+ [status, { "Content-Type" => "text/html", "Access-Control-Allow-Origin" => "*" }, [text]]
108
+ end
109
+
110
+ def json(json, status = 200)
111
+ [status, { "Content-Type" => "application/json", "Access-Control-Allow-Origin" => "*" }, [json]]
112
+ end
113
+
114
+ def alive(_request)
115
+ text "<h1>anmo is alive</h1>"
116
+ end
117
+
118
+ def version(_request)
119
+ text Anmo::VERSION
120
+ end
121
+
122
+ def create_object(request)
123
+ request_info = JSON.parse(read_request_body(request))
124
+ ApplicationDataStore.insert_object(request_info)
125
+ text "", 201
126
+ end
127
+
128
+ def delete_all_objects(_request)
129
+ ApplicationDataStore.clear_objects
130
+ text ""
131
+ end
132
+
133
+ def process_normal_request(request)
134
+ ApplicationDataStore.save_request(request.env)
135
+
136
+ if found_request = find_stored_request(request)
137
+ text found_request["body"], Integer(found_request["status"] || 200)
138
+ else
139
+ text "Not Found", 404
140
+ end
141
+ end
142
+
143
+ def requests(_request)
144
+ json ApplicationDataStore.requests.to_json
145
+ end
146
+
147
+ def delete_all_requests(_request)
148
+ ApplicationDataStore.clear_requests
149
+ text ""
150
+ end
151
+
152
+ def objects(_request)
153
+ json ApplicationDataStore.objects.to_json
154
+ end
155
+
156
+ def find_stored_request(actual_request)
157
+ actual_request_query = Rack::Utils.parse_query(actual_request.query_string)
158
+
159
+ suspected_request = ApplicationDataStore.objects.find do |r|
160
+ uri = URI(r["path"])
161
+
162
+ query = Rack::Utils.parse_query(uri.query)
163
+ method = r["method"] || "GET"
164
+
165
+ if uri.query.nil?
166
+ uri.path == actual_request.path_info && method.upcase == actual_request.request_method.upcase
167
+ else
168
+ uri.path == actual_request.path_info && query == actual_request_query && method.upcase == actual_request.request_method.upcase
169
+ end
170
+ end
171
+
172
+ unless suspected_request.nil?
173
+ return unless request_has_required_headers(actual_request, suspected_request)
174
+ end
175
+
176
+ suspected_request
177
+ end
178
+
179
+ def request_has_same_method(initial_request, suspected_request)
180
+ return true if suspected_request["method"].nil?
181
+ suspected_request["method"].upcase == initial_request.request_method
182
+ end
183
+
184
+ def request_has_same_query(initial_request, suspected_request)
185
+ return true if suspected_request["path"].include?("?") == false
186
+ query = Rack::Utils.parse_query(suspected_request["path"].gsub(/.*\?/, ""))
187
+ query == Rack::Utils.parse_query(initial_request.query_string)
188
+ end
189
+
190
+ def request_has_required_headers(initial_request, suspected_request)
191
+ required_headers = suspected_request["required_headers"] || {}
192
+ required_headers.each do |name, value|
193
+ if initial_request.env[convert_header_name_to_rack_style_name(name)] != value
194
+ return false
195
+ end
196
+ end
197
+ true
198
+ end
199
+
200
+ def convert_header_name_to_rack_style_name(name)
201
+ name = "HTTP_#{name}"
202
+ name.gsub!("-", "_")
203
+ name.upcase!
204
+ name
205
+ end
206
+
207
+ def read_request_body(request)
208
+ request.body.rewind
209
+ request.body.read
210
+ end
211
+ end
212
+ end
@@ -0,0 +1,3 @@
1
+ module Anmo
2
+ VERSION = "0.0.31"
3
+ end
data/run.sh ADDED
@@ -0,0 +1,8 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require "anmo"
4
+
5
+ ENV['MEMCACHE_SERVERS'] = "#{ENV['MEMCACHE_PORT_11211_TCP_ADDR']}:11211"
6
+
7
+ Anmo.server = "0.0.0.0"
8
+ Anmo.launch_server 9999
@@ -0,0 +1,201 @@
1
+ require "minitest/pride"
2
+ require "minitest/autorun"
3
+ require "minitest/unit"
4
+ require "rack/test"
5
+ require "json"
6
+ require File.expand_path(File.join(File.dirname(__FILE__), "../../lib/anmo/application"))
7
+
8
+ module Anmo
9
+ class ApplicationTest < MiniTest::Unit::TestCase
10
+ include Rack::Test::Methods
11
+
12
+ def app
13
+ Application.new
14
+ end
15
+
16
+ def setup
17
+ ApplicationDataStore.clear_objects
18
+ ApplicationDataStore.clear_requests
19
+ end
20
+
21
+ def save_object(path, body, status, required_headers, method)
22
+ options = {
23
+ :path => path,
24
+ :body => body,
25
+ :status => status,
26
+ :required_headers => required_headers
27
+ }
28
+ options[:method] = method if method
29
+
30
+ put "__CREATE_OBJECT__", options.to_json
31
+ end
32
+
33
+ def test_404s_if_object_does_not_exist
34
+ get "/bla/bla/bla.json"
35
+ assert_equal "Not Found", last_response.body
36
+ assert last_response.not_found?
37
+ end
38
+
39
+ def test_stores_mock_data
40
+ save_object "/this/is/the/path.object", "please save this", nil, nil, nil
41
+ assert_equal 201, last_response.status
42
+
43
+ get "/this/is/the/path.object"
44
+ assert_equal "please save this", last_response.body
45
+ assert last_response.ok?
46
+ end
47
+
48
+ def test_does_not_return_object_if_request_has_different_query_parameters
49
+ save_object "/path/?hello=true", "please save this", nil, nil, nil
50
+ get "/path/?hello=false"
51
+ assert_equal 404, last_response.status
52
+ assert_equal "Not Found", last_response.body
53
+ end
54
+
55
+ def test_returns_object_if_request_has_same_query_parameters
56
+ save_object "/?hello=true&bla=bla", "please save this", nil, nil, nil
57
+ get "/?hello=true&bla=bla"
58
+ assert_equal 200, last_response.status
59
+ assert_equal "please save this", last_response.body
60
+ end
61
+
62
+ def test_returns_object_if_request_has_query_parameters_in_different_order
63
+ save_object "/?aaa=aaa&bbb=bbb", "please save this", nil, nil, nil
64
+ get "/?bbb=bbb&aaa=aaa"
65
+ assert_equal 200, last_response.status
66
+ assert_equal "please save this", last_response.body
67
+ end
68
+
69
+ def test_stores_status_code
70
+ save_object "/monkeys", nil, 123, nil, nil
71
+ get "/monkeys"
72
+ assert_equal 123, last_response.status
73
+ end
74
+
75
+ def test_similar_query_string
76
+ save_object "/pulsar/assets?channel=foo.bar.baz.qux.quux.1", "Something", 200, nil, :get
77
+ save_object "/pulsar/assets?channel=foo.bar.baz.qux.quux.2", "Something else", 200, nil, :get
78
+
79
+ get "/pulsar/assets?channel=foo.bar.baz.qux.quux.1"
80
+
81
+ assert_equal 200, last_response.status
82
+ end
83
+
84
+ def test_allows_deleting_all_objects
85
+ save_object "/this/is/the/path.object", "please save this", nil, nil, nil
86
+
87
+ get "/this/is/the/path.object"
88
+ first_response = last_response
89
+
90
+ post "__DELETE_ALL_OBJECTS__"
91
+ assert_equal 200, last_response.status
92
+
93
+ get "/this/is/the/path.object"
94
+ second_response = last_response
95
+
96
+ assert_equal "please save this", first_response.body
97
+ assert_equal "Not Found", second_response.body
98
+ assert_equal 404, second_response.status
99
+ end
100
+
101
+ def test_404s_if_request_does_not_have_required_headers
102
+ save_object "/oh/hai", nil, nil, { "ruby" => "hipsters", "meh" => "bleh" }, nil
103
+ get "/oh/hai"
104
+ assert_equal 404, last_response.status
105
+ end
106
+
107
+ def test_returns_value_if_request_has_required_headers
108
+ save_object "/oh/hai", "the content", nil, { "lol-ruby" => "hipsters", "meh" => "bleh" }, nil
109
+ header "lol-ruby", "hipsters"
110
+ header "meh", "bleh"
111
+ get "/oh/hai"
112
+ assert_equal 200, last_response.status
113
+ assert_equal "the content", last_response.body
114
+ end
115
+
116
+ def test_404s_if_request_does_not_have_correct_method
117
+ save_object "/meh", "content", nil, nil, :delete
118
+ get "/meh"
119
+ assert_equal 404, last_response.status
120
+ end
121
+
122
+ def test_returns_vaue_if_request_has_correct_method
123
+ save_object "/meh", "content", nil, nil, :delete
124
+ delete "/meh"
125
+ assert_equal 200, last_response.status
126
+ end
127
+
128
+ def test_returns_the_last_added_object_first
129
+ save_object "/oh/hai", "the first content", nil, nil, nil
130
+ save_object "/oh/hai", "the second content", nil, nil, nil
131
+ get "/oh/hai"
132
+ assert_equal "the second content", last_response.body
133
+ end
134
+
135
+ def test_stores_all_requests
136
+ get "/hello"
137
+ get "/hai"
138
+ get "/__REQUESTS__"
139
+ json = JSON.parse(last_response.body)
140
+
141
+ assert_equal 2, json.size
142
+ assert_equal "/hello", json.first["PATH_INFO"]
143
+ assert_equal "/hai", json.last["PATH_INFO"]
144
+ end
145
+
146
+ def test_does_not_store_create_or_delete_requests
147
+ save_object "/oh/hai", "the first content", nil, nil, nil
148
+ post "__DELETE_ALL_OBJECTS__"
149
+ get "/__REQUESTS__"
150
+ json = JSON.parse(last_response.body)
151
+ assert_equal 0, json.size
152
+ end
153
+
154
+ def test_returns_requests_as_json
155
+ get "/hello"
156
+ get "/__REQUESTS__"
157
+ json = JSON.parse(last_response.body)
158
+ assert_equal 1, json.size
159
+ assert_equal "/hello", json.first["PATH_INFO"]
160
+ end
161
+
162
+ def test_deletes_all_requests
163
+ get "/hello"
164
+ get "/__REQUESTS__"
165
+ json = JSON.parse(last_response.body)
166
+ assert_equal 1, json.size
167
+ post "/__DELETE_ALL_REQUESTS__"
168
+ get "/__REQUESTS__"
169
+ json = JSON.parse(last_response.body)
170
+ assert_equal 0, json.size
171
+ end
172
+
173
+ def test_returns_empty_list_of_stored_objects
174
+ get "/__OBJECTS__"
175
+ json = JSON.parse(last_response.body)
176
+ assert_equal "application/json", last_response.content_type
177
+ assert_equal 0, json.size
178
+ end
179
+
180
+ def test_lists_stored_objects
181
+ save_object "/some/path", nil, nil, nil, nil
182
+
183
+ get "/__OBJECTS__"
184
+
185
+ json = JSON.parse(last_response.body)
186
+ assert_equal 1, json.size
187
+ assert_equal "/some/path", json.first["path"]
188
+ end
189
+
190
+ def test_shows_that_its_alive
191
+ get "/__ALIVE__"
192
+ assert last_response.ok?
193
+ assert_equal "<h1>anmo is alive</h1>", last_response.body
194
+ end
195
+
196
+ def test_exposes_server_version
197
+ get "/__VERSION__"
198
+ assert_equal Anmo::VERSION, last_response.body
199
+ end
200
+ end
201
+ end
metadata ADDED
@@ -0,0 +1,191 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anmo2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.31
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Vos
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-07-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rack
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: thin
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.6.3
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.6.3
41
+ - !ruby/object:Gem::Dependency
42
+ name: httparty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: dalli
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: minitest
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack-test
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: cucumber
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: ''
140
+ email:
141
+ - andrew.vos@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - ".gitignore"
147
+ - ".rubocop.yml"
148
+ - ".ruby-version"
149
+ - ".rvmrc"
150
+ - ".travis.yml"
151
+ - Dockerfile
152
+ - Gemfile
153
+ - README.md
154
+ - Rakefile
155
+ - anmo.gemspec
156
+ - features/api.feature
157
+ - features/step_definitions/api_steps.rb
158
+ - features/support/env.rb
159
+ - lib/anmo.rb
160
+ - lib/anmo/application.rb
161
+ - lib/anmo/version.rb
162
+ - run.sh
163
+ - test/anmo/application_test.rb
164
+ homepage: ''
165
+ licenses: []
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubyforge_project: anmo
183
+ rubygems_version: 2.4.2
184
+ signing_key:
185
+ specification_version: 4
186
+ summary: ''
187
+ test_files:
188
+ - features/api.feature
189
+ - features/step_definitions/api_steps.rb
190
+ - features/support/env.rb
191
+ - test/anmo/application_test.rb