webmock 1.8.6 → 3.14.0
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 +7 -0
- data/.github/workflows/CI.yml +37 -0
- data/.gitignore +6 -0
- data/CHANGELOG.md +1198 -0
- data/Gemfile +3 -15
- data/README.md +761 -305
- data/Rakefile +13 -40
- data/lib/webmock/api.rb +63 -17
- data/lib/webmock/callback_registry.rb +1 -1
- data/lib/webmock/config.rb +8 -0
- data/lib/webmock/cucumber.rb +2 -0
- data/lib/webmock/errors.rb +8 -24
- data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
- data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
- data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
- data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
- data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
- data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
- data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
- data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
- data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
- data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
- data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
- data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
- data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
- data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
- data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
- data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
- data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
- data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
- data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
- data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
- data/lib/webmock/minitest.rb +29 -3
- data/lib/webmock/rack_response.rb +14 -7
- data/lib/webmock/request_body_diff.rb +64 -0
- data/lib/webmock/request_execution_verifier.rb +38 -17
- data/lib/webmock/request_pattern.rb +158 -38
- data/lib/webmock/request_registry.rb +3 -3
- data/lib/webmock/request_signature.rb +7 -3
- data/lib/webmock/request_signature_snippet.rb +61 -0
- data/lib/webmock/request_stub.rb +9 -6
- data/lib/webmock/response.rb +30 -15
- data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
- data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
- data/lib/webmock/rspec/matchers.rb +0 -1
- data/lib/webmock/rspec.rb +11 -2
- data/lib/webmock/stub_registry.rb +31 -10
- data/lib/webmock/stub_request_snippet.rb +14 -6
- data/lib/webmock/test_unit.rb +4 -4
- data/lib/webmock/util/hash_counter.rb +20 -6
- data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
- data/lib/webmock/util/hash_validator.rb +17 -0
- data/lib/webmock/util/headers.rb +23 -2
- data/lib/webmock/util/json.rb +20 -7
- data/lib/webmock/util/query_mapper.rb +281 -0
- data/lib/webmock/util/uri.rb +29 -19
- data/lib/webmock/util/values_stringifier.rb +20 -0
- data/lib/webmock/util/version_checker.rb +40 -2
- data/lib/webmock/version.rb +1 -1
- data/lib/webmock/webmock.rb +56 -17
- data/lib/webmock.rb +56 -46
- data/minitest/test_helper.rb +8 -3
- data/minitest/test_webmock.rb +4 -1
- data/minitest/webmock_spec.rb +16 -6
- data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
- data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
- data/spec/acceptance/curb/curb_spec.rb +227 -68
- data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
- data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
- data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
- data/spec/acceptance/excon/excon_spec.rb +66 -4
- data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
- data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
- data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
- data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
- data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
- data/spec/acceptance/manticore/manticore_spec.rb +107 -0
- data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
- data/spec/acceptance/net_http/net_http_shared.rb +52 -24
- data/spec/acceptance/net_http/net_http_spec.rb +164 -50
- data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
- data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
- data/spec/acceptance/patron/patron_spec.rb +29 -40
- data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
- data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
- data/spec/acceptance/shared/callbacks.rb +32 -30
- data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
- data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
- data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
- data/spec/acceptance/shared/request_expectations.rb +560 -296
- data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
- data/spec/acceptance/shared/stubbing_requests.rb +385 -154
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
- data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
- data/spec/acceptance/webmock_shared.rb +2 -2
- data/spec/fixtures/test.txt +1 -0
- data/spec/quality_spec.rb +27 -3
- data/spec/spec_helper.rb +11 -20
- data/spec/support/failures.rb +9 -0
- data/spec/support/my_rack_app.rb +8 -3
- data/spec/support/network_connection.rb +7 -13
- data/spec/support/webmock_server.rb +8 -3
- data/spec/unit/api_spec.rb +175 -0
- data/spec/unit/errors_spec.rb +116 -19
- data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
- data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
- data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
- data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
- data/spec/unit/rack_response_spec.rb +54 -16
- data/spec/unit/request_body_diff_spec.rb +90 -0
- data/spec/unit/request_execution_verifier_spec.rb +147 -39
- data/spec/unit/request_pattern_spec.rb +462 -198
- data/spec/unit/request_registry_spec.rb +29 -9
- data/spec/unit/request_signature_snippet_spec.rb +89 -0
- data/spec/unit/request_signature_spec.rb +91 -49
- data/spec/unit/request_stub_spec.rb +71 -70
- data/spec/unit/response_spec.rb +100 -81
- data/spec/unit/stub_registry_spec.rb +37 -20
- data/spec/unit/stub_request_snippet_spec.rb +51 -31
- data/spec/unit/util/hash_counter_spec.rb +6 -6
- data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
- data/spec/unit/util/headers_spec.rb +4 -4
- data/spec/unit/util/json_spec.rb +29 -3
- data/spec/unit/util/query_mapper_spec.rb +157 -0
- data/spec/unit/util/uri_spec.rb +150 -36
- data/spec/unit/util/version_checker_spec.rb +15 -9
- data/spec/unit/webmock_spec.rb +57 -4
- data/test/http_request.rb +3 -3
- data/test/shared_test.rb +45 -13
- data/test/test_helper.rb +1 -1
- data/test/test_webmock.rb +6 -0
- data/webmock.gemspec +30 -11
- metadata +308 -199
- data/.rvmrc +0 -1
- data/.travis.yml +0 -11
- data/Guardfile +0 -24
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
- data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb +0 -210
@@ -6,13 +6,13 @@ module WebMock
|
|
6
6
|
attr_reader :headers
|
7
7
|
|
8
8
|
def initialize(method, uri, options = {})
|
9
|
-
self.method = method
|
9
|
+
self.method = method.to_sym
|
10
10
|
self.uri = uri.is_a?(Addressable::URI) ? uri : WebMock::Util::URI.normalize_uri(uri)
|
11
11
|
assign_options(options)
|
12
12
|
end
|
13
13
|
|
14
14
|
def to_s
|
15
|
-
string = "#{self.method.to_s.upcase}"
|
15
|
+
string = "#{self.method.to_s.upcase}".dup
|
16
16
|
string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
|
17
17
|
string << " with body '#{body.to_s}'" if body && body.to_s != ''
|
18
18
|
if headers && !headers.empty?
|
@@ -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
|
+
"You can stub this request with the following snippet:\n\n" +
|
17
|
+
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".dup
|
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("".dup) do |stream|
|
54
|
+
PP.pp(string_to_print, stream)
|
55
|
+
stream.rewind
|
56
|
+
stream.read
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/webmock/request_stub.rb
CHANGED
@@ -22,6 +22,7 @@ module WebMock
|
|
22
22
|
end
|
23
23
|
self
|
24
24
|
end
|
25
|
+
alias_method :and_return, :to_return
|
25
26
|
|
26
27
|
def to_rack(app, options={})
|
27
28
|
@responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
|
@@ -29,15 +30,17 @@ module WebMock
|
|
29
30
|
|
30
31
|
def to_raise(*exceptions)
|
31
32
|
@responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
|
32
|
-
ResponseFactory.response_for(:
|
33
|
+
ResponseFactory.response_for(exception: e)
|
33
34
|
})
|
34
35
|
self
|
35
36
|
end
|
37
|
+
alias_method :and_raise, :to_raise
|
36
38
|
|
37
39
|
def to_timeout
|
38
|
-
@responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(:
|
40
|
+
@responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(should_timeout: true)])
|
39
41
|
self
|
40
42
|
end
|
43
|
+
alias_method :and_timeout, :to_timeout
|
41
44
|
|
42
45
|
def response
|
43
46
|
if @responses_sequences.empty?
|
@@ -59,7 +62,7 @@ module WebMock
|
|
59
62
|
end
|
60
63
|
|
61
64
|
def times(number)
|
62
|
-
raise "times(N) accepts integers >= 1 only" if !number.is_a?(
|
65
|
+
raise "times(N) accepts integers >= 1 only" if !number.is_a?(Integer) || number < 1
|
63
66
|
if @responses_sequences.empty?
|
64
67
|
raise "Invalid WebMock stub declaration." +
|
65
68
|
" times(N) can be declared only after response declaration."
|
@@ -81,15 +84,15 @@ module WebMock
|
|
81
84
|
|
82
85
|
if signature.body.to_s != ''
|
83
86
|
body = if signature.url_encoded?
|
84
|
-
|
87
|
+
WebMock::Util::QueryMapper.query_to_values(signature.body, notation: Config.instance.query_values_notation)
|
85
88
|
else
|
86
89
|
signature.body
|
87
90
|
end
|
88
|
-
stub.with(:
|
91
|
+
stub.with(body: body)
|
89
92
|
end
|
90
93
|
|
91
94
|
if (signature.headers && !signature.headers.empty?)
|
92
|
-
stub.with(:
|
95
|
+
stub.with(headers: signature.headers)
|
93
96
|
end
|
94
97
|
stub
|
95
98
|
end
|
data/lib/webmock/response.rb
CHANGED
@@ -1,7 +1,4 @@
|
|
1
|
-
|
2
|
-
class StringIO
|
3
|
-
alias_method :read_nonblock, :sysread
|
4
|
-
end
|
1
|
+
require "pathname"
|
5
2
|
|
6
3
|
module WebMock
|
7
4
|
|
@@ -41,6 +38,7 @@ module WebMock
|
|
41
38
|
|
42
39
|
def body=(body)
|
43
40
|
@body = body
|
41
|
+
assert_valid_body!
|
44
42
|
stringify_body!
|
45
43
|
end
|
46
44
|
|
@@ -73,11 +71,13 @@ module WebMock
|
|
73
71
|
end
|
74
72
|
|
75
73
|
def options=(options)
|
76
|
-
|
77
|
-
|
78
|
-
self.
|
79
|
-
self.
|
80
|
-
|
74
|
+
options = WebMock::Util::HashKeysStringifier.stringify_keys!(options)
|
75
|
+
HashValidator.new(options).validate_keys('headers', 'status', 'body', 'exception', 'should_timeout')
|
76
|
+
self.headers = options['headers']
|
77
|
+
self.status = options['status']
|
78
|
+
self.body = options['body']
|
79
|
+
self.exception = options['exception']
|
80
|
+
@should_timeout = options['should_timeout']
|
81
81
|
end
|
82
82
|
|
83
83
|
def evaluate(request_signature)
|
@@ -91,19 +91,32 @@ module WebMock
|
|
91
91
|
|
92
92
|
def ==(other)
|
93
93
|
self.body == other.body &&
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
94
|
+
self.headers === other.headers &&
|
95
|
+
self.status == other.status &&
|
96
|
+
self.exception == other.exception &&
|
97
|
+
self.should_timeout == other.should_timeout
|
98
98
|
end
|
99
99
|
|
100
100
|
private
|
101
101
|
|
102
102
|
def stringify_body!
|
103
|
-
if @body.is_a?(IO)
|
103
|
+
if @body.is_a?(IO) || @body.is_a?(Pathname)
|
104
104
|
io = @body
|
105
105
|
@body = io.read
|
106
|
-
io.close
|
106
|
+
io.close if io.respond_to?(:close)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def assert_valid_body!
|
111
|
+
valid_types = [Proc, IO, Pathname, String, Array]
|
112
|
+
return if @body.nil?
|
113
|
+
return if valid_types.any? { |c| @body.is_a?(c) }
|
114
|
+
|
115
|
+
if @body.class.is_a?(Hash)
|
116
|
+
raise InvalidBody, "must be one of: #{valid_types}, but you've used a #{@body.class}' instead." \
|
117
|
+
"\n What shall we encode it to? try calling .to_json .to_xml instead on the hash instead, or otherwise convert it to a string."
|
118
|
+
else
|
119
|
+
raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
|
107
120
|
end
|
108
121
|
end
|
109
122
|
|
@@ -127,6 +140,8 @@ module WebMock
|
|
127
140
|
options
|
128
141
|
end
|
129
142
|
|
143
|
+
InvalidBody = Class.new(StandardError)
|
144
|
+
|
130
145
|
end
|
131
146
|
|
132
147
|
class DynamicResponse < Response
|
@@ -20,6 +20,36 @@ module WebMock
|
|
20
20
|
self
|
21
21
|
end
|
22
22
|
|
23
|
+
def at_least_once
|
24
|
+
@request_execution_verifier.at_least_times_executed = 1
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def at_least_twice
|
29
|
+
@request_execution_verifier.at_least_times_executed = 2
|
30
|
+
self
|
31
|
+
end
|
32
|
+
|
33
|
+
def at_least_times(times)
|
34
|
+
@request_execution_verifier.at_least_times_executed = times.to_i
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
def at_most_once
|
39
|
+
@request_execution_verifier.at_most_times_executed = 1
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def at_most_twice
|
44
|
+
@request_execution_verifier.at_most_times_executed = 2
|
45
|
+
self
|
46
|
+
end
|
47
|
+
|
48
|
+
def at_most_times(times)
|
49
|
+
@request_execution_verifier.at_most_times_executed = times.to_i
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
23
53
|
def matches?(request_pattern)
|
24
54
|
@request_execution_verifier.request_pattern = request_pattern
|
25
55
|
@request_execution_verifier.matches?
|
@@ -34,9 +64,15 @@ module WebMock
|
|
34
64
|
@request_execution_verifier.failure_message
|
35
65
|
end
|
36
66
|
|
67
|
+
def failure_message_when_negated
|
68
|
+
@request_execution_verifier.failure_message_when_negated
|
69
|
+
end
|
37
70
|
|
38
|
-
def
|
39
|
-
@request_execution_verifier.
|
71
|
+
def description
|
72
|
+
@request_execution_verifier.description
|
40
73
|
end
|
74
|
+
|
75
|
+
# RSpec 2 compatibility:
|
76
|
+
alias_method :negative_failure_message, :failure_message_when_negated
|
41
77
|
end
|
42
78
|
end
|
@@ -16,6 +16,21 @@ module WebMock
|
|
16
16
|
self
|
17
17
|
end
|
18
18
|
|
19
|
+
def at_least_once
|
20
|
+
@request_execution_verifier.at_least_times_executed = 1
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def at_least_twice
|
25
|
+
@request_execution_verifier.at_least_times_executed = 2
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def at_least_times(times)
|
30
|
+
@request_execution_verifier.at_least_times_executed = times
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
19
34
|
def with(options = {}, &block)
|
20
35
|
@request_execution_verifier.request_pattern.with(options, &block)
|
21
36
|
self
|
@@ -38,9 +53,15 @@ module WebMock
|
|
38
53
|
@request_execution_verifier.failure_message
|
39
54
|
end
|
40
55
|
|
56
|
+
def failure_message_when_negated
|
57
|
+
@request_execution_verifier.failure_message_when_negated
|
58
|
+
end
|
41
59
|
|
42
|
-
def
|
43
|
-
@request_execution_verifier.
|
60
|
+
def description
|
61
|
+
@request_execution_verifier.description
|
44
62
|
end
|
63
|
+
|
64
|
+
# RSpec 2 compatibility:
|
65
|
+
alias_method :negative_failure_message, :failure_message_when_negated
|
45
66
|
end
|
46
67
|
end
|
data/lib/webmock/rspec.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'webmock'
|
2
2
|
|
3
3
|
# RSpec 1.x and 2.x compatibility
|
4
|
-
if defined?(RSpec
|
4
|
+
if defined?(RSpec::Expectations::ExpectationNotMetError)
|
5
5
|
RSPEC_NAMESPACE = RSPEC_CONFIGURER = RSpec
|
6
6
|
elsif defined?(Spec) && defined?(Spec.configure)
|
7
7
|
RSPEC_NAMESPACE = Spec
|
@@ -25,7 +25,16 @@ RSPEC_CONFIGURER.configure { |config|
|
|
25
25
|
config.include WebMock::API
|
26
26
|
config.include WebMock::Matchers
|
27
27
|
|
28
|
-
config.
|
28
|
+
config.before(:suite) do
|
29
|
+
WebMock.enable!
|
30
|
+
end
|
31
|
+
|
32
|
+
config.after(:suite) do
|
33
|
+
WebMock.disable!
|
34
|
+
end
|
35
|
+
|
36
|
+
config.around(:each) do |example|
|
37
|
+
example.run
|
29
38
|
WebMock.reset!
|
30
39
|
end
|
31
40
|
}
|
@@ -10,25 +10,39 @@ module WebMock
|
|
10
10
|
end
|
11
11
|
|
12
12
|
def global_stubs
|
13
|
-
@global_stubs ||= []
|
13
|
+
@global_stubs ||= Hash.new { |h, k| h[k] = [] }
|
14
14
|
end
|
15
15
|
|
16
16
|
def reset!
|
17
17
|
self.request_stubs = []
|
18
18
|
end
|
19
19
|
|
20
|
-
def register_global_stub(&block)
|
20
|
+
def register_global_stub(order = :before_local_stubs, &block)
|
21
|
+
unless %i[before_local_stubs after_local_stubs].include?(order)
|
22
|
+
raise ArgumentError.new("Wrong order. Use :before_local_stubs or :after_local_stubs")
|
23
|
+
end
|
24
|
+
|
21
25
|
# This hash contains the responses returned by the block,
|
22
26
|
# keyed by the exact request (using the object_id).
|
23
27
|
# That way, there's no race condition in case #to_return
|
24
28
|
# doesn't run immediately after stub.with.
|
25
29
|
responses = {}
|
30
|
+
response_lock = Mutex.new
|
31
|
+
|
32
|
+
stub = ::WebMock::RequestStub.new(:any, ->(uri) { true }).with { |request|
|
33
|
+
update_response = -> { responses[request.object_id] = yield(request) }
|
26
34
|
|
27
|
-
|
28
|
-
|
29
|
-
|
35
|
+
# The block can recurse, so only lock if we don't already own it
|
36
|
+
if response_lock.owned?
|
37
|
+
update_response.call
|
38
|
+
else
|
39
|
+
response_lock.synchronize(&update_response)
|
40
|
+
end
|
41
|
+
}.to_return(lambda { |request|
|
42
|
+
response_lock.synchronize { responses.delete(request.object_id) }
|
43
|
+
})
|
30
44
|
|
31
|
-
global_stubs.push stub
|
45
|
+
global_stubs[order].push stub
|
32
46
|
end
|
33
47
|
|
34
48
|
def register_request_stub(stub)
|
@@ -36,6 +50,12 @@ module WebMock
|
|
36
50
|
stub
|
37
51
|
end
|
38
52
|
|
53
|
+
def remove_request_stub(stub)
|
54
|
+
if not request_stubs.delete(stub)
|
55
|
+
raise "Request stub \n\n #{stub.to_s} \n\n is not registered."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
39
59
|
def registered_request?(request_signature)
|
40
60
|
request_stub_for(request_signature)
|
41
61
|
end
|
@@ -48,13 +68,14 @@ module WebMock
|
|
48
68
|
private
|
49
69
|
|
50
70
|
def request_stub_for(request_signature)
|
51
|
-
(global_stubs + request_stubs
|
52
|
-
registered_request_stub
|
53
|
-
|
71
|
+
(global_stubs[:before_local_stubs] + request_stubs + global_stubs[:after_local_stubs])
|
72
|
+
.detect { |registered_request_stub|
|
73
|
+
registered_request_stub.request_pattern.matches?(request_signature)
|
74
|
+
}
|
54
75
|
end
|
55
76
|
|
56
77
|
def evaluate_response_for_request(response, request_signature)
|
57
|
-
response.evaluate(request_signature)
|
78
|
+
response.dup.evaluate(request_signature)
|
58
79
|
end
|
59
80
|
|
60
81
|
end
|
@@ -4,25 +4,33 @@ 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
|
-
string = "stub_request(:#{request_pattern.method_pattern.to_s},"
|
13
|
+
string = "stub_request(:#{request_pattern.method_pattern.to_s},".dup
|
10
14
|
string << " \"#{request_pattern.uri_pattern.to_s}\")"
|
11
15
|
|
12
|
-
with = ""
|
16
|
+
with = "".dup
|
13
17
|
|
14
18
|
if (request_pattern.body_pattern)
|
15
|
-
with << ":
|
19
|
+
with << "\n body: #{request_pattern.body_pattern.to_s}"
|
16
20
|
end
|
17
21
|
|
18
22
|
if (request_pattern.headers_pattern)
|
19
|
-
with << "
|
23
|
+
with << "," unless with.empty?
|
20
24
|
|
21
|
-
with << ":
|
25
|
+
with << "\n headers: #{request_pattern.headers_pattern.pp_to_s}"
|
22
26
|
end
|
23
27
|
string << ".\n with(#{with})" unless with.empty?
|
24
28
|
if with_response
|
25
|
-
|
29
|
+
if request_pattern.headers_pattern && request_pattern.headers_pattern.matches?({ 'Accept' => "application/json" })
|
30
|
+
string << ".\n to_return(status: 200, body: \"{}\", headers: {})"
|
31
|
+
else
|
32
|
+
string << ".\n to_return(status: 200, body: \"\", headers: {})"
|
33
|
+
end
|
26
34
|
end
|
27
35
|
string
|
28
36
|
end
|
data/lib/webmock/test_unit.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
require 'test/unit'
|
2
2
|
require 'webmock'
|
3
3
|
|
4
|
+
WebMock.enable!
|
5
|
+
|
4
6
|
module Test
|
5
7
|
module Unit
|
6
8
|
class TestCase
|
7
9
|
include WebMock::API
|
8
10
|
|
9
|
-
|
11
|
+
teardown
|
10
12
|
def teardown_with_webmock
|
11
|
-
teardown_without_webmock
|
12
13
|
WebMock.reset!
|
13
14
|
end
|
14
|
-
alias_method :teardown, :teardown_with_webmock
|
15
15
|
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
WebMock::AssertionFailure.error_class = Test::Unit::AssertionFailedError
|
20
|
+
WebMock::AssertionFailure.error_class = Test::Unit::AssertionFailedError
|
@@ -1,23 +1,37 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
module WebMock
|
2
4
|
module Util
|
3
|
-
class
|
5
|
+
class HashCounter
|
4
6
|
attr_accessor :hash
|
5
7
|
def initialize
|
6
8
|
self.hash = {}
|
7
9
|
@order = {}
|
8
10
|
@max = 0
|
11
|
+
@lock = ::Mutex.new
|
9
12
|
end
|
10
13
|
def put key, num=1
|
11
|
-
|
12
|
-
|
14
|
+
@lock.synchronize do
|
15
|
+
hash[key] = (hash[key] || 0) + num
|
16
|
+
@order[key] = @max = @max + 1
|
17
|
+
end
|
13
18
|
end
|
14
19
|
def get key
|
15
|
-
|
20
|
+
@lock.synchronize do
|
21
|
+
hash[key] || 0
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def select(&block)
|
26
|
+
return unless block_given?
|
27
|
+
@lock.synchronize do
|
28
|
+
hash.select(&block)
|
29
|
+
end
|
16
30
|
end
|
17
31
|
|
18
32
|
def each(&block)
|
19
|
-
@order.to_a.
|
20
|
-
|
33
|
+
@order.to_a.sort_by { |a| a[1] }.each do |a|
|
34
|
+
yield(a[0], hash[a[0]])
|
21
35
|
end
|
22
36
|
end
|
23
37
|
end
|
@@ -2,15 +2,17 @@ module WebMock
|
|
2
2
|
module Util
|
3
3
|
class HashKeysStringifier
|
4
4
|
|
5
|
-
def self.stringify_keys!(arg)
|
5
|
+
def self.stringify_keys!(arg, options = {})
|
6
6
|
case arg
|
7
7
|
when Array
|
8
|
-
arg.map { |elem|
|
8
|
+
arg.map { |elem|
|
9
|
+
options[:deep] ? stringify_keys!(elem, options) : elem
|
10
|
+
}
|
9
11
|
when Hash
|
10
12
|
Hash[
|
11
13
|
*arg.map { |key, value|
|
12
14
|
k = key.is_a?(Symbol) ? key.to_s : key
|
13
|
-
v = stringify_keys!(value)
|
15
|
+
v = (options[:deep] ? stringify_keys!(value, options) : value)
|
14
16
|
[k,v]
|
15
17
|
}.inject([]) {|r,x| r + x}]
|
16
18
|
else
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module WebMock
|
2
|
+
class HashValidator
|
3
|
+
def initialize(hash)
|
4
|
+
@hash = hash
|
5
|
+
end
|
6
|
+
|
7
|
+
#This code is based on https://github.com/rails/rails/blob/master/activesupport/lib/active_support/core_ext/hash/keys.rb
|
8
|
+
def validate_keys(*valid_keys)
|
9
|
+
valid_keys.flatten!
|
10
|
+
@hash.each_key do |k|
|
11
|
+
unless valid_keys.include?(k)
|
12
|
+
raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{valid_keys.map(&:inspect).join(', ')}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/webmock/util/headers.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'base64'
|
2
|
+
|
1
3
|
module WebMock
|
2
4
|
|
3
5
|
module Util
|
@@ -10,7 +12,7 @@ module WebMock
|
|
10
12
|
[name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"),
|
11
13
|
case value
|
12
14
|
when Regexp then value
|
13
|
-
when Array then (value.size == 1) ? value.first : value.map {|v| v.to_s}.sort
|
15
|
+
when Array then (value.size == 1) ? value.first.to_s : value.map {|v| v.to_s}.sort
|
14
16
|
else value.to_s
|
15
17
|
end
|
16
18
|
]
|
@@ -20,7 +22,7 @@ module WebMock
|
|
20
22
|
|
21
23
|
def self.sorted_headers_string(headers)
|
22
24
|
headers = WebMock::Util::Headers.normalize_headers(headers)
|
23
|
-
str = '{'
|
25
|
+
str = '{'.dup
|
24
26
|
str << headers.map do |k,v|
|
25
27
|
v = case v
|
26
28
|
when Regexp then v.inspect
|
@@ -32,10 +34,29 @@ module WebMock
|
|
32
34
|
str << '}'
|
33
35
|
end
|
34
36
|
|
37
|
+
def self.pp_headers_string(headers)
|
38
|
+
headers = WebMock::Util::Headers.normalize_headers(headers)
|
39
|
+
seperator = "\n\t "
|
40
|
+
str = "{#{seperator} ".dup
|
41
|
+
str << headers.map do |k,v|
|
42
|
+
v = case v
|
43
|
+
when Regexp then v.inspect
|
44
|
+
when Array then "["+v.map{|w| "'#{w.to_s}'"}.join(", ")+"]"
|
45
|
+
else "'#{v.to_s}'"
|
46
|
+
end
|
47
|
+
"'#{k}'=>#{v}"
|
48
|
+
end.sort.join(",#{seperator} ")
|
49
|
+
str << "\n }"
|
50
|
+
end
|
51
|
+
|
35
52
|
def self.decode_userinfo_from_header(header)
|
36
53
|
header.sub(/^Basic /, "").unpack("m").first
|
37
54
|
end
|
38
55
|
|
56
|
+
def self.basic_auth_header(*credentials)
|
57
|
+
"Basic #{Base64.strict_encode64(credentials.join(':')).chomp}"
|
58
|
+
end
|
59
|
+
|
39
60
|
end
|
40
61
|
|
41
62
|
end
|