webmock 0.9.1 → 1.0.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 (40) hide show
  1. data/CHANGELOG.md +47 -2
  2. data/README.md +68 -6
  3. data/Rakefile +1 -1
  4. data/VERSION +1 -1
  5. data/lib/webmock.rb +2 -2
  6. data/lib/webmock/adapters/rspec.rb +1 -1
  7. data/lib/webmock/adapters/rspec/matchers.rb +2 -2
  8. data/lib/webmock/adapters/rspec/{request_profile_matcher.rb → request_pattern_matcher.rb} +5 -5
  9. data/lib/webmock/adapters/rspec/webmock_matcher.rb +2 -2
  10. data/lib/webmock/config.rb +2 -1
  11. data/lib/webmock/http_lib_adapters/httpclient.rb +5 -4
  12. data/lib/webmock/http_lib_adapters/net_http.rb +5 -3
  13. data/lib/webmock/http_lib_adapters/patron.rb +82 -0
  14. data/lib/webmock/request_execution_verifier.rb +8 -8
  15. data/lib/webmock/request_pattern.rb +130 -0
  16. data/lib/webmock/request_registry.rb +4 -9
  17. data/lib/webmock/request_signature.rb +18 -37
  18. data/lib/webmock/request_stub.rb +17 -6
  19. data/lib/webmock/response.rb +87 -31
  20. data/lib/webmock/util/headers.rb +5 -0
  21. data/lib/webmock/webmock.rb +10 -6
  22. data/spec/httpclient_spec.rb +0 -1
  23. data/spec/httpclient_spec_helper.rb +11 -1
  24. data/spec/net_http_spec.rb +8 -1
  25. data/spec/net_http_spec_helper.rb +11 -1
  26. data/spec/patron_spec.rb +83 -0
  27. data/spec/patron_spec_helper.rb +44 -0
  28. data/spec/request_execution_verifier_spec.rb +8 -8
  29. data/spec/request_pattern_spec.rb +243 -0
  30. data/spec/request_registry_spec.rb +34 -19
  31. data/spec/request_signature_spec.rb +23 -191
  32. data/spec/request_stub_spec.rb +32 -11
  33. data/spec/response_spec.rb +98 -5
  34. data/spec/spec_helper.rb +8 -16
  35. data/spec/webmock_spec.rb +154 -49
  36. data/webmock.gemspec +14 -7
  37. metadata +21 -7
  38. data/lib/webmock/request.rb +0 -29
  39. data/lib/webmock/request_profile.rb +0 -50
  40. data/spec/request_profile_spec.rb +0 -68
@@ -1,22 +1,22 @@
1
1
  module WebMock
2
2
  class RequestExecutionVerifier
3
3
 
4
- attr_accessor :request_profile, :expected_times_executed, :times_executed
4
+ attr_accessor :request_pattern, :expected_times_executed, :times_executed
5
5
 
6
- def initialize(request_profile = nil, expected_times_executed = nil)
7
- @request_profile = request_profile
6
+ def initialize(request_pattern = nil, expected_times_executed = nil)
7
+ @request_pattern = request_pattern
8
8
  @expected_times_executed = expected_times_executed
9
9
  end
10
10
 
11
11
  def matches?
12
12
  @times_executed =
13
- RequestRegistry.instance.times_executed(@request_profile)
13
+ RequestRegistry.instance.times_executed(@request_pattern)
14
14
  @times_executed == (@expected_times_executed || 1)
15
15
  end
16
16
 
17
17
  def does_not_match?
18
18
  @times_executed =
19
- RequestRegistry.instance.times_executed(@request_profile)
19
+ RequestRegistry.instance.times_executed(@request_pattern)
20
20
  if @expected_times_executed
21
21
  @times_executed != @expected_times_executed
22
22
  else
@@ -27,14 +27,14 @@ module WebMock
27
27
 
28
28
  def failure_message
29
29
  expected_times_executed = @expected_times_executed || 1
