anmo2 0.0.31

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