Skip to content

Commit fd71bb8

Browse files
committed
Merge branch 'lvarvel-master'
2 parents 9092df7 + a3b991d commit fd71bb8

File tree

9 files changed

+300
-30
lines changed

9 files changed

+300
-30
lines changed

.ruby-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
2.1.1

Gemfile.lock

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PATH
33
specs:
44
rspec_api_documentation (4.2.0)
55
activesupport (>= 3.0.0)
6+
faraday (>= 0.9.0)
67
json (~> 1.4, >= 1.4.6)
78
mustache (~> 0.99, >= 0.99.4)
89
rspec (>= 3.0.0)
@@ -40,8 +41,12 @@ GEM
4041
gherkin (~> 2.12)
4142
multi_json (>= 1.7.5, < 2.0)
4243
multi_test (>= 0.1.1)
44+
daemons (1.1.9)
4345
diff-lcs (1.2.5)
46+
eventmachine (1.0.3)
4447
fakefs (0.6.0)
48+
faraday (0.9.0)
49+
multipart-post (>= 1.2, < 3)
4550
ffi (1.9.5)
4651
gherkin (2.12.2)
4752
multi_json (~> 1.3)
@@ -59,6 +64,7 @@ GEM
5964
minitest (5.4.2)
6065
multi_json (1.10.1)
6166
multi_test (0.1.1)
67+
multipart-post (2.0.0)
6268
mustache (0.99.6)
6369
nokogiri (1.6.3.1)
6470
mini_portile (= 0.6.0)
@@ -102,6 +108,10 @@ GEM
102108
sparkr (0.4.1)
103109
term-ansicolor (1.3.0)
104110
tins (~> 1.0)
111+
thin (1.6.2)
112+
daemons (>= 1.0.9)
113+
eventmachine (>= 1.0.0)
114+
rack (>= 1.0.0)
105115
thread_safe (0.3.4)
106116
tilt (1.4.1)
107117
tins (1.3.3)
@@ -128,4 +138,5 @@ DEPENDENCIES
128138
rspec-its (~> 1.0)
129139
rspec_api_documentation!
130140
sinatra (~> 1.4.4)
141+
thin
131142
webmock (~> 1.7)

lib/rspec_api_documentation.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ module RspecApiDocumentation
1919
autoload :Index
2020
autoload :ClientBase
2121
autoload :Headers
22+
autoload :HttpTestClient
2223
end
2324

2425
autoload :DSL

lib/rspec_api_documentation/client_base.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,16 @@ def process(method, path, params = {}, headers ={})
4343
document_example(method.to_s.upcase, path)
4444
end
4545

46+
def read_request_body
47+
input = last_request.env["rack.input"]
48+
input.rewind
49+
input.read
50+
end
51+
4652
def document_example(method, path)
4753
return unless metadata[:document]
4854

49-
input = last_request.env["rack.input"]
50-
input.rewind
51-
request_body = input.read
55+
request_body = read_request_body
5256

