webmock 3.7.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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +34 -0
  4. data/.rspec-tm +2 -0
  5. data/.travis.yml +19 -0
  6. data/CHANGELOG.md +1698 -0
  7. data/Gemfile +9 -0
  8. data/LICENSE +20 -0
  9. data/README.md +1125 -0
  10. data/Rakefile +28 -0
  11. data/lib/webmock.rb +59 -0
  12. data/lib/webmock/api.rb +109 -0
  13. data/lib/webmock/assertion_failure.rb +11 -0
  14. data/lib/webmock/callback_registry.rb +35 -0
  15. data/lib/webmock/config.rb +18 -0
  16. data/lib/webmock/cucumber.rb +10 -0
  17. data/lib/webmock/deprecation.rb +9 -0
  18. data/lib/webmock/errors.rb +17 -0
  19. data/lib/webmock/http_lib_adapters/async_http_client_adapter.rb +214 -0
  20. data/lib/webmock/http_lib_adapters/curb_adapter.rb +347 -0
  21. data/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +228 -0
  22. data/lib/webmock/http_lib_adapters/excon_adapter.rb +162 -0
  23. data/lib/webmock/http_lib_adapters/http_lib_adapter.rb +7 -0
  24. data/lib/webmock/http_lib_adapters/http_lib_adapter_registry.rb +19 -0
  25. data/lib/webmock/http_lib_adapters/http_rb/client.rb +14 -0
  26. data/lib/webmock/http_lib_adapters/http_rb/request.rb +16 -0
  27. data/lib/webmock/http_lib_adapters/http_rb/response.rb +43 -0
  28. data/lib/webmock/http_lib_adapters/http_rb/streamer.rb +29 -0
  29. data/lib/webmock/http_lib_adapters/http_rb/webmock.rb +68 -0
  30. data/lib/webmock/http_lib_adapters/http_rb_adapter.rb +37 -0
  31. data/lib/webmock/http_lib_adapters/httpclient_adapter.rb +242 -0
  32. data/lib/webmock/http_lib_adapters/manticore_adapter.rb +130 -0
  33. data/lib/webmock/http_lib_adapters/net_http.rb +361 -0
  34. data/lib/webmock/http_lib_adapters/net_http_response.rb +34 -0
  35. data/lib/webmock/http_lib_adapters/patron_adapter.rb +130 -0
  36. data/lib/webmock/http_lib_adapters/typhoeus_hydra_adapter.rb +174 -0
  37. data/lib/webmock/matchers/any_arg_matcher.rb +13 -0
  38. data/lib/webmock/matchers/hash_argument_matcher.rb +21 -0
  39. data/lib/webmock/matchers/hash_excluding_matcher.rb +15 -0
  40. data/lib/webmock/matchers/hash_including_matcher.rb +17 -0
  41. data/lib/webmock/minitest.rb +41 -0
  42. data/lib/webmock/rack_response.rb +69 -0
  43. data/lib/webmock/request_body_diff.rb +64 -0
  44. data/lib/webmock/request_execution_verifier.rb +77 -0
  45. data/lib/webmock/request_pattern.rb +370 -0
  46. data/lib/webmock/request_registry.rb +35 -0
  47. data/lib/webmock/request_signature.rb +54 -0
  48. data/lib/webmock/request_signature_snippet.rb +61 -0
  49. data/lib/webmock/request_stub.rb +100 -0
  50. data/lib/webmock/response.rb +153 -0
  51. data/lib/webmock/responses_sequence.rb +40 -0
  52. data/lib/webmock/rspec.rb +41 -0
  53. data/lib/webmock/rspec/matchers.rb +27 -0
  54. data/lib/webmock/rspec/matchers/request_pattern_matcher.rb +78 -0
  55. data/lib/webmock/rspec/matchers/webmock_matcher.rb +67 -0
  56. data/lib/webmock/stub_registry.rb +67 -0
  57. data/lib/webmock/stub_request_snippet.rb +38 -0
  58. data/lib/webmock/test_unit.rb +22 -0
  59. data/lib/webmock/util/hash_counter.rb +39 -0
  60. data/lib/webmock/util/hash_keys_stringifier.rb +25 -0
  61. data/lib/webmock/util/hash_validator.rb +17 -0
  62. data/lib/webmock/util/headers.rb +64 -0
  63. data/lib/webmock/util/json.rb +67 -0
  64. data/lib/webmock/util/query_mapper.rb +281 -0
  65. data/lib/webmock/util/uri.rb +110 -0
  66. data/lib/webmock/util/values_stringifier.rb +20 -0
  67. data/lib/webmock/util/version_checker.rb +111 -0
  68. data/lib/webmock/version.rb +3 -0
  69. data/lib/webmock/webmock.rb +161 -0
  70. data/minitest/test_helper.rb +34 -0
  71. data/minitest/test_webmock.rb +9 -0
  72. data/minitest/webmock_spec.rb +60 -0
  73. data/spec/acceptance/async_http_client/async_http_client_spec.rb +349 -0
  74. data/spec/acceptance/async_http_client/async_http_client_spec_helper.rb +73 -0
  75. data/spec/acceptance/curb/curb_spec.rb +492 -0
  76. data/spec/acceptance/curb/curb_spec_helper.rb +147 -0
  77. data/spec/acceptance/em_http_request/em_http_request_spec.rb +406 -0
  78. data/spec/acceptance/em_http_request/em_http_request_spec_helper.rb +77 -0
  79. data/spec/acceptance/excon/excon_spec.rb +77 -0
  80. data/spec/acceptance/excon/excon_spec_helper.rb +50 -0
  81. data/spec/acceptance/http_rb/http_rb_spec.rb +82 -0
  82. data/spec/acceptance/http_rb/http_rb_spec_helper.rb +54 -0
  83. data/spec/acceptance/httpclient/httpclient_spec.rb +217 -0
  84. data/spec/acceptance/httpclient/httpclient_spec_helper.rb +57 -0
  85. data/spec/acceptance/manticore/manticore_spec.rb +56 -0
  86. data/spec/acceptance/manticore/manticore_spec_helper.rb +35 -0
  87. data/spec/acceptance/net_http/net_http_shared.rb +153 -0
  88. data/spec/acceptance/net_http/net_http_spec.rb +331 -0
  89. data/spec/acceptance/net_http/net_http_spec_helper.rb +64 -0
  90. data/spec/acceptance/net_http/real_net_http_spec.rb +20 -0
  91. data/spec/acceptance/patron/patron_spec.rb +125 -0
  92. data/spec/acceptance/patron/patron_spec_helper.rb +54 -0
  93. data/spec/acceptance/shared/allowing_and_disabling_net_connect.rb +313 -0
  94. data/spec/acceptance/shared/callbacks.rb +148 -0
  95. data/spec/acceptance/shared/complex_cross_concern_behaviors.rb +36 -0
  96. data/spec/acceptance/shared/enabling_and_disabling_webmock.rb +95 -0
  97. data/spec/acceptance/shared/precedence_of_stubs.rb +15 -0
  98. data/spec/acceptance/shared/request_expectations.rb +930 -0
  99. data/spec/acceptance/shared/returning_declared_responses.rb +409 -0
  100. data/spec/acceptance/shared/stubbing_requests.rb +643 -0
  101. data/spec/acceptance/typhoeus/typhoeus_hydra_spec.rb +135 -0
  102. data/spec/acceptance/typhoeus/typhoeus_hydra_spec_helper.rb +60 -0
  103. data/spec/acceptance/webmock_shared.rb +41 -0
  104. data/spec/fixtures/test.txt +1 -0
  105. data/spec/quality_spec.rb +84 -0
  106. data/spec/spec_helper.rb +48 -0
  107. data/spec/support/example_curl_output.txt +22 -0
  108. data/spec/support/failures.rb +9 -0
  109. data/spec/support/my_rack_app.rb +53 -0
  110. data/spec/support/network_connection.rb +19 -0
  111. data/spec/support/webmock_server.rb +70 -0
  112. data/spec/unit/api_spec.rb +175 -0
  113. data/spec/unit/errors_spec.rb +129 -0
  114. data/spec/unit/http_lib_adapters/http_lib_adapter_registry_spec.rb +17 -0
  115. data/spec/unit/http_lib_adapters/http_lib_adapter_spec.rb +12 -0
  116. data/spec/unit/matchers/hash_excluding_matcher_spec.rb +61 -0
  117. data/spec/unit/matchers/hash_including_matcher_spec.rb +87 -0
  118. data/spec/unit/rack_response_spec.rb +112 -0
  119. data/spec/unit/request_body_diff_spec.rb +90 -0
  120. data/spec/unit/request_execution_verifier_spec.rb +208 -0
  121. data/spec/unit/request_pattern_spec.rb +601 -0
  122. data/spec/unit/request_registry_spec.rb +95 -0
  123. data/spec/unit/request_signature_snippet_spec.rb +89 -0
  124. data/spec/unit/request_signature_spec.rb +155 -0
  125. data/spec/unit/request_stub_spec.rb +199 -0
  126. data/spec/unit/response_spec.rb +282 -0
  127. data/spec/unit/stub_registry_spec.rb +103 -0
  128. data/spec/unit/stub_request_snippet_spec.rb +115 -0
  129. data/spec/unit/util/hash_counter_spec.rb +39 -0
  130. data/spec/unit/util/hash_keys_stringifier_spec.rb +27 -0
  131. data/spec/unit/util/headers_spec.rb +28 -0
  132. data/spec/unit/util/json_spec.rb +33 -0
  133. data/spec/unit/util/query_mapper_spec.rb +157 -0
  134. data/spec/unit/util/uri_spec.rb +361 -0
  135. data/spec/unit/util/version_checker_spec.rb +65 -0
  136. data/spec/unit/webmock_spec.rb +19 -0
  137. data/test/http_request.rb +24 -0
  138. data/test/shared_test.rb +108 -0
  139. data/test/test_helper.rb +23 -0
  140. data/test/test_webmock.rb +6 -0
  141. data/webmock.gemspec +45 -0
  142. metadata +496 -0