30
- %Q(The request #{request_profile.to_s} was expected to execute #{expected_times_executed} time#{ (expected_times_executed == 1) ? '' : 's'} but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
30
+ %Q(The request #{request_pattern.to_s} was expected to execute #{expected_times_executed} time#{ (expected_times_executed == 1) ? '' : 's'} but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
31
31
  end
32
32
 
33
33
  def negative_failure_message
34
34
  if @expected_times_executed
35
- %Q(The request #{request_profile.to_s} was not expected to execute #{expected_times_executed} time#{ (expected_times_executed == 1) ? '' : 's'} but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
35
+ %Q(The request #{request_pattern.to_s} was not expected to execute #{expected_times_executed} time#{ (expected_times_executed == 1) ? '' : 's'} but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
36
36
  else
37
- %Q(The request #{request_profile.to_s} was expected to execute 0 times but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
37
+ %Q(The request #{request_pattern.to_s} was expected to execute 0 times but it executed #{times_executed} time#{ (times_executed == 1) ? '' : 's'})
38
38
  end
39
39
  end
40
40
 
@@ -0,0 +1,130 @@
1
+ module WebMock
2
+
3
+ class RequestPattern
4
+
5
+ def initialize(method, uri, options = {})
6
+ @method_pattern = MethodPattern.new(method)
7
+ @uri_pattern = URIPattern.new(uri)
8
+ assign_options(options)
9
+ end
10
+
11
+ def with(options = {}, &block)
12
+ assign_options(options)
13
+ @with_block = block
14
+ self
15
+ end
16
+
17
+ def matches?(request_signature)
18
+ @method_pattern.matches?(request_signature.method) &&
19
+ @uri_pattern.matches?(request_signature.uri) &&
20
+ (@body_pattern.nil? || @body_pattern.matches?(request_signature.body)) &&
21
+ (@headers_pattern.nil? || @headers_pattern.matches?(request_signature.headers)) &&
22
+ (@with_block.nil? || @with_block.call(request_signature))
23
+ end
24
+
25
+ def to_s
26
+ string = "#{@method_pattern.to_s.upcase}"
27
+ string << " #{@uri_pattern.to_s}"
28
+ string << " with body '#{@body_pattern.to_s}'" if @body_pattern
29
+ string << " with headers #{@headers_pattern.to_s}" if @headers_pattern
30
+ string << " with given block" if @with_block
31
+ string
32
+ end
33
+
34
+ private
35
+
36
+
37
+ def assign_options(options)
38
+ @body_pattern = BodyPattern.new(options[:body]) if options.has_key?(:body)
39
+ @headers_pattern = HeadersPattern.new(options[:headers]) if options.has_key?(:headers)
40
+ end
41
+
42
+ end
43
+
44
+
45
+ class MethodPattern
46
+ def initialize(pattern)
47
+ @pattern = pattern
48
+ end
49
+
50
+ def matches?(method)
51
+ @pattern == method || @pattern == :any
52
+ end
53
+
54
+ def to_s
55
+ @pattern.to_s
56
+ end
57
+ end
58
+
59
+ class URIPattern
60
+ def initialize(pattern)
61
+ @pattern = pattern.is_a?(Addressable::URI) ? pattern : WebMock::Util::URI.normalize_uri(pattern)
62
+ end
63
+
64
+ def matches?(uri)
65
+ if @pattern.is_a?(Addressable::URI)
66
+ ##TODO : do I need to normalize again??
67
+ WebMock::Util::URI.normalize_uri(uri) === WebMock::Util::URI.normalize_uri(@pattern)
68
+ elsif @pattern.is_a?(Regexp)
69
+ WebMock::Util::URI.variations_of_uri_as_strings(uri).any? { |u| u.match(@pattern) }
70
+ else
71
+ false
72
+ end
73
+ end
74
+
75
+ def to_s
76
+ WebMock::Util::URI.strip_default_port_from_uri_string(@pattern.to_s)
77
+ end
78
+ end
79
+
80
+ class BodyPattern
81
+ def initialize(pattern)
82
+ @pattern = pattern
83
+ end
84
+
85
+ def matches?(uri)
86
+ empty_string?(@pattern) && empty_string?(uri) ||
87
+ @pattern == uri ||
88
+ @pattern === uri
89
+ end
90
+
91
+ def to_s
92
+ @pattern.to_s
93
+ end
94
+
95
+ private
96
+
97
+ def empty_string?(string)
98
+ string.nil? || string == ""
99
+ end
100
+ end
101
+
102
+ class HeadersPattern
103
+ def initialize(pattern)
104
+ @pattern = WebMock::Util::Headers.normalize_headers(pattern) || {}
105
+ end
106
+
107
+ def matches?(headers)
108
+ if empty_headers?(@pattern)
109
+ empty_headers?(headers)
110
+ else
111
+ return false if empty_headers?(headers)
112
+ @pattern.each do |key, value|
113
+ return false unless headers.has_key?(key) && value === headers[key]
114
+ end
115
+ true
116
+ end
117
+ end
118
+
119
+ def to_s
120
+ WebMock::Util::Headers.sorted_headers_string(@pattern)
121
+ end
122
+
123
+ private
124
+
125
+ def empty_headers?(headers)
126
+ headers.nil? || headers == {}
127
+ end
128
+ end
129
+
130
+ end
@@ -28,9 +28,9 @@ module WebMock
28
28
  stub ? evaluate_response_for_request(stub.response, request_signature) : nil
29
29
  end
30
30
 
31
- def times_executed(request_profile)
31
+ def times_executed(request_pattern)
32
32
  self.requested_signatures.hash.select { |request_signature, times_executed|
33
- request_signature.match(request_profile)
33
+ request_pattern.matches?(request_signature)
34
34
  }.inject(0) {|sum, (_, times_executed)| sum + times_executed }
35
35
  end
36
36
 
@@ -38,18 +38,13 @@ module WebMock
38
38
 
39
39
  def request_stub_for(request_signature)
40
40
  request_stubs.detect { |registered_request_stub|
41
- request_signature.match(registered_request_stub.request_profile)
41
+ registered_request_stub.request_pattern.matches?(request_signature)
42
42
  }
43
43
  end
44
44
 
45
45
  def evaluate_response_for_request(response, request_signature)
46
46
  evaluated_response = response.dup
47
- [:body, :headers].each do |attribute|
48
- if response.options[attribute].is_a?(Proc)
49
- evaluated_response.options[attribute] = response.options[attribute].call(request_signature)
50
- end
51
- end
52
- evaluated_response
47
+ evaluated_response.evaluate!(request_signature)
53
48
  end
54
49
 
55
50
  end
@@ -1,51 +1,32 @@
1
1
  module WebMock
2
2
 
3
- class RequestSignature < Request
4
-
5
- def match(request_profile)
6
- raise ArgumentError unless request_profile.is_a?(RequestProfile)
7
- match_method(request_profile) &&
8
- match_body(request_profile) &&
9
- match_headers(request_profile) &&
10
- match_uri(request_profile) &&
11
- match_with_block(request_profile)
12
- end
3
+ class RequestSignature
13
4
 
14
- private
5
+ attr_accessor :method, :uri, :body, :headers
15
6
 
16
- def match_uri(request_profile)
17
- if request_profile.uri.is_a?(Addressable::URI)
18
- WebMock::Util::URI.normalize_uri(uri) === WebMock::Util::URI.normalize_uri(request_profile.uri)
19
- elsif request_profile.uri.is_a?(Regexp)
20
- WebMock::Util::URI.variations_of_uri_as_strings(self.uri).any? { |u| u.match(request_profile.uri) }
21
- else
22
- false
23
- end
7
+ def initialize(method, uri, options = {})
8
+ self.method = method
9
+ self.uri = uri.is_a?(Addressable::URI) ? uri : WebMock::Util::URI.normalize_uri(uri)
10
+ assign_options(options)
24
11
  end
25
12
 
26
- def match_headers(request_profile)
27
- return false if self.headers && !self.headers.empty? && request_profile.headers && request_profile.headers.empty?
28
- if request_profile.headers && !request_profile.headers.empty?
29
- request_profile.headers.each do | key, value |
30
- return false unless (self.headers && self.headers.has_key?(key) && value === self.headers[key])
31
- end
13
+ def to_s
14
+ string = "#{self.method.to_s.upcase}"
15
+ string << " #{WebMock::Util::URI.strip_default_port_from_uri_string(self.uri.to_s)}"
16
+ string << " with body '#{body.to_s}'" if body && body.to_s != ''
17
+ if headers && !headers.empty?
18
+ string << " with headers #{WebMock::Util::Headers.sorted_headers_string(headers)}"
32
19
  end
33
- return true
20
+ string
34
21
  end
35
22
 
36
- def match_body(request_profile)
37
- request_profile.body == self.body || request_profile.body.nil?
38
- end
23
+ private
39
24
 
40
- def match_method(request_profile)
41
- request_profile.method == self.method || request_profile.method == :any
42
- end
43
-
44
- def match_with_block(request_profile)
45
- return true unless request_profile.with_block
46
- request_profile.with_block.call(self)
25
+ def assign_options(options)
26
+ self.body = options[:body] if options.has_key?(:body)
27
+ self.headers = WebMock::Util::Headers.normalize_headers(options[:headers]) if options.has_key?(:headers)
47
28
  end
48
- end
49
29
 
30
+ end
50
31
 
51
32
  end
@@ -1,26 +1,37 @@
1
1
  module WebMock
2
2
  class RequestStub
3
3
 
4
- attr_accessor :request_profile, :responses
4
+ attr_accessor :request_pattern
5
5
 
6
6
  def initialize(method, uri)
7
- @request_profile = RequestProfile.new(method, uri)
7
+ @request_pattern = RequestPattern.new(method, uri)
8
8
  @responses_sequences = []
9
9
  self
10
10
  end
11
11
 
12
12
  def with(params = {}, &block)
13
- @request_profile.with(params, &block)
13
+ @request_pattern.with(params, &block)
14
14
  self
15
15
  end
16
16
 
17
- def to_return(*response_hashes)
18
- @responses_sequences << ResponsesSequence.new([*response_hashes].flatten.map {|r| WebMock::Response.new(r)})
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
19
23
  self
20
24
  end
21
25
 
22
26
  def to_raise(*exceptions)
23
- @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e| WebMock::Response.new(:exception => e)})
27
+ @responses_sequences << ResponsesSequence.new([*exceptions].flatten.map {|e|
28
+ ResponseFactory.response_for(:exception => e)
29
+ })
30
+ self
31
+ end
32
+
33
+ def to_timeout
34
+ @responses_sequences << ResponsesSequence.new([ResponseFactory.response_for(:should_timeout => true)])
24
35
  self
25
36
  end
26
37
 
@@ -4,81 +4,137 @@ class StringIO
4
4
  end
5
5
 
6
6
  module WebMock
7
+
8
+ class ResponseFactory
9
+ def self.response_for(options)
10
+ if options.respond_to?(:call)
11
+ WebMock::DynamicResponse.new(options)
12
+ else
13
+ WebMock::Response.new(options)
14
+ end
15
+ end
16
+ end
7
17
 
8
18
  class Response
9
- attr_reader :options
10
-
11
19
  def initialize(options = {})
12
- if options.is_a?(IO) || options.is_a?(String)
13
- self.options = read_raw_response(options)
14
- else
15
- self.options = options
20
+ if options.is_a?(IO) || options.is_a?(String)
21
+ self.options = read_raw_response(options)
22
+ else
23
+ self.options = options
16
24
  end
17
- @options[:headers] = Util::Headers.normalize_headers(@options[:headers]) unless @options[:headers].is_a?(Proc)
18
25
  end
19
26
 
20
27
  def headers
21
- @options[:headers]
28
+ @headers
29
+ end
30
+
31
+ def headers=(headers)
32
+ @headers = headers
33
+ if @headers && !@headers.is_a?(Proc)
34
+ @headers = Util::Headers.normalize_headers(@headers)
35
+ end
22
36
  end
23
37
 
24
38
  def body
25
- return '' unless @options.has_key?(:body)
39
+ @body || ''
40
+ end
41
+
42
+ def body=(body)
43
+ @body = body
26
44
  stringify_body!
27
- @options[:body]
28
45
  end
29
46
 
30
47
  def status
31
- @options.has_key?(:status) ? @options[:status] : 200
48
+ @status || [200, ""]
49
+ end
50
+
51
+ def status=(status)
52
+ @status = status.is_a?(Integer) ? [status, ""] : status
53
+ end
54
+
55
+ def exception
56
+ @exception
32
57
  end
33
58
 
34
59
  def raise_error_if_any
35
- raise @options[:exception].new('Exception from WebMock') if @options.has_key?(:exception)
60
+ raise @exception.new('Exception from WebMock') if @exception
61
+ end
62
+
63
+ def should_timeout
64
+ @should_timeout == true
36
65
  end
37
66
 
38
67
  def options=(options)
39
- @options = options
40
- stringify_body!
68
+ self.headers = options[:headers]
69
+ self.status = options[:status]
70
+ self.body = options[:body]
71
+ @exception = options[:exception]
72
+ @should_timeout = options[:should_timeout]
41
73
  end
42
74
 
43
- def dup
44
- dup_response = super
45
- dup_response.options = options.dup
46
- dup_response
75
+ def evaluate!(request_signature)
76
+ self.body = @body.call(request_signature) if @body.is_a?(Proc)
77
+ self.headers = @headers.call(request_signature) if @headers.is_a?(Proc)
78
+ self.status = @status.call(request_signature) if @status.is_a?(Proc)
79
+ @should_timeout = @should_timeout.call(request_signature) if @should_timeout.is_a?(Proc)
80
+ @exception = @exception.call(request_signature) if @exception.is_a?(Proc)
81
+ self
47
82
  end
48
83
 
49
84
  def ==(other)
50
- options == other.options
85
+ self.body == other.body &&
86
+ self.headers === other.headers &&
87
+ self.status == other.status &&
88
+ self.exception == other.exception &&
89
+ self.should_timeout == other.should_timeout
51
90
  end
52
-
53
- private
91
+
92
+ private
54
93
 
55
94
  def stringify_body!
56
- if @options[:body].is_a?(IO)
57
- io = @options[:body]
58
- @options[:body] = io.read
95
+ if @body.is_a?(IO)
96
+ io = @body
97
+ @body = io.read
59
98
  io.close
60
99
  end
61
100
  end
62
-
101
+
63
102
  def read_raw_response(raw_response)
64
103
  if raw_response.is_a?(IO)
65
- string = raw_response.read
104
+ string = raw_response.read
66
105
  raw_response.close
67
106
  raw_response = string
68
- end
69
- socket = Net::BufferedIO.new(raw_response)
107
+ end
108
+ socket = Net::BufferedIO.new(raw_response)
70
109
  response = Net::HTTPResponse.read_new(socket)
71
110
  transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
72
111
  response.reading_body(socket, true) {}
73
-
112
+
74
113
  options = {}
75
114
  options[:headers] = {}
76
115
  response.each_header {|name, value| options[:headers][name] = value}
77
116
  options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
78
117
  options[:body] = response.read_body
79
- options[:status] = response.code.to_i
80
- options
118
+ options[:status] = [response.code.to_i, response.message]
119
+ options
120
+ end
121
+
122
+ end
123
+
124
+ class DynamicResponse < Response
125
+ attr_accessor :responder
126
+
127
+ def initialize(responder)
128
+ @responder = responder
81
129
  end
82
130
 
131
+ def dup
132
+ self.class.new(@responder)
133
+ end
134
+
135
+ def evaluate!(request_signature)
136
+ self.options = @responder.call(request_signature)
137
+ self
138
+ end
83
139
  end
84
140
  end