5357
request_metadata = {}
5458

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
require 'faraday'
2+
3+
Faraday::Request.register_middleware :request_saver => lambda { RspecApiDocumentation::RequestSaver }
4+
5+
module RspecApiDocumentation
6+
class RequestSaver < Faraday::Middleware
7+
attr_reader :client
8+
9+
def initialize(app, client)
10+
super(app)
11+
@client = client
12+
end
13+
14+
def call(env)
15+
client.last_request = env
16+
17+
@app.call(env).on_complete do |env|
18+
client.last_response = env
19+
end
20+
end
21+
end
22+
23+
class HttpTestClient < ClientBase
24+
attr_reader :last_response, :last_request
25+
26+
LastRequest = Struct.new(:url, :method, :request_headers, :body)
27+
28+
def request_headers
29+
env_to_headers(last_request.request_headers)
30+
end
31+
32+
def response_headers
33+
last_response.response_headers
34+
end
35+
36+
def query_string
37+
last_request.url.query
38+
end
39+
40+
def status
41+
last_response.status
42+
end
43+
44+
def response_body
45+
last_response.body
46+
end
47+
48+
def request_content_type
49+
last_request.request_headers["CONTENT_TYPE"]
50+
end
51+
52+
def response_content_type
53+
last_response.request_headers["CONTENT_TYPE"]
54+
end
55+
56+
def do_request(method, path, params, request_headers)
57+
http_test_session.send(method, path, params, headers(method, path, params, request_headers))
58+
end
59+
60+
def last_request=(env)
61+
@last_request = LastRequest.new(env.url, env.method, env.request_headers, env.body)
62+
end
63+
64+
def last_response=(env)
65+
@last_response = env
66+
end
67+
68+
protected
69+
70+
def headers(*args)
71+
headers_to_env(super)
72+
end
73+
74+
def handle_multipart_body(request_headers, request_body)
75+
parsed_parameters = Rack::Request.new({
76+
"CONTENT_TYPE" => request_headers["Content-Type"],
77+
"rack.input" => StringIO.new(request_body)
78+
}).params
79+
80+
clean_out_uploaded_data(parsed_parameters, request_body)
81+
end
82+
83+
def read_request_body
84+
if [:post, :put].include?(last_request.method)
85+
last_request.body || ""
86+
else
87+
""
88+
end
89+
end
90+
91+
private
92+
93+
def clean_out_uploaded_data(params, request_body)
94+
params.each do |_, value|
95+
if value.is_a?(Hash)
96+
if value.has_key?(:tempfile)
97+
data = value[:tempfile].read
98+
request_body = request_body.gsub(data, "[uploaded data]")
99+
else
100+
request_body = clean_out_uploaded_data(value,request_body)
101+
end
102+
end
103+
end
104+
request_body
105+
end
106+
107+
108+
def http_test_session
109+
::Faraday.new(:url => options[:host]) do |faraday|
110+
faraday.request :url_encoded # form-encode POST params
111+
faraday.request :request_saver, self # save the request and response
112+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
113+
end
114+
end
115+
end
116+
end

rspec_api_documentation.gemspec

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Gem::Specification.new do |s|
1818
s.add_runtime_dependency "activesupport", ">= 3.0.0"
1919
s.add_runtime_dependency "mustache", "~> 0.99", ">= 0.99.4"
2020
s.add_runtime_dependency "json", "~> 1.4", ">= 1.4.6"
21+
s.add_runtime_dependency "faraday", ">= 0.9.0"
2122

2223
s.add_development_dependency "fakefs", "~> 0.4"
2324
s.add_development_dependency "sinatra", "~> 1.4.4"
@@ -28,6 +29,7 @@ Gem::Specification.new do |s|
2829
s.add_development_dependency "rack-oauth2", "~> 1.0.7"
2930
s.add_development_dependency "webmock", "~> 1.7"
3031
s.add_development_dependency "rspec-its", "~> 1.0"
32+
s.add_development_dependency "thin"
3133

3234
s.files = Dir.glob("lib/**/*") + Dir.glob("templates/**/*")
3335
s.require_path = "lib"

