webmock 0.9.1 → 1.0.0

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