webmock 0.8.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,19 @@
1
+ #compatibility with Ruby 1.9.2 preview1 to allow reading raw responses
2
+ class StringIO
3
+ alias_method :read_nonblock, :sysread
4
+ end
5
+
1
6
  module WebMock
7
+
2
8
  class Response
3
9
  attr_reader :options
4
10
 
5
11
  def initialize(options = {})
6
- self.options = options
12
+ if options.is_a?(IO) || options.is_a?(String)
13
+ self.options = read_raw_response(options)
14
+ else
15
+ self.options = options
16
+ end
7
17
  @options[:headers] = Util::Headers.normalize_headers(@options[:headers]) unless @options[:headers].is_a?(Proc)
8
18
  end
9
19
 
@@ -39,6 +49,8 @@ module WebMock
39
49
  def ==(other)
40
50
  options == other.options
41
51
  end
52
+
53
+ private
42
54
 
43
55
  def stringify_body!
44
56
  if @options[:body].is_a?(IO)
@@ -47,6 +59,25 @@ module WebMock
47
59
  io.close
48
60
  end
49
61
  end
62
+
63
+ def read_raw_response(raw_response)
64
+ if raw_response.is_a?(IO)
65
+ string = raw_response.read
66
+ raw_response.close
67
+ raw_response = string
68
+ end
69
+ socket = Net::BufferedIO.new(raw_response)
70
+ response = Net::HTTPResponse.read_new(socket)
71
+ transfer_encoding = response.delete('transfer-encoding') #chunks were already read by curl
72
+ response.reading_body(socket, true) {}
73
+
74
+ options = {}
75
+ options[:headers] = {}
76
+ response.each_header {|name, value| options[:headers][name] = value}
77
+ options[:headers]['transfer-encoding'] = transfer_encoding if transfer_encoding
78
+ options[:body] = response.read_body
79
+ options
80
+ end
50
81
 
51
82
  end
52
83
  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
@@ -7,7 +7,7 @@ module WebMock
7
7
  def self.normalize_headers(headers)
8
8
  return nil unless headers
9
9
  array = headers.map { |name, value|
10
- [name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"), value.to_s]
10
+ [name.to_s.split(/_|-/).map { |segment| segment.capitalize }.join("-"), value.is_a?(Regexp) ? value : value.to_s]
11
11
  }
12
12
  Hash[*array.flatten]
13
13
  end
@@ -11,15 +11,15 @@ module WebMock
11
11
  RequestProfile.new(method, uri)
12
12
  end
13
13
 
14
- def assert_requested(method, uri, options = {})
14
+ def assert_requested(method, uri, options = {}, &block)
15
15
  expected_times_executed = options.delete(:times) || 1
16
- request = RequestProfile.new(method, uri, options)
16
+ request = RequestProfile.new(method, uri, options).with(&block)
17
17
  verifier = RequestExecutionVerifier.new(request, expected_times_executed)
18
18
  assertion_failure(verifier.failure_message) unless verifier.matches?
19
19
  end
20
20
 
21
- def assert_not_requested(method, uri, options = {})
22
- request = RequestProfile.new(method, uri, options)
21
+ def assert_not_requested(method, uri, options = {}, &block)
22
+ request = RequestProfile.new(method, uri, options).with(&block)
23
23
  verifier = RequestExecutionVerifier.new(request, options.delete(:times))
24
24
  assertion_failure(verifier.negative_failure_message) unless verifier.does_not_match?
25
25
  end
@@ -0,0 +1,20 @@
1
+ HTTP/1.1 200 OK
2
+ Content-Type: text/html; charset=UTF-8
3
+ Connection: Keep-Alive
4
+ Date: Sat, 23 Jan 2010 01:01:05 GMT
5
+ Content-Length: 438
6
+
7
+ <HTML>
8
+ <HEAD>
9
+ <TITLE>Example Web Page</TITLE>
10
+ </HEAD>
11
+ <body>
12
+ <p>You have reached this web page by typing &quot;example.com&quot;,
13
+ &quot;example.net&quot;,
14
+ or &quot;example.org&quot; into your web browser.</p>
15
+ <p>These domain names are reserved for use in documentation and are not available
16
+ for registration. See <a href="http://www.rfc-editor.org/rfc/rfc2606.txt">RFC
17
+ 2606</a>, Section 3.</p>
18
+ </BODY>
19
+ </HTML>
20
+
@@ -23,6 +23,10 @@ module HTTPClientSpecHelper
23
23
  :status => response.code.to_s })