spec/http_test_client_spec.rb

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
require 'spec_helper'
2+
require 'rack/test'
3+
require 'capybara'
4+
require 'capybara/server'
5+
require 'sinatra/base'
6+
require 'webmock'
7+
require 'support/stub_app'
8+
9+
describe RspecApiDocumentation::HttpTestClient do
10+
before(:all) do
11+
WebMock.allow_net_connect!
12+
13+
Capybara.server do |app, port|
14+
require 'rack/handler/thin'
15+
Thin::Logging.silent = true
16+
Rack::Handler::Thin.run(app, :Port => port)
17+
end
18+
19+
server = Capybara::Server.new(StubApp.new, 8888)
20+
server.boot
21+
end
22+
23+
after(:all) do
24+
WebMock.disable_net_connect!
25+
end
26+
27+
let(:client_context) { |example| double(example: example, app_root: 'nowhere') }
28+
let(:target_host) { 'http://localhost:8888' }
29+
let(:test_client) { RspecApiDocumentation::HttpTestClient.new(client_context, {host: target_host}) }
30+
31+
subject { test_client }
32+
33+
it { should be_a(RspecApiDocumentation::HttpTestClient) }
34+
35+
its(:context) { should equal(client_context) }
36+
its(:example) { |example| should equal(example) }
37+
its(:metadata) { |example| should equal(example.metadata) }
38+
39+
describe "xml data", :document => true do
40+
before do
41+
test_client.get "/xml"
42+
end
43+
44+
it "should handle xml data" do
45+
expect(test_client.response_headers["Content-Type"]).to match(/application\/xml/)
46+
end
47+
48+
it "should log the request" do |example|
49+
expect(example.metadata[:requests].first[:response_body]).to be_present
50+
end
51+
end
52+
53+
describe "#query_string" do
54+
before do
55+
test_client.get "/?query_string=true"
56+
end
57+
58+
it 'should contain the query_string' do
59+
expect(test_client.query_string).to eq("query_string=true")
60+
end
61+
end
62+
63+
describe "#request_headers" do
64+
before do
65+
test_client.get "/", {}, { "Accept" => "application/json", "Content-Type" => "application/json" }
66+
end
67+
68+
it "should contain all the headers" do
69+
expect(test_client.request_headers).to eq({
70+
"Accept" => "application/json",
71+
"Content-Type" => "application/json"
72+
})
73+
end
74+
end
75+
76+
context "when doing request without parameter value" do
77+
before do
78+
test_client.post "/greet?query=&other=exists"
79+
end
80+
81+
context "when examples should be documented", :document => true do
82+
it "should still argument the metadata" do |example|
83+
metadata = example.metadata[:requests].first
84+
expect(metadata[:request_query_parameters]).to eq({'query' => "", 'other' => 'exists'})
85+
end
86+
end
87+
end
88+
89+
context "after a request is made" do
90+
before do
91+
test_client.post "/greet?query=test+query", post_data, headers
92+
end
93+
94+
let(:post_data) { { :target => "nurse" }.to_json }
95+
let(:headers) { { "Content-Type" => "application/json;charset=utf-8", "X-Custom-Header" => "custom header value" } }
96+
97+
context "when examples should be documented", :document => true do
98+
it "should augment the metadata with information about the request" do |example|
99+
metadata = example.metadata[:requests].first
100+
expect(metadata[:request_method]).to eq("POST")
101+
expect(metadata[:request_path]).to eq("/greet?query=test+query")
102+
expect(metadata[:request_body]).to be_present
103+
expect(metadata[:request_headers]).to include({'Content-Type' => 'application/json;charset=utf-8'})
104+
expect(metadata[:request_headers]).to include({'X-Custom-Header' => 'custom header value'})
105+
expect(metadata[:request_query_parameters]).to eq({"query" => "test query"})
106+
expect(metadata[:request_content_type]).to match(/application\/json/)
107+
expect(metadata[:response_status]).to eq(200)
108+
expect(metadata[:response_body]).to be_present
109+
expect(metadata[:response_headers]['Content-Type']).to match(/application\/json/)
110+
expect(metadata[:response_headers]['Content-Length']).to eq('17')
111+
expect(metadata[:response_content_type]).to match(/application\/json/)
112+
expect(metadata[:curl]).to eq(RspecApiDocumentation::Curl.new("POST", "/greet?query=test+query", post_data, {"Content-Type" => "application/json;charset=utf-8", "X-Custom-Header" => "custom header value"}))
113+
end
114+
115+
context "when post data is not json" do
116+
let(:post_data) { { :target => "nurse", :email => "[email protected]" } }
117+
let(:headers) { { "X-Custom-Header" => "custom header value" } }
118+
119+
it "should not nil out request_body" do |example|
120+
body = example.metadata[:requests].first[:request_body]
121+
expect(body).to match(/target=nurse/)
122+
expect(body).to match(/email=email%40example\.com/)
123+
end
124+
end
125+
126+
context "when post data is nil" do
127+
let(:post_data) { }
128+
129+
it "should nil out request_body" do |example|
130+
expect(example.metadata[:requests].first[:request_body]).to be_nil
131+
end
132+
end
133+
end
134+
end
135+
end

spec/rack_test_client_spec.rb

Lines changed: 1 addition & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,7 @@
11
require 'spec_helper'
22
require 'rack/test'
33
require 'sinatra/base'
4-
5-
class StubApp < Sinatra::Base
6-
get "/" do
7-
content_type :json
8-
9-
{ :hello => "world" }.to_json
10-
end
11-
12-
post "/greet" do
13-
content_type :json
14-
15-
request.body.rewind
16-
begin
17-
data = JSON.parse request.body.read
18-
rescue JSON::ParserError
19-
request.body.rewind
20-
data = request.body.read
21-
end
22-
{ :hello => data["target"] }.to_json
23-
end
24-
25-
get "/xml" do
26-
content_type :xml
27-
28-
"<hello>World</hello>"
29-
end
30-
end
4+
require 'support/stub_app'
315

326
describe RspecApiDocumentation::RackTestClient do
337
let(:context) { |example| double(:app => StubApp, :example => example) }

0 commit comments

Comments
 (0)