elevate 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,10 @@
1
1
  module Elevate
2
+ # A blank slate for hosting task blocks.
3
+ #
4
+ # Because task blocks run in another thread, it is dangerous to expose them
5
+ # to the calling context. This class acts as a sandbox for task blocks.
6
+ #
7
+ # @api private
2
8
  class TaskContext
3
9
  def initialize(args, &block)
4
10
  metaclass = class << self; self; end
@@ -1,3 +1,3 @@
1
1
  module Elevate
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -0,0 +1,159 @@
1
+ module Bacon
2
+ class Context
3
+ include ::Elevate
4
+ end
5
+ end
6
+
7
+ describe Elevate do
8
+ extend WebStub::SpecHelpers
9
+
10
+ describe "#async" do
11
+ it "runs the specified task asynchronously" do
12
+ async do
13
+ task do
14
+ true
15
+ end
16
+
17
+ on_finish do |result, exception|
18
+ @called = result
19
+ resume
20
+ end
21
+ end
22
+
23
+ wait_max 1.0 do
24
+ @called.should.be.true
25
+ end
26
+ end
27
+
28
+ it "passes provided args to the task as instance variables" do
29
+ async name: "harry" do
30
+ task do
31
+ @name
32
+ end
33
+
34
+ on_finish do |name, exception|
35
+ @result = name
36
+ resume
37
+ end
38
+ end
39
+
40
+ wait_max 1.0 do
41
+ @result.should == "harry"
42
+ end
43
+ end
44
+
45
+ it "allows tasks to report progress" do
46
+ @updates = []
47
+
48
+ async do
49
+ task do
50
+ sleep 0.1
51
+ yield 1
52
+ sleep 0.2
53
+ yield 2
54
+ sleep 0.3
55
+ yield 3
56
+
57
+ true
58
+ end
59
+
60
+ on_update do |count|
61
+ @updates << count
62
+ end
63
+
64
+ on_finish do |result, exception|
65
+ resume
66
+ end
67
+ end
68
+
69
+ wait_max 1.0 do
70
+ @updates.should == [1,2,3]
71
+ end
72
+ end
73
+
74
+ describe "timeouts" do
75
+ before do
76
+ stub_request(:get, "http://example.com/").
77
+ to_return(body: "Hello!", content_type: "text/plain", delay: 1.0)
78
+ end
79
+
80
+ it "does not cancel the operation if it completes in time" do
81
+ @timed_out = false
82
+
83
+ async do
84
+ timeout 3.0
85
+
86
+ task do
87
+ Elevate::HTTP.get("http://example.com/")
88
+
89
+ "finished"
90
+ end
91
+
92
+ on_finish do |result, exception|
93
+ @result = result
94
+ resume
95
+ end
96
+ end
97
+
98
+ wait_max 5.0 do
99
+ @result.should == "finished"
100
+ @timed_out.should.be.false
101
+ end
102
+ end
103
+
104
+ it "stops the operation when timeout interval has elapsed" do
105
+ @result = nil
106
+
107
+ @task = async do
108
+ timeout 0.5
109
+
110
+ task do
111
+ Elevate::HTTP.get("http://example.com/")
112
+
113
+ "finished"
114
+ end
115
+
116
+ on_finish do |result, exception|
117
+ @result = result
118
+ resume
119
+ end
120
+ end
121
+
122
+ wait_max 5.0 do
123
+ @result.should.not == "finished"
124
+
125
+ @task.timed_out?.should.be.true
126
+ end
127
+ end
128
+
129
+ it "invokes on_timeout when a timeout occurs" do
130
+ @result = ""
131
+ @timed_out = false
132
+
133
+ async do
134
+ timeout 0.5
135
+
136
+ task do
137
+ Elevate::HTTP.get("http://example.com/")
138
+
139
+ "finished"
140
+ end
141
+
142
+ on_timeout do
143
+ @timed_out = true
144
+ end
145
+
146
+ on_finish do |result, exception|
147
+ @result = result
148
+ resume
149
+ end
150
+ end
151
+
152
+ wait_max 5.0 do
153
+ @result.should.not == "finished"
154
+ @timed_out.should.be.true
155
+ end
156
+ end
157
+ end
158
+ end
159
+ end
@@ -1,4 +1,4 @@
1
- describe Elevate::HTTP::HTTPRequest do
1
+ describe Elevate::HTTP::Request do
2
2
  extend WebStub::SpecHelpers