24
24
  end
25
25
 
26
+ def default_client_request_headers(request_method = nil, has_body = false)
27
+ {'Content-Type'=>'application/x-www-form-urlencoded'} if request_method == 'POST' && has_body
28
+ end
29
+
26
30
  def setup_expectations_for_real_request(options = {})
27
31
  socket = mock("TCPSocket")
28
32
  TCPSocket.should_receive(:new).
@@ -29,6 +29,13 @@ describe "Webmock with Net:HTTP" do
29
29
  req.body = "my_params"
30
30
  Net::HTTP.start("www.example.com") { |http| http.request(req)}.body.should == "abc"
31
31
  end
32
+
33
+ it "should handle Net::HTTP::Post#body_stream" do
34
+ stub_http_request(:post, "www.example.com").with(:body => "my_params").to_return(:body => "abc")
35
+ req = Net::HTTP::Post.new("/")
36
+ req.body_stream = StringIO.new("my_params")
37
+ Net::HTTP.start("www.example.com") { |http| http.request(req)}.body.should == "abc"
38
+ end
32
39
 
33
40
  it "should behave like Net::HTTP and raise error if both request body and body argument are set" do
34
41
  stub_http_request(:post, "www.example.com").with(:body => "my_params").to_return(:body => "abc")
@@ -39,4 +46,5 @@ describe "Webmock with Net:HTTP" do
39
46
  }.should raise_error("both of body argument and HTTPRequest#body set")
40
47
  end
41
48
 
49
+
42
50
  end
@@ -16,11 +16,20 @@ module NetHTTPSpecHelper
16
16
  response = http.start {|http|
17
17
  http.request(req, options[:body], &block)
18
18
  }
19
+ headers = {}
20
+ response.each_header {|name, value| headers[name] = value}
19
21
  OpenStruct.new({
20
22
  :body => response.body,
21
- :headers => response,
23
+ :headers => WebMock::Util::Headers.normalize_headers(headers),
22
24
  :status => response.code })
23
25
  end
26
+
27
+ def default_client_request_headers(request_method = nil, has_body = false)
28
+ default_request = Net::HTTPGenericRequest.new('','','','/')
29
+ default_net_http_headers = Hash[*default_request.to_hash.map {|k,v|
30
+ [k, v.flatten]
31
+ }.flatten]
32
+ end
24
33
 
25
34
  # Sets several expectations that a real HTTP request makes it
26
35
  # past WebMock to the socket layer. You can use this when you need to check
@@ -35,6 +35,11 @@ describe RequestProfile do
35
35
  "GET http://www.example.com/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'}"
36
36
  end
37
37
 
38
+ it "should report string describing itself with block" do
39
+ RequestProfile.new(:get, "www.example.com",
40
+ :body => "abc", :headers => {'A' => 'a', 'B' => 'b'}).with {|req| true}.to_s.should ==
41
+ "GET http://www.example.com/ with body 'abc' with headers {'A'=>'a', 'B'=>'b'} with given block"
42
+ end
38
43
 
39
44
  describe "with" do
40
45
  before(:each) do
@@ -50,7 +50,7 @@ describe RequestRegistry do
50
50
  describe "response for request" do
51
51
 
52
52
  it "should registered response for request profile" do
53
- @request_stub.response = @response = Response.new
53
+ @request_stub.instance_variable_set(:@responses, [@response = Response.new])
54
54
  RequestRegistry.instance.register_request_stub(@request_stub)
55
55
  RequestRegistry.instance.response_for_request(@request_signature).should == @response
56
56
  end
@@ -61,11 +61,11 @@ describe RequestRegistry do
61
61
 
62
62
  it "should always return last registered matching response" do
63
63
  @request_stub1 = RequestStub.new(:get, "www.example.com")
64
- @request_stub1.response = @response1 = Response.new
64
+ @request_stub1.instance_variable_set(:@responses, [@response1 = Response.new])
65
65
  @request_stub2 = RequestStub.new(:get, "www.example.com")
66
- @request_stub2.response = @response2 = Response.new
66
+ @request_stub2.instance_variable_set(:@responses, [@response2 = Response.new])
67
67
  @request_stub3 = RequestStub.new(:get, "www.example.org")
68
- @request_stub3.response = @response3 = Response.new
68
+ @request_stub3.instance_variable_set(:@responses, [@response3 = Response.new])
69
69
  RequestRegistry.instance.register_request_stub(@request_stub1)
70
70
  RequestRegistry.instance.register_request_stub(@request_stub2)
