webmock 1.21.0 → 1.22.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.
- data/.travis.yml +8 -0
- data/CHANGELOG.md +51 -1
- data/Gemfile +0 -1
- data/README.md +17 -6
- data/lib/webmock.rb +4 -1
- data/lib/webmock/config.rb +2 -0
- data/lib/webmock/errors.rb +3 -21
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +5 -0
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +123 -0
- data/lib/webmock/http_lib_adapters/net_http.rb +16 -2
- data/lib/webmock/request_body_diff.rb +63 -0
- data/lib/webmock/request_execution_verifier.rb +24 -21
- data/lib/webmock/request_pattern.rb +2 -0
- data/lib/webmock/request_signature.rb +5 -1
- data/lib/webmock/request_signature_snippet.rb +61 -0
- data/lib/webmock/rspec/matchers.rb +0 -1
- data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +4 -0
- data/lib/webmock/rspec/matchers/webmock_matcher.rb +4 -0
- data/lib/webmock/stub_request_snippet.rb +4 -0
- data/lib/webmock/util/json.rb +25 -6
- data/lib/webmock/util/query_mapper.rb +6 -4
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +12 -0
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +60 -0
- data/spec/acceptance/http_rb/http_rb_spec.rb +2 -2
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +1 -1
- data/spec/acceptance/httpclient/httpclient_spec.rb +8 -7
- data/spec/acceptance/manticore/manticore_spec.rb +56 -0
- data/spec/acceptance/manticore/manticore_spec_helper.rb +31 -0
- data/spec/acceptance/net_http/net_http_spec.rb +24 -1
- data/spec/acceptance/shared/request_expectations.rb +10 -10
- data/spec/spec_helper.rb +4 -0
- data/spec/unit/errors_spec.rb +45 -4
- data/spec/unit/request_body_diff_spec.rb +90 -0
- data/spec/unit/request_execution_verifier_spec.rb +48 -11
- data/spec/unit/request_signature_snippet_spec.rb +89 -0
- data/spec/unit/request_signature_spec.rb +61 -23
- data/spec/unit/stub_registry_spec.rb +1 -1
- data/spec/unit/util/json_spec.rb +29 -3
- data/spec/unit/util/query_mapper_spec.rb +15 -4
- data/spec/unit/webmock_spec.rb +4 -0
- data/test/shared_test.rb +2 -2
- data/webmock.gemspec +4 -1
- metadata +63 -24
@@ -33,32 +33,16 @@ module WebMock
|
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
36
|
+
def description
|
37
|
+
"request #{request_pattern.to_s} #{quantity_phrase.strip}"
|
38
|
+
end
|
36
39
|
|
37
40
|
def failure_message
|
38
|
-
|
39
|
-
text = if @at_least_times_executed
|
40
|
-
%Q(The request #{request_pattern.to_s} was expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
|
41
|
-
elsif @at_most_times_executed
|
42
|
-
%Q(The request #{request_pattern.to_s} was expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
|
43
|
-
else
|
44
|
-
%Q(The request #{request_pattern.to_s} was expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
|
45
|
-
end
|
46
|
-
text << self.class.executed_requests_message
|
47
|
-
text
|
41
|
+
failure_message_phrase(is_negated=false)
|
48
42
|
end
|
49
43
|
|
50
44
|
def failure_message_when_negated
|
51
|
-
|
52
|
-
%Q(The request #{request_pattern.to_s} was not expected to execute at least #{times(@at_least_times_executed)} but it executed #{times(times_executed)})
|
53
|
-
elsif @at_most_times_executed
|
54
|
-
%Q(The request #{request_pattern.to_s} was not expected to execute at most #{times(@at_most_times_executed)} but it executed #{times(times_executed)})
|
55
|
-
elsif @expected_times_executed
|
56
|
-
%Q(The request #{request_pattern.to_s} was not expected to execute #{times(expected_times_executed)} but it executed #{times(times_executed)})
|
57
|
-
else
|
58
|
-
%Q(The request #{request_pattern.to_s} was expected to execute 0 times but it executed #{times(times_executed)})
|
59
|
-
end
|
60
|
-
text << self.class.executed_requests_message
|
61
|
-
text
|
45
|
+
failure_message_phrase(is_negated=true)
|
62
46
|
end
|
63
47
|
|
64
48
|
def self.executed_requests_message
|
@@ -67,6 +51,25 @@ module WebMock
|
|
67
51
|
|
68
52
|
private
|
69
53
|
|
54
|
+
def failure_message_phrase(is_negated=false)
|
55
|
+
negation = is_negated ? "was not" : "was"
|
56
|
+
text = "The request #{request_pattern.to_s} #{negation} expected to execute #{quantity_phrase(is_negated)}but it executed #{times(times_executed)}"
|
57
|
+
text << self.class.executed_requests_message
|
58
|
+
text
|
59
|
+
end
|
60
|
+
|
61
|
+
def quantity_phrase(is_negated=false)
|
62
|
+
if @at_least_times_executed
|
63
|
+
"at least #{times(@at_least_times_executed)} "
|
64
|
+
elsif @at_most_times_executed
|
65
|
+
"at most #{times(@at_most_times_executed)} "
|
66
|
+
elsif @expected_times_executed
|
67
|
+
"#{times(@expected_times_executed)} "
|
68
|
+
else
|
69
|
+
is_negated ? "" : "#{times(1)} "
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
70
73
|
def times(times)
|
71
74
|
"#{times} time#{ (times == 1) ? '' : 's'}"
|
72
75
|
end
|
@@ -35,7 +35,11 @@ module WebMock
|
|
35
35
|
alias == eql?
|
36
36
|
|
37
37
|
def url_encoded?
|
38
|
-
headers && headers['Content-Type'] == 'application/x-www-form-urlencoded'
|
38
|
+
!!(headers && headers['Content-Type'] == 'application/x-www-form-urlencoded')
|
39
|
+
end
|
40
|
+
|
41
|
+
def json_headers?
|
42
|
+
!!(headers && headers['Content-Type'] == 'application/json')
|
39
43
|
end
|
40
44
|
|
41
45
|
private
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require "pp"
|
2
|
+
|
3
|
+
module WebMock
|
4
|
+
class RequestSignatureSnippet
|
5
|
+
|
6
|
+
attr_reader :request_signature, :request_stub
|
7
|
+
|
8
|
+
def initialize(request_signature)
|
9
|
+
@request_signature = request_signature
|
10
|
+
@request_stub = RequestStub.from_request_signature(request_signature)
|
11
|
+
end
|
12
|
+
|
13
|
+
def stubbing_instructions
|
14
|
+
return unless WebMock.show_stubbing_instructions?
|
15
|
+
|
16
|
+
text = "You can stub this request with the following snippet:\n\n"
|
17
|
+
text << WebMock::StubRequestSnippet.new(request_stub).to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def request_stubs
|
21
|
+
return if WebMock::StubRegistry.instance.request_stubs.empty?
|
22
|
+
|
23
|
+
text = "registered request stubs:\n"
|
24
|
+
WebMock::StubRegistry.instance.request_stubs.each do |stub|
|
25
|
+
text << "\n#{WebMock::StubRequestSnippet.new(stub).to_s(false)}"
|
26
|
+
add_body_diff(stub, text) if WebMock.show_body_diff?
|
27
|
+
end
|
28
|
+
text
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def add_body_diff(stub, text)
|
34
|
+
body_diff_str = signature_stub_body_diff(stub)
|
35
|
+
text << "\n\n#{body_diff_str}" unless body_diff_str.empty?
|
36
|
+
end
|
37
|
+
|
38
|
+
def signature_stub_body_diff(stub)
|
39
|
+
diff = RequestBodyDiff.new(request_signature, stub).body_diff
|
40
|
+
diff.empty? ? "" : "Body diff:\n #{pretty_print_to_string(diff)}"
|
41
|
+
end
|
42
|
+
|
43
|
+
def request_params
|
44
|
+
@request_params ||=
|
45
|
+
if request_signature.json_headers?
|
46
|
+
JSON.parse(request_signature.body)
|
47
|
+
else
|
48
|
+
""
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def pretty_print_to_string(string_to_print)
|
53
|
+
StringIO.open("") do |stream|
|
54
|
+
PP.pp(string_to_print, stream)
|
55
|
+
stream.rewind
|
56
|
+
stream.read
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -68,6 +68,10 @@ module WebMock
|
|
68
68
|
@request_execution_verifier.failure_message_when_negated
|
69
69
|
end
|
70
70
|
|
71
|
+
def description
|
72
|
+
@request_execution_verifier.description
|
73
|
+
end
|
74
|
+
|
71
75
|
# RSpec 2 compatibility:
|
72
76
|
alias_method :negative_failure_message, :failure_message_when_negated
|
73
77
|
end
|
@@ -42,6 +42,10 @@ module WebMock
|
|
42
42
|
@request_execution_verifier.failure_message_when_negated
|
43
43
|
end
|
44
44
|
|
45
|
+
def description
|
46
|
+
@request_execution_verifier.description
|
47
|
+
end
|
48
|
+
|
45
49
|
# RSpec 2 compatibility:
|
46
50
|
alias_method :negative_failure_message, :failure_message_when_negated
|
47
51
|
end
|
@@ -4,6 +4,10 @@ module WebMock
|
|
4
4
|
@request_stub = request_stub
|
5
5
|
end
|
6
6
|
|
7
|
+
def body_pattern
|
8
|
+
request_pattern.body_pattern
|
9
|
+
end
|
10
|
+
|
7
11
|
def to_s(with_response = true)
|
8
12
|
request_pattern = @request_stub.request_pattern
|
9
13
|
string = "stub_request(:#{request_pattern.method_pattern.to_s},"
|
data/lib/webmock/util/json.rb
CHANGED
@@ -8,10 +8,13 @@
|
|
8
8
|
module WebMock
|
9
9
|
module Util
|
10
10
|
class JSON
|
11
|
+
class ParseError < StandardError; end
|
12
|
+
|
11
13
|
def self.parse(json)
|
12
|
-
|
13
|
-
|
14
|
-
|
14
|
+
yaml = unescape(convert_json_to_yaml(json))
|
15
|
+
YAML.load(yaml)
|
16
|
+
rescue ArgumentError => e
|
17
|
+
raise ParseError, "Invalid JSON string: #{yaml}, Error: #{e.inspect}"
|
15
18
|
end
|
16
19
|
|
17
20
|
protected
|
@@ -42,11 +45,27 @@ module WebMock
|
|
42
45
|
json.gsub(/\\\//, '/')
|
43
46
|
else
|
44
47
|
left_pos = [-1].push(*marks)
|
45
|
-
right_pos = marks << json.
|
48
|
+
right_pos = marks << json.bytesize
|
46
49
|
output = []
|
47
|
-
|
48
|
-
|
50
|
+
|
51
|
+
if RUBY_VERSION != "1.9.2"
|
52
|
+
left_pos.each_with_index do |left, i|
|
53
|
+
if json.respond_to?(:byteslice)
|
54
|
+
output << json.byteslice(left.succ..right_pos[i])
|
55
|
+
else
|
56
|
+
output << json[left.succ..right_pos[i]]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
else
|
60
|
+
json_as_binary = json.force_encoding("binary")
|
61
|
+
left_pos.each_with_index do |left, i|
|
62
|
+
output << json_as_binary[left.succ..right_pos[i]]
|
63
|
+
end
|
64
|
+
output.map! do |binary_str|
|
65
|
+
binary_str.force_encoding("UTF-8")
|
66
|
+
end
|
49
67
|
end
|
68
|
+
|
50
69
|
output = output * " "
|
51
70
|
|
52
71
|
times.each { |i| output[i-1] = ' ' }
|
@@ -196,7 +196,11 @@ module WebMock::Util
|
|
196
196
|
end
|
197
197
|
# Useful default for OAuth and caching.
|
198
198
|
# Only to be used for non-Array inputs. Arrays should preserve order.
|
199
|
-
|
199
|
+
begin
|
200
|
+
new_query_values.sort! # may raise for non-comparable values
|
201
|
+
rescue NoMethodError, ArgumentError
|
202
|
+
# ignore
|
203
|
+
end
|
200
204
|
end
|
201
205
|
|
202
206
|
buffer = ''
|
@@ -241,7 +245,7 @@ module WebMock::Util
|
|
241
245
|
when ::Hash
|
242
246
|
value = value.map do |key, val|
|
243
247
|
[
|
244
|
-
::Addressable::URI.encode_component(key.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
|
248
|
+
::Addressable::URI.encode_component(key.to_s.dup, ::Addressable::URI::CharacterClasses::UNRESERVED),
|
245
249
|
val
|
246
250
|
]
|
247
251
|
end
|
@@ -259,8 +263,6 @@ module WebMock::Util
|
|
259
263
|
buffer << "#{to_query(new_parent, val, options)}&"
|
260
264
|
end
|
261
265
|
buffer.chop
|
262
|
-
when TrueClass
|
263
|
-
parent
|
264
266
|
else
|
265
267
|
encoded_value = Addressable::URI.encode_component(
|
266
268
|
value.to_s.dup, Addressable::URI::CharacterClasses::UNRESERVED
|
data/lib/webmock/version.rb
CHANGED
data/lib/webmock/webmock.rb
CHANGED
@@ -75,6 +75,18 @@ module WebMock
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
def self.show_body_diff!
|
79
|
+
Config.instance.show_body_diff = true
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.hide_body_diff!
|
83
|
+
Config.instance.show_body_diff = false
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.show_body_diff?
|
87
|
+
Config.instance.show_body_diff
|
88
|
+
end
|
89
|
+
|
78
90
|
def self.hide_stubbing_instructions!
|
79
91
|
Config.instance.show_stubbing_instructions = false
|
80
92
|
end
|
@@ -229,6 +229,66 @@ unless RUBY_PLATFORM =~ /java/
|
|
229
229
|
expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize)
|
230
230
|
end
|
231
231
|
|
232
|
+
it "should work with multiple requests to the same connection" do
|
233
|
+
stub_request(:get, "www.example.com/foo").to_return(:body => "bar")
|
234
|
+
stub_request(:get, "www.example.com/baz").to_return(:body => "wombat")
|
235
|
+
err1 = nil
|
236
|
+
err2 = nil
|
237
|
+
body1 = nil
|
238
|
+
body2 = nil
|
239
|
+
i = 0
|
240
|
+
|
241
|
+
EM.run do
|
242
|
+
conn = EM::HttpRequest.new("http://www.example.com")
|
243
|
+
conn.get(:path => "/foo").callback do |resp|
|
244
|
+
body1 = resp.response
|
245
|
+
i += 1; EM.stop if i == 2
|
246
|
+
end.errback do |resp|
|
247
|
+
err1 = resp.error
|
248
|
+
i += 1; EM.stop if i == 2
|
249
|
+
end
|
250
|
+
|
251
|
+
conn.get(:path => "/baz").callback do |resp|
|
252
|
+
body2 = resp.response
|
253
|
+
i += 1; EM.stop if i == 2
|
254
|
+
end.errback do |resp|
|
255
|
+
err2 = resp.error
|
256
|
+
i += 1; EM.stop if i == 2
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
expect(err1).to be(nil)
|
261
|
+
expect(err2).to be(nil)
|
262
|
+
expect(body1).to eq("bar")
|
263
|
+
expect(body2).to eq("wombat")
|
264
|
+
end
|
265
|
+
|
266
|
+
it "should work with multiple requests to the same connection when the first request times out" do
|
267
|
+
stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(:status => 200, :body => "wombat")
|
268
|
+
err = nil
|
269
|
+
body = nil
|
270
|
+
|
271
|
+
EM.run do
|
272
|
+
conn = EM::HttpRequest.new("http://www.example.com")
|
273
|
+
conn.get(:path => "/foo").callback do |resp|
|
274
|
+
err = :success_from_timeout
|
275
|
+
EM.stop
|
276
|
+
end.errback do |resp|
|
277
|
+
conn.get(:path => "/foo").callback do |resp|
|
278
|
+
expect(resp.response_header.status).to eq(200)
|
279
|
+
body = resp.response
|
280
|
+
EM.stop
|
281
|
+
end.errback do |resp|
|
282
|
+
err = resp.error
|
283
|
+
EM.stop
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
expect(err).to be(nil)
|
289
|
+
expect(body).to eq("wombat")
|
290
|
+
end
|
291
|
+
|
232
292
|
describe "mocking EM::HttpClient API" do
|
233
293
|
let(:uri) { "http://www.example.com/" }
|
234
294
|
|
@@ -49,11 +49,11 @@ describe "HTTP.rb" do
|
|
49
49
|
end
|
50
50
|
|
51
51
|
it "restores request uri on replayed response object" do
|
52
|
-
uri = URI "http://example.com/foo"
|
52
|
+
uri = Addressable::URI.parse "http://example.com/foo"
|
53
53
|
|
54
54
|
stub_request :get, "example.com/foo"
|
55
55
|
response = HTTP.get uri
|
56
56
|
|
57
|
-
expect(response.uri).to eq uri
|
57
|
+
expect(response.uri.to_s).to eq uri.to_s
|
58
58
|
end
|
59
59
|
end
|
@@ -113,13 +113,14 @@ describe "HTTPClient" do
|
|
113
113
|
nil # to let the request be made for real
|
114
114
|
end
|
115
115
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
116
|
+
http_request(:get, webmock_server_url, :client => client, :headers => { "Cookie" => "bar=; foo=" })
|
117
|
+
|
118
|
+
if defined? HTTP::CookieJar
|
119
|
+
http_request(:get, webmock_server_url, :client => client, :headers => { "Cookie" => "bar=; foo=" })
|
120
|
+
else
|
121
|
+
# If http-cookie is not present, then the cookie headers will saved between requests
|
122
|
+
http_request(:get, webmock_server_url, :client => client)
|
123
|
+
end
|
123
124
|
|
124
125
|
expect(request_signatures.size).to eq(2)
|
125
126
|
# Verify the request signatures were identical as needed by this example
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'acceptance/webmock_shared'
|
3
|
+
|
4
|
+
if RUBY_PLATFORM =~ /java/
|
5
|
+
require 'acceptance/manticore/manticore_spec_helper'
|
6
|
+
|
7
|
+
describe "Manticore" do
|
8
|
+
include ManticoreSpecHelper
|
9
|
+
|
10
|
+
include_context "with WebMock", :no_status_message
|
11
|
+
|
12
|
+
context "calling http methods on Manticore directly using Manticore's facade" do
|
13
|
+
it "handles GET" do
|
14
|
+
stub_request(:get, "http://example-foo.com").to_return(:status => 301)
|
15
|
+
response = Manticore.get("http://example-foo.com")
|
16
|
+
expect(response.code).to eq(301)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "handles POST" do
|
20
|
+
stub_request(:post, "http://example-foo.com").to_return(:status => 201)
|
21
|
+
response = Manticore.post("http://example-foo.com", {:hello => "world"})
|
22
|
+
expect(response.code).to eq(201)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "handles PUT" do
|
26
|
+
stub_request(:put, "http://example-foo.com").to_return(:status => 409)
|
27
|
+
response = Manticore.put("http://example-foo.com", {:hello => "world"})
|
28
|
+
expect(response.code).to eq(409)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "handles PATCH" do
|
32
|
+
stub_request(:patch, "http://example-foo.com").to_return(:status => 409)
|
33
|
+
response = Manticore.patch("http://example-foo.com", {:hello => "world"})
|
34
|
+
expect(response.code).to eq(409)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "handles DELETE" do
|
38
|
+
stub_request(:delete, "http://example-foo.com").to_return(:status => 204)
|
39
|
+
response = Manticore.delete("http://example-foo.com", {:id => 1})
|
40
|
+
expect(response.code).to eq(204)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "handles OPTIONS" do
|
44
|
+
stub_request(:options, "http://example-foo.com").to_return(:status => 200)
|
45
|
+
response = Manticore.options("http://example-foo.com")
|
46
|
+
expect(response.code).to eq(200)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "handles HEAD" do
|
50
|
+
stub_request(:head, "http://example-foo.com").to_return(:status => 204)
|
51
|
+
response = Manticore.head("http://example-foo.com")
|
52
|
+
expect(response.code).to eq(204)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|