3
3
 
4
4
  before do
@@ -11,15 +11,15 @@ describe Elevate::HTTP::HTTPRequest do
11
11
  end
12
12
 
13
13
  it "requires a valid HTTP method" do
14
- lambda { Elevate::HTTP::HTTPRequest.new(:invalid, @url) }.should.raise(ArgumentError)
14
+ lambda { Elevate::HTTP::Request.new(:invalid, @url) }.should.raise(ArgumentError)
15
15
  end
16
16
 
17
17
  it "requires a URL starting with http" do
18
- lambda { Elevate::HTTP::HTTPRequest.new(:get, "asdf") }.should.raise(ArgumentError)
18
+ lambda { Elevate::HTTP::Request.new(:get, "asdf") }.should.raise(ArgumentError)
19
19
  end
20
20
 
21
21
  it "requires the body to be an instance of NSData" do
22
- lambda { Elevate::HTTP::HTTPRequest.new(:get, @url, body: @body) }.should.raise(ArgumentError)
22
+ lambda { Elevate::HTTP::Request.new(:get, @url, body: @body) }.should.raise(ArgumentError)
23
23
  end
24
24
 
25
25
  describe "fulfilling a GET request" do
@@ -27,7 +27,7 @@ describe Elevate::HTTP::HTTPRequest do
27
27
  stub_request(:get, @url).
28
28
  to_return(body: @body, headers: {"Content-Type" => "text/plain"}, status_code: 201)
29
29
 
30
- @request = Elevate::HTTP::HTTPRequest.new(:get, @url)
30
+ @request = Elevate::HTTP::Request.new(:get, @url)
31
31
  @response = @request.response
32
32
  end
33
33
 
@@ -52,7 +52,7 @@ describe Elevate::HTTP::HTTPRequest do
52
52
  before do
53
53
  stub_request(:get, @url).with(headers: { "API-Token" => "abc123" }).to_return(body: @body)
54
54
 
55
- @request = Elevate::HTTP::HTTPRequest.new(:get, @url, headers: {})
55
+ @request = Elevate::HTTP::Request.new(:get, @url, headers: { "API-Token" => "abc123" })
56
56
  @response = @request.response
57
57
  end
58
58
 
@@ -67,7 +67,7 @@ describe Elevate::HTTP::HTTPRequest do
67
67
  end
68
68
 
69
69
  it "sends the body as part of the request" do
70
- request = Elevate::HTTP::HTTPRequest.new(:post, @url, body: @body.dataUsingEncoding(NSUTF8StringEncoding))
70
+ request = Elevate::HTTP::Request.new(:post, @url, body: @body.dataUsingEncoding(NSUTF8StringEncoding))
71
71
  response = request.response
72
72
 
73
73
  NSString.alloc.initWithData(response.body, encoding:NSUTF8StringEncoding).should == @body
@@ -82,9 +82,9 @@ describe Elevate::HTTP::HTTPRequest do
82
82
  it "aborts the request" do
83
83
  start = Time.now
84
84
 
85
- request = Elevate::HTTP::HTTPRequest.new(:get, @url)
86
- request.start()
87
- request.cancel()
85
+ request = Elevate::HTTP::Request.new(:get, @url)
86
+ request.send
87
+ request.cancel
88
88
 
89
89
  response = request.response # simulate blocking
90
90
  finish = Time.now
@@ -93,9 +93,9 @@ describe Elevate::HTTP::HTTPRequest do
93
93
  end
94
94
 
95
95
  it "sets the response to nil" do
96
- request = Elevate::HTTP::HTTPRequest.new(:get, @url)
97
- request.start()
98
- request.cancel()
96
+ request = Elevate::HTTP::Request.new(:get, @url)
97
+ request.send
98
+ request.cancel
99
99
 