71
71
  RequestRegistry.instance.register_request_stub(@request_stub3)
@@ -89,11 +89,21 @@ describe RequestSignature do
89
89
  RequestSignature.new(:get, "www.example.com", :body => "abc").
90
90
  should match(RequestProfile.new(:get, "www.example.com", :body => "abc"))
91
91
  end
92
+
93
+ it "should match for body matching regexp" do
94
+ RequestSignature.new(:get, "www.example.com", :body => "abc").
95
+ should match(RequestProfile.new(:get, "www.example.com", :body => /^abc$/))
96
+ end
92
97
 
93
98
  it "should not match for different bodies" do
94
99
  RequestSignature.new(:get, "www.example.com", :body => "abc").
95
100
  should_not match(RequestProfile.new(:get, "www.example.com", :body => "def"))
96
101
  end
102
+
103
+ it "should not match for body not matching regexp" do
104
+ RequestSignature.new(:get, "www.example.com", :body => "xabc").
105
+ should_not match(RequestProfile.new(:get, "www.example.com", :body => /^abc$/))
106
+ end
97
107
 
98
108
  it "should match if other has not specified body" do
99
109
  RequestSignature.new(:get, "www.example.com", :body => "abc").
@@ -119,11 +129,21 @@ describe RequestSignature do
119
129
  RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
120
130
  should match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}))
121
131
  end
132
+
133
+ it "should match for header values matching regexp" do
134
+ RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
135
+ should match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => %r{^image/jpeg$}}))
136
+ end
122
137
 
123
138
  it "should not match for different values of the same header" do
124
139
  RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg'}).
125
140
  should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/png'}))
126
141
  end
142
+
143
+ it "should not match for header values not matching regexp" do
144
+ RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpegx'}).
145
+ should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'Content-Type' => %r{^image\/jpeg$}}))
146
+ end
127
147
 
128
148
  it "should match if request has more headers than other" do
129
149
  RequestSignature.new(:get, "www.example.com", :headers => {'Content-Type' => 'image/jpeg', 'Content-Length' => '8888'}).
@@ -165,6 +185,21 @@ describe RequestSignature do
165
185
  should_not match(RequestProfile.new(:get, "www.example.com", :headers => {'A'=>'a'}))
166
186
  end
167
187
 
188
+ it "should match if block given to profile evaluates on request to true" do
189
+ RequestSignature.new(:get, "www.example.com").
190
+ should match(RequestProfile.new(:get, "www.example.com").with { |request| true } )
191
+ end
192
+
193
+ it "should not match if block given to profile evaluates on request to false" do
194
+ RequestSignature.new(:get, "www.example.com").
195
+ should_not match( RequestProfile.new(:get, "www.example.com").with { |request| false } )
196
+ end
197
+
198
+ it "should pass self to the block when matching" do
199
+ signature = RequestSignature.new(:get, "www.example.com")
200
+ signature.should match(RequestProfile.new(:get, "www.example.com").with { |request| request == signature } )
201
+ end
202
+
168
203
  end
169
204
 
170
205
  end
@@ -28,6 +28,11 @@ describe RequestStub do
28
28
  @request_stub.request_profile.headers.should == {'B' => 'b'}
29
29
  end
30
30
 
31
+ it "should assign given block to request profile" do
32
+ @request_stub.with { |req| "block output" }
33
+ @request_stub.request_profile.with_block.call(nil).should == "block output"
34
+ end
35
+
31
36
  end
32
37
 
33
38
  describe "to_return" do
@@ -38,6 +43,46 @@ describe RequestStub do
38
43
  @request_stub.response.status.should == 500
39
44
  end
40
45
 
46
+ it "should assign responses with provided options" do
47
+ @request_stub.to_return([{:body => "abc"}, {:body => "def"}])
48
+ [@request_stub.response.body, @request_stub.response.body].should == ["abc", "def"]
49
+ end
50
+
51
+ end
52
+
53
+ describe "then" do
54
+ it "should return stub without any modifications, acting as syntactic sugar" do
55
+ @request_stub.then.should == @request_stub
56
+ end
57
+ end
58
+
59
+ describe "response" do
60
+
61
+ it "should return responses in a sequence passed as array" do
62
+ @request_stub.to_return([{:body => "abc"}, {:body => "def"}])
63
+ @request_stub.response.body.should == "abc"
64
+ @request_stub.response.body.should == "def"
65
+ end
66
+
67
+ it "should repeat returning last response" do
68
+ @request_stub.to_return([{:body => "abc"}, {:body => "def"}])
69
+ @request_stub.response
70
+ @request_stub.response
71
+ @request_stub.response.body.should == "def"
72
+ end
73
+
74
+ it "should return responses in a sequence passed as comma separated params" do
75
+ @request_stub.to_return({:body => "abc"}, {:body => "def"})
76
+ @request_stub.response.body.should == "abc"
77
+ @request_stub.response.body.should == "def"
78
+ end
79
+
80
+ it "should return responses declared in multiple to_return declarations" do
81
+ @request_stub.to_return({:body => "abc"}).to_return({:body => "def"})
82
+ @request_stub.response.body.should == "abc"
83
+ @request_stub.response.body.should == "def"
84
+ end
85
+
41
86
  end
