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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/CI.yml +37 -0
  3. data/.gitignore +6 -0
  4. data/CHANGELOG.md +1198 -0
  5. data/Gemfile +3 -15
  6. data/README.md +761 -305
  7. data/Rakefile +13 -40
  8. data/lib/webmock/api.rb +63 -17
  9. data/lib/webmock/callback_registry.rb +1 -1
  10. data/lib/webmock/config.rb +8 -0
  11. data/lib/webmock/cucumber.rb +2 -0
  12. data/lib/webmock/errors.rb +8 -24
  13. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +216 -0
  14. data/lib/webmock/http_lib_adapters/curb_adapter.rb +148 -84
  15. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +224 -4
  16. data/lib/webmock/http_lib_adapters/excon_adapter.rb +104 -34
  17. data/lib/webmock/http_lib_adapters/http_rb/client.rb +17 -0
  18. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  19. data/lib/webmock/http_lib_adapters/http_rb/response.rb +64 -0
  20. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  21. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  22. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  23. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +152 -86
  24. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +145 -0
  25. data/lib/webmock/http_lib_adapters/net_http.rb +155 -46
  26. data/lib/webmock/http_lib_adapters/net_http_response.rb +1 -1
  27. data/lib/webmock/http_lib_adapters/patron_adapter.rb +16 -15
  28. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +76 -82
  29. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  30. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  31. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  32. data/lib/webmock/matchers/hash_including_matcher.rb +4 -12
  33. data/lib/webmock/minitest.rb +29 -3
  34. data/lib/webmock/rack_response.rb +14 -7
  35. data/lib/webmock/request_body_diff.rb +64 -0
  36. data/lib/webmock/request_execution_verifier.rb +38 -17
  37. data/lib/webmock/request_pattern.rb +158 -38
  38. data/lib/webmock/request_registry.rb +3 -3
  39. data/lib/webmock/request_signature.rb +7 -3
  40. data/lib/webmock/request_signature_snippet.rb +61 -0
  41. data/lib/webmock/request_stub.rb +9 -6
  42. data/lib/webmock/response.rb +30 -15
  43. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +38 -2
  44. data/lib/webmock/rspec/matchers/webmock_matcher.rb +23 -2
  45. data/lib/webmock/rspec/matchers.rb +0 -1
  46. data/lib/webmock/rspec.rb +11 -2
  47. data/lib/webmock/stub_registry.rb +31 -10
  48. data/lib/webmock/stub_request_snippet.rb +14 -6
  49. data/lib/webmock/test_unit.rb +4 -4
  50. data/lib/webmock/util/hash_counter.rb +20 -6
  51. data/lib/webmock/util/hash_keys_stringifier.rb +5 -3
  52. data/lib/webmock/util/hash_validator.rb +17 -0
  53. data/lib/webmock/util/headers.rb +23 -2
  54. data/lib/webmock/util/json.rb +20 -7
  55. data/lib/webmock/util/query_mapper.rb +281 -0
  56. data/lib/webmock/util/uri.rb +29 -19
  57. data/lib/webmock/util/values_stringifier.rb +20 -0
  58. data/lib/webmock/util/version_checker.rb +40 -2
  59. data/lib/webmock/version.rb +1 -1
  60. data/lib/webmock/webmock.rb +56 -17
  61. data/lib/webmock.rb +56 -46
  62. data/minitest/test_helper.rb +8 -3
  63. data/minitest/test_webmock.rb +4 -1
  64. data/minitest/webmock_spec.rb +16 -6
  65. data/spec/acceptance/async_http_client/async_http_client_spec.rb +375 -0
  66. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  67. data/spec/acceptance/curb/curb_spec.rb +227 -68
  68. data/spec/acceptance/curb/curb_spec_helper.rb +11 -8
  69. data/spec/acceptance/em_http_request/em_http_request_spec.rb +322 -28
  70. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +15 -10
  71. data/spec/acceptance/excon/excon_spec.rb +66 -4
  72. data/spec/acceptance/excon/excon_spec_helper.rb +21 -7
  73. data/spec/acceptance/http_rb/http_rb_spec.rb +93 -0
  74. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  75. data/spec/acceptance/httpclient/httpclient_spec.rb +152 -11
  76. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +25 -16
  77. data/spec/acceptance/manticore/manticore_spec.rb +107 -0
  78. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  79. data/spec/acceptance/net_http/net_http_shared.rb +52 -24
  80. data/spec/acceptance/net_http/net_http_spec.rb +164 -50
  81. data/spec/acceptance/net_http/net_http_spec_helper.rb +19 -10
  82. data/spec/acceptance/net_http/real_net_http_spec.rb +1 -1
  83. data/spec/acceptance/patron/patron_spec.rb +29 -40
  84. data/spec/acceptance/patron/patron_spec_helper.rb +15 -11
  85. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +229 -58
  86. data/spec/acceptance/shared/callbacks.rb +32 -30
  87. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +20 -5
  88. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +14 -14
  89. data/spec/acceptance/shared/precedence_of_stubs.rb +6 -6
  90. data/spec/acceptance/shared/request_expectations.rb +560 -296
  91. data/spec/acceptance/shared/returning_declared_responses.rb +180 -138
  92. data/spec/acceptance/shared/stubbing_requests.rb +385 -154
  93. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +78 -17
  94. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +19 -15
  95. data/spec/acceptance/webmock_shared.rb +2 -2
  96. data/spec/fixtures/test.txt +1 -0
  97. data/spec/quality_spec.rb +27 -3
  98. data/spec/spec_helper.rb +11 -20
  99. data/spec/support/failures.rb +9 -0
  100. data/spec/support/my_rack_app.rb +8 -3
  101. data/spec/support/network_connection.rb +7 -13
  102. data/spec/support/webmock_server.rb +8 -3
  103. data/spec/unit/api_spec.rb +175 -0
  104. data/spec/unit/errors_spec.rb +116 -19
  105. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +1 -1
  106. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +2 -2
  107. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  108. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  109. data/spec/unit/rack_response_spec.rb +54 -16
  110. data/spec/unit/request_body_diff_spec.rb +90 -0
  111. data/spec/unit/request_execution_verifier_spec.rb +147 -39
  112. data/spec/unit/request_pattern_spec.rb +462 -198
  113. data/spec/unit/request_registry_spec.rb +29 -9
  114. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  115. data/spec/unit/request_signature_spec.rb +91 -49
  116. data/spec/unit/request_stub_spec.rb +71 -70
  117. data/spec/unit/response_spec.rb +100 -81
  118. data/spec/unit/stub_registry_spec.rb +37 -20
  119. data/spec/unit/stub_request_snippet_spec.rb +51 -31
  120. data/spec/unit/util/hash_counter_spec.rb +6 -6
  121. data/spec/unit/util/hash_keys_stringifier_spec.rb +4 -4
  122. data/spec/unit/util/headers_spec.rb +4 -4
  123. data/spec/unit/util/json_spec.rb +29 -3
  124. data/spec/unit/util/query_mapper_spec.rb +157 -0
  125. data/spec/unit/util/uri_spec.rb +150 -36
  126. data/spec/unit/util/version_checker_spec.rb +15 -9
  127. data/spec/unit/webmock_spec.rb +57 -4
  128. data/test/http_request.rb +3 -3
  129. data/test/shared_test.rb +45 -13
  130. data/test/test_helper.rb +1 -1
  131. data/test/test_webmock.rb +6 -0
  132. data/webmock.gemspec +30 -11
  133. metadata +308 -199
  134. data/.rvmrc +0 -1
  135. data/.travis.yml +0 -11
  136. data/Guardfile +0 -24
  137. data/lib/webmock/http_lib_adapters/em_http_request/em_http_request_0_x.rb +0 -151
  138. 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
