webmock 1.8.6 → 3.14.0

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