100
100
  request.response.should.be.nil
101
101
  end
@@ -0,0 +1,195 @@
1
+ describe Elevate::HTTP do
2
+ extend WebStub::SpecHelpers
3
+
4
+ before { disable_network_access! }
5
+ after { enable_network_access! }
6
+
7
+ before do
8
+ @url = "http://www.example.com/"
9
+ end
10
+
11
+ Elevate::HTTP::Request::METHODS.each do |m|
12
+ describe ".#{m}" do
13
+ it "synchronously issues a HTTP #{m} request" do
14
+ stub = stub_request(m, @url)
15
+ Elevate::HTTP.send(m, @url)
16
+
17
+ stub.should.be.requested
18
+ end
19
+ end
20
+ end
21
+
22
+ describe "Request options" do
23
+ it "encodes query string of :headers" do
24
+ stub = stub_request(:get, "#{@url}?a=1&b=hello&c=4.2")
25
+
26
+ Elevate::HTTP.get(@url, query: { a: 1, b: "hello", c: "4.2" })
27
+
28
+ stub.should.be.requested
29
+ end
30
+
31
+ it "sends headers specified by :headers" do
32
+ stub = stub_request(:get, @url).
33
+ with(headers: { "API-Key" => "secret" })
34
+
35
+ Elevate::HTTP.get(@url, headers: { "API-Key" => "secret" })
36
+
37
+ stub.should.be.requested
38
+ end
39
+
40
+ it "sends body content specified by :body" do
41
+ stub = stub_request(:post, @url).with(body: "hello")
42
+
43
+ Elevate::HTTP.post(@url, body: "hello".dataUsingEncoding(NSUTF8StringEncoding))
44
+
45
+ stub.should.be.requested
46
+ end
47
+
48
+ describe "with a JSON body" do
49
+ it "encodes JSON dictionary specified by :json" do
50
+ stub = stub_request(:post, @url).with(body: '{"test":"secret"}')
51
+
52
+ Elevate::HTTP.post(@url, json: { "test" => "secret" })
53
+
54
+ stub.should.be.requested
55
+ end
56
+
57
+ it "encodes JSON array specified by :json" do
58
+ stub = stub_request(:post, @url).with(body: '["1","2"]')
59
+
60
+ Elevate::HTTP.post(@url, json: ["1", "2"])
61
+
62
+ stub.should.be.requested
63
+ end
64
+
65
+ it "sets the correct Content-Type" do
66
+ stub = stub_request(:post, @url).
67
+ with(body: '{"test":"secret"}', headers: { "Content-Type" => "application/json" })
68
+
69
+ Elevate::HTTP.post(@url, json: { "test" => "secret" })
70
+
71
+ stub.should.be.requested
72
+ end
73
+ end
74
+
75
+ describe "with a form body" do
76
+ it "encodes form data specified by :form" do
77
+ stub = stub_request(:post, @url).with(body: { "test" => "secret", "user" => "matt" })
78
+
79
+ Elevate::HTTP.post(@url, form: { "test" => "secret", "user" => "matt" })
80
+
81
+ stub.should.be.requested
82
+ end
83
+
84
+ it "sets the correct Content-Type" do
85
+ stub = stub_request(:post, @url).
86
+ with(body: { "test" => "secret", "user" => "matt" }, headers: { "Content-Type" => "application/x-www-form-urlencoded" })
87
+
88
+ Elevate::HTTP.post(@url, form: { "test" => "secret", "user" => "matt" })
89
+
90
+ stub.should.be.requested
91
+ end
92
+ end
93
+ end
94
+
95
+ describe "Response" do
96
+ it "returns a Response" do
97
+ stub_request(:get, @url).
98
+ to_return(body: "hello",
99
+ headers: { "X-TestHeader" => "Value" },
100
+ status_code: 204)
101
+
102
+ response = Elevate::HTTP.get(@url)
103
+
104
+ NSString.alloc.initWithData(response.body, encoding: NSUTF8StringEncoding).should == "hello"
105
+ NSString.alloc.initWithData(response.raw_body, encoding: NSUTF8StringEncoding).should == "hello"
106
+ response.error.should.be.nil
107
+ response.headers.keys.should.include("X-TestHeader")
108
+ response.status_code.should == 204
109
+ response.url.should == @url
110
+ end
111
+
112
+ describe "when the response is encoded as JSON" do
113
+ it "should automatically decode it" do
114
+ stub_request(:get, @url).
115
+ to_return(json: { user_id: "3", token: "secret" })
116
+
117
+ response = Elevate::HTTP.get(@url)
118
+
119
+ response.body.should == { "user_id" => "3", "token" => "secret" }
120
+ end
121
+
122
+ describe "when a JSON dictionary is returned" do
123
+ it "the returned response should behave like a Hash" do
124
+ stub_request(:get, @url).
125
+ to_return(json: { user_id: "3", token: "secret" })
126
+
127
+ response = Elevate::HTTP.get(@url)
128
+
129
+ response["user_id"].should == "3"
130
+ end
131
+ end
132
+
133
+ describe "when a JSON array is returned" do
134
+ it "the returned response should behave like an Array" do
135
+ stub_request(:get, @url).
136
+ to_return(json: ["apple", "orange", "pear"])
137
+
138
+ response = Elevate::HTTP.get(@url)
139
+
140
+ response.length.should == 3
141
+ end
142
+ end
143
+ end
144
+
145
+ describe "when the response is redirected" do
146
+ it "redirects to the final URL" do
147
+ @redirect_url = @url + "redirected"
148
+
149
+ stub_request(:get, @redirect_url).to_return(body: "redirected")
150
+ stub_request(:get, @url).to_redirect(url: @redirect_url)
151
+
152
+ response = Elevate::HTTP.get(@url)
153
+ response.url.should == @redirect_url
154
+ end
155
+
156
+ describe "with an infinite redirect loop" do
157
+ before do
158
+ @url2 = @url + "redirect"
159
+
160
+ stub_request(:get, @url).to_redirect(url: @url2)
161
+ stub_request(:get, @url2).to_redirect(url: @url)
162
+ end
163
+
164
+ it "raises a RequestError" do
165
+ lambda { Elevate::HTTP.get(@url) }.
166
+ should.raise(Elevate::HTTP::RequestError)
167
+ end
168
+ end
169
+ end
170
+
171
+ #describe "when the response has a HTTP error status" do
172
+ #it "raises RequestError" do
173
+ #stub_request(m, @url).to_return(status_code: 422)
174
+
175
+ #lambda { Elevate::HTTP.send(m, @url) }.should.raise(Elevate::HTTP::RequestError)
176
+ #end
177
+ #end
178
+
179
+ describe "when the request cannot be fulfilled" do
180
+ it "raises a RequestError" do
181
+ lambda { Elevate::HTTP.get(@url) }.
182
+ should.raise(Elevate::HTTP::RequestError)
183
+ end
184
+ end
185
+
186
+ describe "when the Internet connection is offline" do
187
+ it "raises an OfflineError" do
188
+ stub_request(:get, @url).to_fail(code: NSURLErrorNotConnectedToInternet)
189
+
190
+ lambda { Elevate::HTTP.get(@url) }.
191
+ should.raise(Elevate::HTTP::OfflineError)
192
+ end
193
+ end
194
+ end
195
+ end
@@ -30,6 +30,14 @@ describe Elevate::IOCoordinator do
30
30
  lambda { @coordinator.send(method, "hello") }.should.raise(Elevate::CancelledError)
31
31
  end
32
32
  end
33
+
34
+ describe "when IO has timed out" do
35
+ it "raises TimeoutError" do
36
+ @coordinator.cancel(Elevate::TimeoutError)
37
+
38
+ lambda { @coordinator.send(method, "hello") }.should.raise(Elevate::TimeoutError)
39
+ end
40
+ end
33
41
  end
34
42
  end
35
43