@@ -0,0 +1,35 @@
1
+ module WebMock
2
+
3
+ class RequestRegistry
4
+ include Singleton
5
+
6
+ attr_accessor :requested_signatures
7
+
8
+ def initialize
9
+ reset!
10
+ end
11
+
12
+ def reset!
13
+ self.requested_signatures = Util::HashCounter.new
14
+ end
15
+
16
+ def times_executed(request_pattern)
17
+ self.requested_signatures.select do |request_signature|
18
+ request_pattern.matches?(request_signature)
19
+ end.inject(0) { |sum, (_, times_executed)| sum + times_executed }
20
+ end
21
+
22
+ def to_s
23
+ if requested_signatures.hash.empty?
24
+ "No requests were made."
25
+ else
26
+ text = "".dup
27
+ self.requested_signatures.each do |request_signature, times_executed|
28
+ text << "#{request_signature} was made #{times_executed} time#{times_executed == 1 ? '' : 's' }\n"
29
+ end
30
+ text
31
+ end
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,54 @@
1
+ module WebMock
2
+
3
+ class RequestSignature
4
+
5
+ attr_accessor :method, :uri, :body
6
+ attr_reader :headers
7
+
8
+ def initialize(method, uri, options = {})
9
+ self.method = method.to_sym
10
+ self.uri = uri.is_a?(Addressable::URI) ? uri : WebMock::Util::URI.normalize_uri(uri)
11
+ assign_options(options)
12
+ end
13
+
14
+ def to_s
15
+ string = "#{self.method.to_s.upcase}".dup
16
+ string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
17
+ string << " with body '#{body.to_s}'" if body && body.to_s != ''
18
+ if headers && !headers.empty?
19
+ string << " with headers #{WebMock::Util::Headers.sorted_headers_string(headers)}"
20
+ end
21
+ string
22
+ end
23
+
24
+ def headers=(headers)
25
+ @headers = WebMock::Util::Headers.normalize_headers(headers)
26
+ end
27
+
28
+ def hash
29
+ self.to_s.hash
30
+ end
31
+
32
+ def eql?(other)
33
+ self.to_s == other.to_s
34
+ end
35
+ alias == eql?
36
+
37
+ def url_encoded?
38
+ !!(headers && headers['Content-Type'] == 'application/x-www-form-urlencoded')
39
+ end
40
+
41
+ def json_headers?
42
+ !!(headers && headers['Content-Type'] == 'application/json')
43
+ end
44
+
45
+ private
46
+
47
+ def assign_options(options)
48
+ self.body = options[:body] if options.has_key?(:body)
49
+ self.headers = options[:headers] if options.has_key?(:headers)
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -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
@@ -0,0 +1,100 @@
1
+ module WebMock
2
+ class RequestStub
3
+
4
+ attr_accessor :request_pattern
5
+
6
+ def initialize(method, uri)
7
+ @request_pattern = RequestPattern.new(method, uri)
8
+ @responses_sequences = []
9
+ self
10
+ end
11
+
12
+ def with(params = {}, &block)
13
+ @request_pattern.with(params, &block)
14
+ self
15
+ end
16
+
17
+ def to_return(*response_hashes, &block)
18
+ if block
19
+ @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(block)])
20
+ else
21
+ @responses_sequences << ResponsesSequence.new([*response_hashes].flatten.map {|r| ResponseFactory.response_for(r)})
22
+ end
23
+ self
24
+ end
25
+ alias_method :and_return, :to_return
26
+
27
+ def to_rack(app, options={})
28
+ @responses_sequences << ResponsesSequence.new([RackResponse.new(app)])
29
+ end
30
+
31
+ def to_raise(*exceptions)
32
+ @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
33
+ ResponseFactory.response_for(exception: e)
34
+ })
35
+ self
36
+ end
37
+ alias_method :and_raise, :to_raise
38
+
39
+ def to_timeout
40
+ @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(should_timeout: true)])
41
+ self
42
+ end
43
+ alias_method :and_timeout, :to_timeout
44
+
45
+ def response
46
+ if @responses_sequences.empty?
47
+ WebMock::Response.new
48
+ elsif @responses_sequences.length > 1
49
+ @responses_sequences.shift if @responses_sequences.first.end?
50
+ @responses_sequences.first.next_response
51
+ else
52
+ @responses_sequences[0].next_response
53
+ end
54
+ end
55
+
56
+ def has_responses?
57
+ !@responses_sequences.empty?
58
+ end
59
+
60
+ def then
61
+ self
62
+ end
63
+
64
+ def times(number)
65
+ raise "times(N) accepts integers >= 1 only" if !number.is_a?(Integer) || number < 1
66
+ if @responses_sequences.empty?
67
+ raise "Invalid WebMock stub declaration." +
68
+ " times(N) can be declared only after response declaration."
69
+ end
70
+ @responses_sequences.last.times_to_repeat += number-1
71
+ self
72
+ end
73
+
74
+ def matches?(request_signature)
75
+ self.request_pattern.matches?(request_signature)
76
+ end
77
+
78
+ def to_s
79
+ self.request_pattern.to_s
80
+ end
81
+
82
+ def self.from_request_signature(signature)
83
+ stub = self.new(signature.method.to_sym, signature.uri.to_s)
84
+
85
+ if signature.body.to_s != ''
86
+ body = if signature.url_encoded?
87
+ WebMock::Util::QueryMapper.query_to_values(signature.body, notation: Config.instance.query_values_notation)
88
+ else
89
+ signature.body
90
+ end
91
+ stub.with(body: body)
92
+ end
93
+
94
+ if (signature.headers && !signature.headers.empty?)
95
+ stub.with(headers: signature.headers)
96
+ end
97
+ stub
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,153 @@
1
+ require "pathname"
2
+
3
+ module WebMock
4
+
5
+ class ResponseFactory
6
+ def self.response_for(options)
7
+ if options.respond_to?(:call)
8
+ WebMock::DynamicResponse.new(options)
9
+ else
10
+ WebMock::Response.new(options)
11
+ end
12
+ end
13
+ end
14
+
15
+ class Response
16
+ def initialize(options = {})
17
+ if options.is_a?(IO) || options.is_a?(String)
18
+ self.options = read_raw_response(options)
19
+ else
20
+ self.options = options
21
+ end
22
+ end
23
+
24
+ def headers
25
+ @headers
26
+ end
27
+
28
+ def headers=(headers)
29
+ @headers = headers
30
+ if @headers && !@headers.is_a?(Proc)
31
+ @headers = Util::Headers.normalize_headers(@headers)
32
+ end
33
+ end
34
+
35
+ def body
36
+ @body || ''
37
+ end
38
+
39
+ def body=(body)
40
+ @body = body
41
+ assert_valid_body!
42
+ stringify_body!
43
+ end
44
+
45
+ def status
46
+ @status || [200, ""]
47
+ end
48
+
49
+ def status=(status)
50
+ @status = status.is_a?(Integer) ? [status, ""] : status
51
+ end
52
+
53
+ def exception
54
+ @exception
55
+ end
56
+
57
+ def exception=(exception)
58
+ @exception = case exception
59
+ when String then StandardError.new(exception)
60
+ when Class then exception.new('Exception from WebMock')
61
+ when Exception then exception
62
+ end
63
+ end
64
+
65
+ def raise_error_if_any
66
+ raise @exception if @exception
67
+ end
68
+
69
+ def should_timeout
70
+ @should_timeout == true
71
+ end
72
+
73
+ def options=(options)
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
+ end
82
+
83
+ def evaluate(request_signature)
84
+ self.body = @body.call(request_signature) if @body.is_a?(Proc)
85
+ self.headers = @headers.call(request_signature) if @headers.is_a?(Proc)
86
+ self.status = @status.call(request_signature) if @status.is_a?(Proc)
87
+ @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc)
88
+ @exception = @exception.call(request_signature) if @exception.is_a?(Proc)
89
+ self
90
+ end
91
+
92
+ def ==(other)
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
98
+ end
99
+
100
+ private
101
+
102
+ def stringify_body!
103
+ if @body.is_a?(IO) || @body.is_a?(Pathname)
104
+ io = @body
105
+ @body = io.read
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
+ raise InvalidBody, "must be one of: #{valid_types}. '#{@body.class}' given"
115
+ end
116
+
117
+ def read_raw_response(raw_response)
118
+ if raw_response.is_a?(IO)
119
+ string = raw_response.read
120
+ raw_response.close
121
+ raw_response = string
122
+ end
123
+ socket = ::Net::BufferedIO.new(raw_response)
124
+ response = ::Net::HTTPResponse.read_new(socket)
125
+ transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
126
+ response.reading_body(socket, true) {}
127
+
128
+ options = {}
129
+ options[:headers] = {}
130
+ response.each_header {|name, value| options[:headers][name] = value}
131
+ options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
132
+ options[:body] = response.read_body
133
+ options[:status] = [response.code.to_i, response.message]
134
+ options
135
+ end
136
+
137
+ InvalidBody = Class.new(StandardError)
138
+
139
+ end
140
+
141
+ class DynamicResponse < Response
142
+ attr_accessor :responder
143
+
144
+ def initialize(responder)
145
+ @responder = responder
146
+ end
147
+
148
+ def evaluate(request_signature)
149
+ options = @responder.call(request_signature)
150
+ Response.new(options)
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,40 @@
1
+ module WebMock
2
+
3
+ class ResponsesSequence
4
+
5
+ attr_accessor :times_to_repeat
6
+
7
+ def initialize(responses)
8
+ @times_to_repeat = 1
9
+ @responses = responses
10
+ @current_position = 0
11
+ end
12
+
13
+ def end?
14
+ @times_to_repeat == 0
15
+ end
16
+
17
+ def next_response
18
+ if @times_to_repeat > 0
19
+ response = @responses[@current_position]
20
+ increase_position
21
+ response
22
+ else
23
+ @responses.last
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def increase_position
30
+ if @current_position == (@responses.length - 1)
31
+ @current_position = 0
32
+ @times_to_repeat -= 1
33
+ else
34
+ @current_position += 1
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end