@@ -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(:exception => e)
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(:should_timeout => true)])
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?(Fixnum) || number < 1
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
- Addressable::URI.parse('?' + signature.body).query_values
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(:body => body)
91
+ stub.with(body: body)
89
92
  end
90
93
 
91
94
  if (signature.headers && !signature.headers.empty?)
92
- stub.with(:headers => signature.headers)
95
+ stub.with(headers: signature.headers)
93
96
  end
94
97
  stub
95
98
  end
@@ -1,7 +1,4 @@
1
- #compatibility with Ruby 1.9.2 preview1 to allow reading raw responses
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
- 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]
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
- self.headers === other.headers &&
95
- self.status == other.status &&
96
- self.exception == other.exception &&
97
- self.should_timeout == other.should_timeout
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 negative_failure_message
39
- @request_execution_verifier.negative_failure_message
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 negative_failure_message
43
- @request_execution_verifier.negative_failure_message
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
@@ -12,7 +12,6 @@ module WebMock
12
12
  WebMock::RequestPatternMatcher.new
13
13
  end
14
14
 
15
-
16
15
  def have_not_been_made
17
16
  WebMock::RequestPatternMatcher.new.times(0)
18
17
  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) && defined?(RSpec::Expectations)
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.after(:each) do
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
- stub = ::WebMock::RequestStub.new(:any, /.*/).with { |request|
28
- responses[request.object_id] = block.call(request)
29
- }.to_return(lambda { |request| responses.delete(request.object_id) })
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).detect { |registered_request_stub|
52
- registered_request_stub.request_pattern.matches?(request_signature)
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 << ":body => #{request_pattern.body_pattern.to_s}"
19
+ with << "\n body: #{request_pattern.body_pattern.to_s}"
16
20
  end
17
21
 
18
22
  if (request_pattern.headers_pattern)
19
- with << ",\n " unless with.empty?
23
+ with << "," unless with.empty?
20
24
 
21
- with << ":headers => #{request_pattern.headers_pattern.to_s}"
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
- string << ".\n to_return(:status => 200, :body => \"\", :headers => {})"
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
@@ -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
- alias_method :teardown_without_webmock, :teardown
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 rescue MiniTest::Assertion # ruby1.9 compat
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 Util::HashCounter
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
- hash[key] = (hash[key] || 0) + num
12
- @order[key] = @max = @max + 1
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
- hash[key] || 0
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.sort {|a, b| a[1] <=> b[1]}.each do |a|
20
- block.call(a[0], hash[a[0]])
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| stringify_keys!(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
@@ -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