42
87
 
43
88
  describe "to_raise" do
@@ -48,7 +93,85 @@ describe RequestStub do
48
93
  @request_stub.response.raise_error_if_any
49
94
  }.should raise_error(ArgumentError, "Exception from WebMock")
50
95
  end
96
+
97
+ it "should assign sequence of responses with response with exception to be thrown" do
98
+ @request_stub.to_return(:body => "abc").then.to_raise(ArgumentError)
99
+ @request_stub.response.body.should == "abc"
100
+ lambda {
101
+ @request_stub.response.raise_error_if_any
102
+ }.should raise_error(ArgumentError, "Exception from WebMock")
103
+ end
51
104
 
105
+ it "should assign a list responses to be thrown in a sequence" do
106
+ @request_stub.to_raise(ArgumentError, IndexError)
107
+ lambda {
108
+ @request_stub.response.raise_error_if_any
109
+ }.should raise_error(ArgumentError, "Exception from WebMock")
110
+ lambda {
111
+ @request_stub.response.raise_error_if_any
112
+ }.should raise_error(IndexError, "Exception from WebMock")
113
+ end
114
+
115
+ it "should raise exceptions declared in multiple to_raise declarations" do
116
+ @request_stub.to_raise(ArgumentError).then.to_raise(IndexError)
117
+ lambda {
118
+ @request_stub.response.raise_error_if_any
119
+ }.should raise_error(ArgumentError, "Exception from WebMock")
120
+ lambda {
121
+ @request_stub.response.raise_error_if_any
122
+ }.should raise_error(IndexError, "Exception from WebMock")
123
+ end
124
+
125
+ end
126
+
127
+
128
+ describe "times" do
129
+
130
+ it "should give error if declared before any response declaration is declared" do
131
+ lambda {
132
+ @request_stub.times(3)
133
+ }.should raise_error("Invalid WebMock stub declaration. times(N) can be declared only after response declaration.")
134
+ end
135
+
136
+ it "should repeat returning last declared response declared number of times" do
137
+ @request_stub.to_return({:body => "abc"}).times(2).then.to_return({:body => "def"})
138
+ @request_stub.response.body.should == "abc"
139
+ @request_stub.response.body.should == "abc"
140
+ @request_stub.response.body.should == "def"
141
+ end
142
+
143
+ it "should repeat raising last declared exception declared number of times" do
144
+ @request_stub.to_return({:body => "abc"}).times(2).then.to_return({:body => "def"})
145
+ @request_stub.response.body.should == "abc"
146
+ @request_stub.response.body.should == "abc"
147
+ @request_stub.response.body.should == "def"
148
+ end
149
+
150
+ it "should repeat returning last declared sequence of responses declared number of times" do
151
+ @request_stub.to_return({:body => "abc"}, {:body => "def"}).times(2).then.to_return({:body => "ghj"})
152
+ @request_stub.response.body.should == "abc"
153
+ @request_stub.response.body.should == "def"
154
+ @request_stub.response.body.should == "abc"
155
+ @request_stub.response.body.should == "def"
156
+ @request_stub.response.body.should == "ghj"
157
+ end
158
+
159
+ it "should return self" do
160
+ @request_stub.to_return({:body => "abc"}).times(1).should == @request_stub
161
+ end
162
+
163
+ it "should raise error if argument is not integer" do
164
+ lambda {
165
+ @request_stub.to_return({:body => "abc"}).times("not number")
166
+ }.should raise_error("times(N) accepts integers >= 1 only")
167
+ end
168
+
169
+ it "should raise error if argument is < 1" do
170
+ lambda {
171
+ @request_stub.to_return({:body => "abc"}).times(0)
172
+ }.should raise_error("times(N) accepts integers >= 1 only")
173
+ end
174
+
52
175
  end
53
176
 
54
177
  end