typhoeus 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Filter do
4
+ it "should take a method name and optionally take options" do
5
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
6
+ filter = Typhoeus::Filter.new(:bar)
7
+ end
8
+
9
+ describe "#apply_filter?" do
10
+ it "should return true for any method when :only and :except aren't specified" do
11
+ filter = Typhoeus::Filter.new(:bar)
12
+ filter.apply_filter?(:asdf).should be_true
13
+ end
14
+
15
+ it "should return true if a method is in only" do
16
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
17
+ filter.apply_filter?(:foo).should be_true
18
+ end
19
+
20
+ it "should return false if a method isn't in only" do
21
+ filter = Typhoeus::Filter.new(:bar, :only => :foo)
22
+ filter.apply_filter?(:bar).should be_false
23
+ end
24
+
25
+ it "should return true if a method isn't in except" do
26
+ filter = Typhoeus::Filter.new(:bar, :except => :foo)
27
+ filter.apply_filter?(:bar).should be_true
28
+ end
29
+
30
+ it "should return false if a method is in except" do
31
+ filter = Typhoeus::Filter.new(:bar, :except => :foo)
32
+ filter.apply_filter?(:foo).should be_false
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,82 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::Easy do
4
+ before(:all) do
5
+ @pid = start_method_server(3002)
6
+ end
7
+
8
+ after(:all) do
9
+ stop_method_server(@pid)
10
+ end
11
+
12
+ it "should save easy handles that get added" do
13
+ multi = Typhoeus::Multi.new
14
+ easy = Typhoeus::Easy.new
15
+ easy.url = "http://localhost:3002"
16
+ easy.method = :get
17
+
18
+ multi.add(easy)
19
+ multi.easy_handles.should == [easy]
20
+ multi.perform
21
+ multi.easy_handles.should == []
22
+ end
23
+
24
+ it "should be reusable" do
25
+ easy = Typhoeus::Easy.new
26
+ easy.url = "http://localhost:3002"
27
+ easy.method = :get
28
+
29
+ multi = Typhoeus::Multi.new
30
+ multi.add(easy)
31
+ multi.perform
32
+ easy.response_code.should == 200
33
+ easy.response_body.should include("METHOD=GET")
34
+
35
+ e2 = Typhoeus::Easy.new
36
+ e2.url = "http://localhost:3002"
37
+ e2.method = :post
38
+ multi.add(e2)
39
+ multi.perform
40
+
41
+ e2.response_code.should == 200
42
+ e2.response_body.should include("METHOD=POST")
43
+ end
44
+
45
+ it "should perform easy handles added after the first one runs" do
46
+ easy = Typhoeus::Easy.new
47
+ easy.url = "http://localhost:3002"
48
+ easy.method = :get
49
+ multi = Typhoeus::Multi.new
50
+ multi.add(easy)
51
+
52
+ e2 = Typhoeus::Easy.new
53
+ e2.url = "http://localhost:3002"
54
+ e2.method = :post
55
+ easy.on_success do |e|
56
+ multi.add(e2)
57
+ end
58
+
59
+ multi.perform
60
+ easy.response_code.should == 200
61
+ easy.response_body.should include("METHOD=GET")
62
+ e2.response_code.should == 200
63
+ e2.response_body.should include("METHOD=POST")
64
+ end
65
+
66
+ # it "should do multiple gets" do
67
+ # multi = Typhoeus::Multi.new
68
+ #
69
+ # handles = []
70
+ # 5.times do |i|
71
+ # easy = Typhoeus::Easy.new
72
+ # easy.url = "http://localhost:3002"
73
+ # easy.method = :get
74
+ # easy.on_success {|e| puts "get #{i} succeeded"}
75
+ # easy.on_failure {|e| puts "get #{i} failed with #{e.response_code}"}
76
+ # handles << easy
77
+ # multi.add(easy)
78
+ # end
79
+ #
80
+ # multi.perform
81
+ # end
82
+ end
@@ -0,0 +1,141 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::RemoteMethod do
4
+ it "should take options" do
5
+ Typhoeus::RemoteMethod.new(:body => "foo")
6
+ end
7
+
8
+ describe "http_method" do
9
+ it "should return the http method" do
10
+ m = Typhoeus::RemoteMethod.new(:method => :put)
11
+ m.http_method.should == :put
12
+ end
13
+
14
+ it "should default to :get" do
15
+ m = Typhoeus::RemoteMethod.new
16
+ m.http_method.should == :get
17
+ end
18
+ end
19
+
20
+ it "should return the options" do
21
+ m = Typhoeus::RemoteMethod.new(:body => "foo")
22
+ m.options.should == {:body => "foo"}
23
+ end
24
+
25
+ it "should pull uri out of the options hash" do
26
+ m = Typhoeus::RemoteMethod.new(:base_uri => "http://pauldix.net")
27
+ m.base_uri.should == "http://pauldix.net"
28
+ m.options.should_not have_key(:base_uri)
29
+ end
30
+
31
+ describe "on_success" do
32
+ it "should return the block" do
33
+ m = Typhoeus::RemoteMethod.new(:on_success => lambda {:foo})
34
+ m.on_success.call.should == :foo
35
+ end
36
+ end
37
+
38
+ describe "on_failure" do
39
+ it "should return method name" do
40
+ m = Typhoeus::RemoteMethod.new(:on_failure => lambda {:bar})
41
+ m.on_failure.call.should == :bar
42
+ end
43
+ end
44
+
45
+ describe "path" do
46
+ it "should pull path out of the options hash" do
47
+ m = Typhoeus::RemoteMethod.new(:path => "foo")
48
+ m.path.should == "foo"
49
+ m.options.should_not have_key(:path)
50
+ end
51
+
52
+ it "should output argument names from the symbols in the path" do
53
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/:post_id/comments/:comment_id")
54
+ m.argument_names.should == [:post_id, :comment_id]
55
+ end
56
+
57
+ it "should output an empty string when there are no arguments in path" do
58
+ m = Typhoeus::RemoteMethod.new(:path => "/default.html")
59
+ m.argument_names.should == []
60
+ end
61
+
62
+ it "should output and empty string when there is no path specified" do
63
+ m = Typhoeus::RemoteMethod.new
64
+ m.argument_names.should == []
65
+ end
66
+
67
+ it "should interpolate a path with arguments" do
68
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/:post_id/comments/:comment_id")
69
+ m.interpolate_path_with_arguments(:post_id => 1, :comment_id => "asdf").should == "/posts/1/comments/asdf"
70
+ end
71
+
72
+ it "should provide the path when interpolated called and there is nothing to interpolate" do
73
+ m = Typhoeus::RemoteMethod.new(:path => "/posts/123")
74
+ m.interpolate_path_with_arguments(:foo => :bar).should == "/posts/123"
75
+ end
76
+ end
77
+
78
+ describe "#merge_options" do
79
+ it "should keep the passed in options first" do
80
+ m = Typhoeus::RemoteMethod.new("User-Agent" => "whatev", :foo => :bar)
81
+ m.merge_options({"User-Agent" => "http-machine"}).should == {"User-Agent" => "http-machine", :foo => :bar}
82
+ end
83
+
84
+ it "should combine the params" do
85
+ m = Typhoeus::RemoteMethod.new(:foo => :bar, :params => {:id => :asdf})
86
+ m.merge_options({:params => {:desc => :jkl}}).should == {:foo => :bar, :params => {:id => :asdf, :desc => :jkl}}
87
+ end
88
+ end
89
+
90
+ describe "memoize_reponses" do
91
+ before(:each) do
92
+ @m = Typhoeus::RemoteMethod.new(:memoize_responses => true)
93
+ @args = ["foo", "bar"]
94
+ @options = {:asdf => {:jkl => :bar}}
95
+ end
96
+
97
+ it "should store if responses should be memoized" do
98
+ @m.memoize_responses?.should be_true
99
+ @m.options.should == {}
100
+ end
101
+
102
+ it "should tell when a method is already called" do
103
+ @m.already_called?(@args, @options).should be_false
104
+ @m.calling(@args, @options)
105
+ @m.already_called?(@args, @options).should be_true
106
+ @m.already_called?([], {}).should be_false
107
+ end
108
+
109
+ it "should call response blocks and clear the methods that have been called" do
110
+ response_block_called = mock('response_block')
111
+ response_block_called.should_receive(:call).exactly(1).times
112
+
113
+ @m.add_response_block(lambda {|res| res.should == :foo; response_block_called.call}, @args, @options)
114
+ @m.calling(@args, @options)
115
+ @m.call_response_blocks(:foo, @args, @options)
116
+ @m.already_called?(@args, @options).should be_false
117
+ @m.call_response_blocks(:asdf, @args, @options) #just to make sure it doesn't actually call that block again
118
+ end
119
+ end
120
+
121
+ describe "cache_reponses" do
122
+ before(:each) do
123
+ @m = Typhoeus::RemoteMethod.new(:cache_responses => true)
124
+ @args = ["foo", "bar"]
125
+ @options = {:asdf => {:jkl => :bar}}
126
+ end
127
+
128
+ it "should store if responses should be cached" do
129
+ @m.cache_responses?.should be_true
130
+ @m.options.should == {}
131
+ end
132
+
133
+ it "should force memoization if caching is enabled" do
134
+ @m.memoize_responses?.should be_true
135
+ end
136
+
137
+ it "should store cache ttl" do
138
+ Typhoeus::RemoteMethod.new(:cache_responses => 30).cache_ttl.should == 30
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,73 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus::RemoteProxyObject do
4
+ before(:each) do
5
+ @easy = Typhoeus::Easy.new
6
+ @easy.method = :get
7
+ @easy.url = "http://localhost:3001"
8
+ end
9
+
10
+ before(:all) do
11
+ @pid = start_method_server(3001)
12
+ end
13
+
14
+ after(:all) do
15
+ stop_method_server(@pid)
16
+ end
17
+
18
+ it "should take a caller and call the clear_memoized_proxy_objects" do
19
+ clear_proxy = lambda {}
20
+ clear_proxy.should_receive(:call)
21
+ response = Typhoeus::RemoteProxyObject.new(clear_proxy, @easy)
22
+ response.code.should == 200
23
+ end
24
+
25
+ it "should take an easy object and return the body when requested" do
26
+ response = Typhoeus::RemoteProxyObject.new(lambda {}, @easy)
27
+ @easy.response_code.should == 0
28
+ response.code.should == 200
29
+ end
30
+
31
+ it "should perform requests only on the first access" do
32
+ response = Typhoeus::RemoteProxyObject.new(lambda {}, @easy)
33
+ response.code.should == 200
34
+ Typhoeus.should_receive(:perform_easy_requests).exactly(0).times
35
+ response.code.should == 200
36
+ end
37
+
38
+ it "should set the requested_url and requested_http_method on the response" do
39
+ response = Typhoeus::RemoteProxyObject.new(lambda {}, @easy)
40
+ response.requested_url.should == "http://localhost:3001"
41
+ response.requested_http_method.should == :get
42
+ end
43
+
44
+ it "should call the on_success method with an easy object and proxy to the result of on_success" do
45
+ klass = Class.new do
46
+ def initialize(r)
47
+ @response = r
48
+ end
49
+
50
+ def blah
51
+ @response.code
52
+ end
53
+ end
54
+
55
+ k = Typhoeus::RemoteProxyObject.new(lambda {}, @easy, :on_success => lambda {|e| klass.new(e)})
56
+ k.blah.should == 200
57
+ end
58
+
59
+ it "should call the on_failure method with an easy object and proxy to the result of on_failure" do
60
+ klass = Class.new do
61
+ def initialize(r)
62
+ @response = r
63
+ end
64
+
65
+ def blah
66
+ @response.code
67
+ end
68
+ end
69
+ @easy.url = "http://localhost:3002" #bad port
70
+ k = Typhoeus::RemoteProxyObject.new(lambda {}, @easy, :on_failure => lambda {|e| klass.new(e)})
71
+ k.blah.should == 0
72
+ end
73
+ end
@@ -0,0 +1,699 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe Typhoeus do
4
+ before(:each) do
5
+ @klass = Class.new do
6
+ include Typhoeus
7
+ end
8
+ end
9
+
10
+ before(:all) do
11
+ @pid = start_method_server(3001)
12
+ end
13
+
14
+ after(:all) do
15
+ stop_method_server(@pid)
16
+ end
17
+
18
+ describe "entirely disallowing HTTP connections in specs" do
19
+ describe "allow_net_connect" do
20
+ it "should default to true" do
21
+ @klass.allow_net_connect.should be_true
22
+ end
23
+
24
+ it "should be settable" do
25
+ @klass.allow_net_connect.should be_true
26
+ @klass.allow_net_connect = false
27
+ @klass.allow_net_connect.should be_false
28
+ end
29
+ end
30
+
31
+ describe "and hitting a URL that hasn't been mocked" do
32
+ it "should raise an error for any HTTP verbs" do
33
+ @klass.allow_net_connect = false
34
+
35
+ [:get, :put, :post, :delete].each do |method|
36
+ lambda {
37
+ @klass.send(method, "http://crazy_url_that_isnt_mocked.com")
38
+ }.should raise_error(Typhoeus::MockExpectedError)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "hitting a mocked URL that returns false" do
44
+ it "should not raise a MockExpectedError" do
45
+ @klass.allow_net_connect = false
46
+ @klass.mock(:delete,
47
+ :url => "http://test.com",
48
+ :code => 500,
49
+ :body => 'ok')
50
+
51
+ lambda {
52
+ @klass.delete("http://test.com",
53
+ :on_failure => lambda { |response| false })
54
+ }.should_not raise_error
55
+ end
56
+ end
57
+
58
+ describe "handlers" do
59
+ it "should be able to return nil as part of their block" do
60
+ @klass.allow_net_connect = false
61
+ url = 'http://api.mysite.com/v1/stuff.json'
62
+ @klass.mock(:get,
63
+ :url => url,
64
+ :body => '',
65
+ :code => 500)
66
+ result = @klass.get(url,
67
+ :on_failure => lambda { |response| nil })
68
+ result.should be_nil
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "mocking" do
74
+ it "should mock out GET" do
75
+ @klass.mock(:get)
76
+ response = @klass.get("http://mock_url")
77
+ response.code.should == 200
78
+ response.body.should == ""
79
+ response.headers.should == ""
80
+ response.time.should == 0
81
+ end
82
+
83
+ it "should mock out PUT" do
84
+ @klass.mock(:put)
85
+ response = @klass.put("http://mock_url")
86
+ response.code.should == 200
87
+ response.body.should == ""
88
+ response.headers.should == ""
89
+ response.time.should == 0
90
+ end
91
+
92
+ it "should mock out POST" do
93
+ @klass.mock(:post)
94
+ response = @klass.post("http://mock_url")
95
+ response.code.should == 200
96
+ response.body.should == ""
97
+ response.headers.should == ""
98
+ response.time.should == 0
99
+ end
100
+
101
+ it "should mock out DELETE" do
102
+ @klass.mock(:delete)
103
+ response = @klass.delete("http://mock_url")
104
+ response.code.should == 200
105
+ response.body.should == ""
106
+ response.headers.should == ""
107
+ response.time.should == 0
108
+ end
109
+
110
+ it "should take an optional url" do
111
+ @klass.mock(:get, :url => "http://stuff")
112
+ response = @klass.get("http://stuff")
113
+ response.code.should == 200
114
+ response.body.should == ""
115
+ response.headers.should == ""
116
+ response.time.should == 0
117
+
118
+ @klass.get("http://localhost:234234234").code.should == 0
119
+ end
120
+
121
+ it "should be able to mock using specific params as well" do
122
+ @klass.allow_net_connect = false
123
+ @klass.mock(:get, :url => "http://stuff")
124
+
125
+ lambda {
126
+ @klass.get("http://stuff", :params => { :a => 'test' })
127
+ }.should raise_error(Typhoeus::MockExpectedError)
128
+
129
+ @klass.mock(:get,
130
+ :url => "http://stuff",
131
+ :params => { :a => 'test' })
132
+ lambda {
133
+ @klass.get("http://stuff", :params => { :a => 'test' })
134
+ }.should_not raise_error(Typhoeus::MockExpectedError)
135
+ end
136
+
137
+ describe "request body expectations" do
138
+ before(:all) do
139
+ @body_klass = Class.new do
140
+ include Typhoeus
141
+ end
142
+ @body_klass.mock(:put, :url => "http://whatev", :expected_body => "hi")
143
+ end
144
+
145
+ it "should take an expected request body" do
146
+ @body_klass.put("http://whatev", :body => "hi").code.should == 200
147
+ end
148
+
149
+ it "should raise if the expected request body doesn't match" do
150
+ lambda {
151
+ @body_klass.put("http://whatev", :body => "not what we expect")
152
+ }.should raise_error
153
+ end
154
+ end
155
+
156
+ describe "check_expected_headers!" do
157
+ before(:each) do
158
+ @header_klass = Class.new do
159
+ include Typhoeus
160
+ end
161
+ end
162
+
163
+ it "should match a header with :anything" do
164
+ lambda {
165
+ @header_klass.check_expected_headers!(
166
+ { :expected_headers => { 'Content-Type' => :anything } },
167
+ { :headers => { 'Content-Type' => 'text/xml' } }
168
+ )
169
+ }.should_not raise_error
170
+ end
171
+
172
+ it "should enforce exact matching" do
173
+ lambda {
174
+ @header_klass.check_expected_headers!(
175
+ { :expected_headers => { 'Content-Type' => 'text/html' } },
176
+ { :headers => { 'Content-Type' => 'text/xml' } }
177
+ )
178
+ }.should raise_error
179
+ end
180
+ end
181
+
182
+ describe "check_unexpected_headers!" do
183
+ before(:each) do
184
+ @header_klass = Class.new do
185
+ include Typhoeus
186
+ end
187
+ end
188
+
189
+ it "should match a header with :anything" do
190
+ lambda {
191
+ @header_klass.check_unexpected_headers!(
192
+ { :unexpected_headers => { 'Content-Type' => :anything } },
193
+ { :headers => { "Content-Type" => "text/xml" } }
194
+ )
195
+ }.should raise_error
196
+ end
197
+
198
+ it "should not match if a header is different from the expected value" do
199
+ lambda {
200
+ @header_klass.check_unexpected_headers!(
201
+ { :unexpected_headers => { 'Content-Type' => 'text/html' } },
202
+ { :headers => { "Content-Type" => "text/xml" } }
203
+ )
204
+ }.should_not raise_error
205
+ end
206
+ end
207
+
208
+ describe "request header expectations" do
209
+ before(:all) do
210
+ @header_klass = Class.new do
211
+ include Typhoeus
212
+ end
213
+ @header_klass.mock(:get,
214
+ :url => "http://asdf",
215
+ :expected_headers => {"If-None-Match" => "\"lkjsd90823\""},
216
+ :unexpected_headers => { 'Content-Type' => "text/xml" })
217
+ end
218
+
219
+ it "should take expected request headers" do
220
+ @header_klass.get("http://asdf", :headers => {"If-None-Match" => "\"lkjsd90823\""})
221
+ end
222
+
223
+ it "should raise if the expected request headers don't match" do
224
+ lambda {
225
+ @header_klass.get("http://asdf")
226
+ }.should raise_error
227
+ end
228
+
229
+ it "should raise if an unexpected header shows up" do
230
+ lambda {
231
+ @header_klass.get("http://asdf",
232
+ :headers => { "Content-Type" => "text/xml" })
233
+ }.should raise_error
234
+ end
235
+ end
236
+
237
+ describe "remote methods" do
238
+ it "should work for defined remote methods" do
239
+ @klass.instance_eval do
240
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :on_success => lambda {|r| r.body.should == "hi"; :great_success}
241
+ end
242
+ @klass.mock(:get, :url => "http://localhost:3001", :body => "hi")
243
+ @klass.do_stuff.should == :great_success
244
+ end
245
+
246
+ it "should call the on failure handler for remote methods" do
247
+ @klass.instance_eval do
248
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :on_failure => lambda {|r| r.body.should == "hi"; :fail}
249
+ end
250
+ @klass.mock(:get, :url => "http://localhost:3001", :body => "hi", :code => 500)
251
+ @klass.do_stuff.should == :fail
252
+ end
253
+
254
+ it "should allow for subclassing a class that includes Typhoeus, and merging defaults" do
255
+ class TestA
256
+ include Typhoeus
257
+ remote_defaults :on_failure => lambda { |response|
258
+ :fail
259
+ }
260
+ end
261
+
262
+ class TestB < TestA
263
+ remote_defaults :base_uri => "http://localhost"
264
+ define_remote_method :do_stuff
265
+ end
266
+
267
+ TestB.mock(:get, :url => "http://localhost", :body => "hi", :code => 500)
268
+ TestB.do_stuff.should == :fail
269
+ end
270
+ end
271
+
272
+ describe "response hash" do
273
+ it "should use provided code" do
274
+ @klass.mock(:get, :url => "http://localhost/whatever", :code => 301)
275
+ response = @klass.get("http://localhost/whatever")
276
+ response.code.should == 301
277
+ response.body.should == ""
278
+ response.headers.should == ""
279
+ response.time.should == 0
280
+ end
281
+
282
+ it "should use provided body" do
283
+ @klass.mock(:get, :url => "http://localhost/whatever", :body => "hey paul")
284
+ response = @klass.get("http://localhost/whatever")
285
+ response.code.should == 200
286
+ response.body.should == "hey paul"
287
+ response.headers.should == ""
288
+ response.time.should == 0
289
+ end
290
+
291
+ it "should use provided headers" do
292
+ @klass.mock(:get, :url => "http://localhost/whatever", :headers => "whooo, headers!")
293
+ response = @klass.get("http://localhost/whatever")
294
+ response.code.should == 200
295
+ response.body.should == ""
296
+ response.headers.should == "whooo, headers!"
297
+ response.time.should == 0
298
+ end
299
+
300
+ it "should use provided time" do
301
+ @klass.mock(:get, :url => "http://localhost/whatever", :time => 123)
302
+ response = @klass.get("http://localhost/whatever")
303
+ response.code.should == 200
304
+ response.body.should == ""
305
+ response.headers.should == ""
306
+ response.time.should == 123
307
+ end
308
+ end
309
+ end
310
+
311
+ describe "get" do
312
+ it "should add a get method" do
313
+ easy = @klass.get("http://localhost:3001/posts.xml")
314
+ easy.code.should == 200
315
+ easy.body.should include("REQUEST_METHOD=GET")
316
+ easy.body.should include("REQUEST_URI=/posts.xml")
317
+ end
318
+
319
+ it "should take passed in params and add them to the query string" do
320
+ easy = @klass.get("http://localhost:3001", {:params => {:foo => :bar}})
321
+ easy.body.should include("QUERY_STRING=foo=bar")
322
+ end
323
+ end # get
324
+
325
+ describe "post" do
326
+ it "should add a post method" do
327
+ easy = @klass.post("http://localhost:3001/posts.xml", {:params => {:post => {:author => "paul", :title => "a title", :body => "a body"}}})
328
+ easy.code.should == 200
329
+ easy.body.should include("post%5Bbody%5D=a+body")
330
+ easy.body.should include("post%5Bauthor%5D=paul")
331
+ easy.body.should include("post%5Btitle%5D=a+title")
332
+ easy.body.should include("REQUEST_METHOD=POST")
333
+ end
334
+
335
+ it "should add a body" do
336
+ easy = @klass.post("http://localhost:3001/posts.xml", {:body => "this is a request body"})
337
+ easy.code.should == 200
338
+ easy.body.should include("this is a request body")
339
+ easy.body.should include("REQUEST_METHOD=POST")
340
+ end
341
+ end # post
342
+
343
+ it "should add a put method" do
344
+ easy = @klass.put("http://localhost:3001/posts/3.xml")
345
+ easy.code.should == 200
346
+ easy.body.should include("REQUEST_METHOD=PUT")
347
+ end
348
+
349
+ it "should add a delete method" do
350
+ easy = @klass.delete("http://localhost:3001/posts/3.xml")
351
+ easy.code.should == 200
352
+ easy.body.should include("REQUEST_METHOD=DELETE")
353
+ end
354
+
355
+ describe "#define_remote_method" do
356
+ before(:each) do
357
+ @klass = Class.new do
358
+ include Typhoeus
359
+ end
360
+ end
361
+
362
+ describe "defined methods" do
363
+ before(:each) do
364
+ @klass.instance_eval do
365
+ define_remote_method :do_stuff
366
+ end
367
+ end
368
+
369
+ it "should take a method name as the first argument and define that as a class method" do
370
+ @klass.should respond_to(:do_stuff)
371
+ end
372
+
373
+ it "should optionally take arguments" do
374
+ @klass.should_receive(:get)
375
+ @klass.do_stuff
376
+ end
377
+
378
+ it "should take arguments" do
379
+ @klass.should_receive(:get).with("", {:params=>{:foo=>"bar"}, :body=>"whatever"})
380
+ @klass.do_stuff(:params => {:foo => "bar"}, :body => "whatever")
381
+ end
382
+ end
383
+
384
+ describe "base_uri" do
385
+ it "should take a :uri as an argument" do
386
+ @klass.instance_eval do
387
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net"
388
+ end
389
+
390
+ @klass.should_receive(:get).with("http://pauldix.net", {})
391
+ @klass.do_stuff
392
+ end
393
+
394
+ it "should use default_base_uri if no base_uri provided" do
395
+ @klass.instance_eval do
396
+ remote_defaults :base_uri => "http://kgb.com"
397
+ define_remote_method :do_stuff
398
+ end
399
+
400
+ @klass.should_receive(:get).with("http://kgb.com", {})
401
+ @klass.do_stuff
402
+ end
403
+
404
+ it "should override default_base_uri if uri argument is provided" do
405
+ @klass.instance_eval do
406
+ remote_defaults :base_uri => "http://kgb.com"
407
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net"
408
+ end
409
+
410
+ @klass.should_receive(:get).with("http://pauldix.net", {})
411
+ @klass.do_stuff
412
+ end
413
+ end
414
+
415
+ describe "path" do
416
+ it "should take :path as an argument" do
417
+ @klass.instance_eval do
418
+ define_remote_method :do_stuff, :base_uri => "http://kgb.com", :path => "/default.html"
419
+ end
420
+
421
+ @klass.should_receive(:get).with("http://kgb.com/default.html", {})
422
+ @klass.do_stuff
423
+ end
424
+
425
+ it "should use deafult_path if no path provided" do
426
+ @klass.instance_eval do
427
+ remote_defaults :path => "/index.html"
428
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net"
429
+ end
430
+
431
+ @klass.should_receive(:get).with("http://pauldix.net/index.html", {})
432
+ @klass.do_stuff
433
+ end
434
+
435
+ it "should orverride default_path if path argument is provided" do
436
+ @klass.instance_eval do
437
+ remote_defaults :path => "/index.html"
438
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net", :path => "/foo.html"
439
+ end
440
+
441
+ @klass.should_receive(:get).with("http://pauldix.net/foo.html", {})
442
+ @klass.do_stuff
443
+ end
444
+
445
+ it "should map symbols in path to arguments for the remote method" do
446
+ @klass.instance_eval do
447
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net", :path => "/posts/:post_id/comments/:comment_id"
448
+ end
449
+
450
+ @klass.should_receive(:get).with("http://pauldix.net/posts/foo/comments/bar", {})
451
+ @klass.do_stuff(:post_id => "foo", :comment_id => "bar")
452
+ end
453
+
454
+ it "should use a path passed into the remote method call" do
455
+ @klass.instance_eval do
456
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net"
457
+ end
458
+
459
+ @klass.should_receive(:get).with("http://pauldix.net/whatev?asdf=foo", {})
460
+ @klass.do_stuff(:path => "/whatev?asdf=foo")
461
+ end
462
+ end
463
+
464
+ describe "method" do
465
+ it "should take :method as an argument" do
466
+ @klass.instance_eval do
467
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net", :method => :put
468
+ end
469
+
470
+ @klass.should_receive(:put).with("http://pauldix.net", {})
471
+ @klass.do_stuff
472
+ end
473
+
474
+ it "should use :get if no method or default_method exists" do
475
+ @klass.instance_eval do
476
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net"
477
+ end
478
+
479
+ @klass.should_receive(:get).with("http://pauldix.net", {})
480
+ @klass.do_stuff
481
+ end
482
+
483
+ it "should use default_method if no method provided" do
484
+ @klass.instance_eval do
485
+ remote_defaults :method => :delete
486
+ define_remote_method :do_stuff, :base_uri => "http://kgb.com"
487
+ end
488
+
489
+ @klass.should_receive(:delete).with("http://kgb.com", {})
490
+ @klass.do_stuff
491
+ end
492
+
493
+ it "should override deafult_method if method argument is provided" do
494
+ @klass.instance_eval do
495
+ remote_defaults :method => :put
496
+ define_remote_method :do_stuff, :base_uri => "http://pauldix.net", :method => :post
497
+ end
498
+
499
+ @klass.should_receive(:post).with("http://pauldix.net", {})
500
+ @klass.do_stuff
501
+ end
502
+ end
503
+
504
+ describe "on_success" do
505
+ it "should take :on_success as an argument" do
506
+ @klass.instance_eval do
507
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :on_success => lambda {|e| e.code.should == 200; :foo}
508
+ end
509
+
510
+ @klass.do_stuff.should == :foo
511
+ end
512
+
513
+ it "should use default_on_success if no on_success provided" do
514
+ @klass.instance_eval do
515
+ remote_defaults :on_success => lambda {|e| e.code.should == 200; :foo}
516
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001"
517
+ end
518
+
519
+ @klass.do_stuff.should == :foo
520
+ end
521
+
522
+ it "should override default_on_success if on_success is provided" do
523
+ @klass.instance_eval do
524
+ remote_defaults :on_success => lambda {|e| :foo}
525
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :on_success => lambda {|e| e.code.should == 200; :bar}
526
+ end
527
+
528
+ @klass.do_stuff.should == :bar
529
+ end
530
+ end
531
+
532
+ describe "on_failure" do
533
+ it "should take :on_failure as an argument" do
534
+ @klass.instance_eval do
535
+ define_remote_method :do_stuff, :base_uri => "http://localhost:9999", :on_failure => lambda {|e| e.code.should == 0; :foo}
536
+ end
537
+
538
+ @klass.do_stuff.should == :foo
539
+ end
540
+
541
+ it "should use default_on_failure if no on_success provided" do
542
+ @klass.instance_eval do
543
+ remote_defaults :on_failure => lambda {|e| e.code.should == 0; :bar}
544
+ define_remote_method :do_stuff, :base_uri => "http://localhost:9999"
545
+ end
546
+
547
+ @klass.do_stuff.should == :bar
548
+ end
549
+
550
+ it "should override default_on_failure if no method is provided" do
551
+ @klass.instance_eval do
552
+ remote_defaults :on_failure => lambda {|e| :foo}
553
+ define_remote_method :do_stuff, :base_uri => "http://localhost:9999", :on_failure => lambda {|e| e.code.should == 0; :bar}
554
+ end
555
+
556
+ @klass.do_stuff.should == :bar
557
+ end
558
+ end
559
+
560
+ describe "params" do
561
+ it "should take :params as an argument" do
562
+ @klass.instance_eval do
563
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :params => {:foo => :bar}
564
+ end
565
+
566
+ @klass.do_stuff.body.should include("QUERY_STRING=foo=bar")
567
+ end
568
+
569
+ it "should add :params from remote method definition with params passed in when called" do
570
+ @klass.instance_eval do
571
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :params => {:foo => :bar}
572
+ end
573
+
574
+ result = @klass.do_stuff(:params => {:asdf => :jkl})
575
+
576
+ # Make this test more robust to hash ordering.
577
+ query_string = result.body.match(/QUERY_STRING=([^\n]+)/)
578
+ params = query_string[1].split("&")
579
+ ["asdf=jkl", "foo=bar"].each do |param|
580
+ params.should include(param)
581
+ end
582
+ end
583
+ end
584
+
585
+ describe "memoize_responses" do
586
+ it "should only make one call to the http method and the on_success handler if :memoize_responses => true" do
587
+ success_mock = mock("success")
588
+ success_mock.should_receive(:call).exactly(2).times
589
+
590
+ @klass.instance_eval do
591
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :path => "/:file", :on_success => lambda {|e| success_mock.call; :foo}
592
+ end
593
+
594
+ first_return_val = @klass.do_stuff(:file => "user.html")
595
+ second_return_val = @klass.do_stuff(:file => "post.html")
596
+ third_return_val = @klass.do_stuff(:file => "user.html")
597
+
598
+ first_return_val.should == :foo
599
+ second_return_val.should == :foo
600
+ third_return_val.should == :foo
601
+ end
602
+
603
+ it "should clear memoized responses after a full run" do
604
+ success_mock = mock("success")
605
+ success_mock.should_receive(:call).exactly(2).times
606
+
607
+ @klass.instance_eval do
608
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :path => "/:file", :on_success => lambda {|e| success_mock.call; :foo}
609
+ end
610
+
611
+ @klass.do_stuff(:file => "user.html").should == :foo
612
+ @klass.do_stuff(:file => "user.html").should == :foo
613
+ end
614
+ end
615
+
616
+ describe "cache_response" do
617
+ before(:each) do
618
+ @cache = Class.new do
619
+ def self.get(key)
620
+ @cache ||= {}
621
+ @cache[key]
622
+ end
623
+
624
+ def self.set(key, value, timeout = 0)
625
+ @cache ||= {}
626
+ @cache[key] = value
627
+ end
628
+ end
629
+
630
+ @klass.instance_eval do
631
+ define_remote_method :do_stuff, :base_uri => "http://localhost:3001", :path => "/:file", :cache_responses => true, :on_success => lambda {|e| :foo}
632
+ end
633
+ end
634
+
635
+ it "should pull from the cache if :cache_response => true" do
636
+ @cache.should_receive(:get).and_return(:foo)
637
+ @klass.cache = @cache
638
+ Typhoeus.should_receive(:perform_easy_requests).exactly(0).times
639
+ @klass.do_stuff.should == :foo
640
+ end
641
+
642
+ it "should only hit the cache once for the same value" do
643
+ @cache.should_receive(:get).exactly(1).times.and_return(:foo)
644
+ @klass.cache = @cache
645
+ Typhoeus.should_receive(:perform_easy_requests).exactly(0).times
646
+
647
+
648
+ first = @klass.do_stuff
649
+ second = @klass.do_stuff
650
+
651
+ first.should == :foo
652
+ second.should == :foo
653
+ end
654
+
655
+ it "should only hit the cache once if there is a cache miss (don't check again and again inside the same block)." do
656
+ @cache.should_receive(:get).exactly(1).times.and_return(nil)
657
+ @cache.should_receive(:set).exactly(1).times
658
+ @klass.cache = @cache
659
+
660
+ first = @klass.do_stuff
661
+ second = @klass.do_stuff
662
+
663
+ first.should == :foo
664
+ second.should == :foo
665
+ end
666
+
667
+ it "should store an object in the cache with a set ttl"
668
+ it "should take a hash with get and set method pointers to enable custom caching behavior"
669
+ end
670
+ end # define_remote_method
671
+
672
+ describe "cache_server" do
673
+ it "should store a cache_server" do
674
+ @klass.cache = :foo
675
+ end
676
+ end
677
+
678
+ describe "get_memcache_resposne_key" do
679
+ it "should return a key that is an and of the method name, args, and options" do
680
+ @klass.get_memcache_response_key(:do_stuff, ["foo"]).should == "20630a9d4864c41cbbcb8bd8ac91ab4767e72107b93329aa2e6f5629037392f3"
681
+ end
682
+ end
683
+
684
+ # describe "multiple with post" do
685
+ # require 'rubygems'
686
+ # require 'json'
687
+ # it "shoudl do stuff" do
688
+ # @klass.instance_eval do
689
+ # define_remote_method :post_stuff, :path => "/entries/metas/:meta_id/ids", :base_uri => "http://localhost:4567", :method => :post
690
+ # define_remote_method :get_stuff, :base_uri => "http://localhost:4567"
691
+ # end
692
+ #
693
+ # Typhoeus.service_access do
694
+ # @klass.post_stuff("paul-tv", :body => ["foo", "bar"].to_json) {|e| }
695
+ # @klass.get_stuff {|e| }
696
+ # end
697
+ # end
698
+ # end
699
+ end