rack-test 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +17 -0
- data/lib/rack/mock_session.rb +50 -0
- data/lib/rack/test.rb +31 -73
- data/lib/rack/test/cookie_jar.rb +74 -26
- data/lib/rack/test/methods.rb +11 -0
- data/lib/rack/test/mock_digest_request.rb +27 -0
- metadata +8 -6
data/History.txt
CHANGED
@@ -1,3 +1,20 @@
|
|
1
|
+
== 0.3.0 / 2009-05-17
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
|
5
|
+
* Ruby 1.9 compatible (Simon Rozet, Michael Fellinger)
|
6
|
+
|
7
|
+
* Minor enhancements
|
8
|
+
|
9
|
+
* Add CookieJar#[] and CookieJar#[]= methods
|
10
|
+
* Make the default host configurable
|
11
|
+
* Use Rack::Lint and fix errors (Simon Rozet)
|
12
|
+
* Extract Rack::MockSession from Rack::Test::Session to handle tracking
|
13
|
+
the last request and response and the cookie jar
|
14
|
+
* Add #set_cookie and #clear_cookies methods
|
15
|
+
* Rename #authorize to #basic_authorize (#authorize remains as an alias)
|
16
|
+
(Simon Rozet)
|
17
|
+
|
1
18
|
== 0.2.0 / 2009-04-26
|
2
19
|
|
3
20
|
Because #last_response is now a MockResponse instead of a Rack::Response,
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Rack
|
2
|
+
|
3
|
+
class MockSession
|
4
|
+
attr_writer :cookie_jar
|
5
|
+
attr_reader :last_response
|
6
|
+
|
7
|
+
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
|
8
|
+
@app = app
|
9
|
+
@default_host = default_host
|
10
|
+
end
|
11
|
+
|
12
|
+
def clear_cookies
|
13
|
+
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
|
14
|
+
end
|
15
|
+
|
16
|
+
def set_cookie(cookie, uri = nil)
|
17
|
+
cookie_jar.merge(cookie, uri)
|
18
|
+
end
|
19
|
+
|
20
|
+
def request(uri, env)
|
21
|
+
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
|
22
|
+
@last_request = Rack::Request.new(env)
|
23
|
+
status, headers, body = @app.call(@last_request.env)
|
24
|
+
@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
|
25
|
+
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
|
26
|
+
|
27
|
+
@last_response
|
28
|
+
end
|
29
|
+
|
30
|
+
# Return the last request issued in the session. Raises an error if no
|
31
|
+
# requests have been sent yet.
|
32
|
+
def last_request
|
33
|
+
raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
|
34
|
+
@last_request
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return the last response received in the session. Raises an error if
|
38
|
+
# no requests have been sent yet.
|
39
|
+
def last_response
|
40
|
+
raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
|
41
|
+
@last_response
|
42
|
+
end
|
43
|
+
|
44
|
+
def cookie_jar
|
45
|
+
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/lib/rack/test.rb
CHANGED
@@ -4,7 +4,9 @@ end
|
|
4
4
|
|
5
5
|
require "uri"
|
6
6
|
require "rack"
|
7
|
+
require "rack/mock_session"
|
7
8
|
require "rack/test/cookie_jar"
|
9
|
+
require "rack/test/mock_digest_request"
|
8
10
|
require "rack/test/utils"
|
9
11
|
require "rack/test/methods"
|
10
12
|
require "rack/test/uploaded_file"
|
@@ -12,44 +14,25 @@ require "rack/test/uploaded_file"
|
|
12
14
|
module Rack
|
13
15
|
module Test
|
14
16
|
|
15
|
-
|
16
|
-
def initialize(params)
|
17
|
-
@params = params
|
18
|
-
end
|
19
|
-
|
20
|
-
def method_missing(sym)
|
21
|
-
if @params.has_key? k = sym.to_s
|
22
|
-
return @params[k]
|
23
|
-
end
|
24
|
-
|
25
|
-
super
|
26
|
-
end
|
27
|
-
|
28
|
-
def method
|
29
|
-
@params['method']
|
30
|
-
end
|
31
|
-
|
32
|
-
def response(password)
|
33
|
-
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
VERSION = "0.2.0"
|
17
|
+
VERSION = "0.3.0"
|
38
18
|
|
19
|
+
DEFAULT_HOST = "example.org"
|
39
20
|
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
|
40
21
|
|
41
22
|
# The common base class for exceptions raised by Rack::Test
|
42
23
|
class Error < StandardError; end
|
43
24
|
|
44
25
|
class Session
|
26
|
+
extend Forwardable
|
45
27
|
include Rack::Test::Utils
|
46
28
|
|
47
|
-
|
48
|
-
def initialize(app)
|
49
|
-
raise ArgumentError.new("app must respond_to?(:call)") unless app.respond_to?(:call)
|
29
|
+
def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
|
50
30
|
|
31
|
+
# Initialize a new session for the given Rack app
|
32
|
+
def initialize(app, default_host = DEFAULT_HOST)
|
51
33
|
@headers = {}
|
52
|
-
@
|
34
|
+
@default_host = default_host
|
35
|
+
@rack_mock_session = Rack::MockSession.new(app, default_host)
|
53
36
|
end
|
54
37
|
|
55
38
|
# Issue a GET request for the given URI with the given params and Rack
|
@@ -129,12 +112,14 @@ module Rack
|
|
129
112
|
# included in subsequent requests in the HTTP_AUTHORIZATION header.
|
130
113
|
#
|
131
114
|
# Example:
|
132
|
-
#
|
133
|
-
def
|
115
|
+
# basic_authorize "bryan", "secret"
|
116
|
+
def basic_authorize(username, password)
|
134
117
|
encoded_login = ["#{username}:#{password}"].pack("m*")
|
135
118
|
header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
|
136
119
|
end
|
137
120
|
|
121
|
+
alias_method :authorize, :basic_authorize
|
122
|
+
|
138
123
|
def digest_authorize(username, password)
|
139
124
|
@digest_username = username
|
140
125
|
@digest_password = password
|
@@ -151,27 +136,11 @@ module Rack
|
|
151
136
|
get(last_response["Location"])
|
152
137
|
end
|
153
138
|
|
154
|
-
# Return the last request issued in the session. Raises an error if no
|
155
|
-
# requests have been sent yet.
|
156
|
-
def last_request
|
157
|
-
raise Error.new("No request yet. Request a page first.") unless @last_request
|
158
|
-
|
159
|
-
@last_request
|
160
|
-
end
|
161
|
-
|
162
|
-
# Return the last response received in the session. Raises an error if
|
163
|
-
# no requests have been sent yet.
|
164
|
-
def last_response
|
165
|
-
raise Error.new("No response yet. Request a page first.") unless @last_response
|
166
|
-
|
167
|
-
@last_response
|
168
|
-
end
|
169
|
-
|
170
139
|
private
|
171
140
|
|
172
141
|
def env_for(path, env)
|
173
142
|
uri = URI.parse(path)
|
174
|
-
uri.host ||=
|
143
|
+
uri.host ||= @default_host
|
175
144
|
|
176
145
|
env = default_env.merge(env)
|
177
146
|
|
@@ -181,9 +150,8 @@ module Rack
|
|
181
150
|
if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input)
|
182
151
|
env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
|
183
152
|
|
184
|
-
multipart = (env[:params]
|
185
|
-
UploadedFile === v
|
186
|
-
end
|
153
|
+
multipart = (Hash === env[:params]) &&
|
154
|
+
env[:params].any? { |_, v| UploadedFile === v }
|
187
155
|
|
188
156
|
if multipart
|
189
157
|
env[:input] = multipart_body(env.delete(:params))
|
@@ -200,61 +168,51 @@ module Rack
|
|
200
168
|
uri.query = requestify(params)
|
201
169
|
|
202
170
|
if env.has_key?(:cookie)
|
203
|
-
|
204
|
-
env["HTTP_COOKIE"] = cookie_jar.merge(uri, env.delete(:cookie)).for(uri)
|
205
|
-
else
|
206
|
-
env["HTTP_COOKIE"] = cookie_jar.for(uri)
|
171
|
+
set_cookie(env.delete(:cookie), uri)
|
207
172
|
end
|
208
173
|
|
209
174
|
Rack::MockRequest.env_for(uri.to_s, env)
|
210
175
|
end
|
211
176
|
|
212
|
-
def cookie_jar
|
213
|
-
@cookie_jar || Rack::Test::CookieJar.new
|
214
|
-
end
|
215
|
-
|
216
177
|
def process_request(uri, env)
|
217
178
|
uri = URI.parse(uri)
|
218
|
-
uri.host ||=
|
219
|
-
|
220
|
-
@last_request = Rack::Request.new(env)
|
221
|
-
|
222
|
-
status, headers, body = @app.call(@last_request.env)
|
223
|
-
@last_response = MockResponse.new(status, headers, body, env["rack.errors"])
|
179
|
+
uri.host ||= @default_host
|
224
180
|
|
225
|
-
@
|
181
|
+
@rack_mock_session.request(uri, env)
|
226
182
|
|
227
183
|
if retry_with_digest_auth?(env)
|
228
|
-
auth_env = env.merge(
|
229
|
-
|
184
|
+
auth_env = env.merge({
|
185
|
+
"HTTP_AUTHORIZATION" => digest_auth_header,
|
186
|
+
"rack-test.digest_auth_retry" => true
|
187
|
+
})
|
230
188
|
auth_env.delete('rack.request')
|
231
189
|
process_request(uri.path, auth_env)
|
232
190
|
else
|
233
|
-
yield
|
191
|
+
yield last_response if block_given?
|
234
192
|
|
235
|
-
|
193
|
+
last_response
|
236
194
|
end
|
237
195
|
end
|
238
196
|
|
239
197
|
def digest_auth_header
|
240
|
-
challenge =
|
198
|
+
challenge = last_response["WWW-Authenticate"].split(" ", 2).last
|
241
199
|
params = Rack::Auth::Digest::Params.parse(challenge)
|
242
200
|
|
243
201
|
params.merge!({
|
244
202
|
"username" => @digest_username,
|
245
203
|
"nc" => "00000001",
|
246
204
|
"cnonce" => "nonsensenonce",
|
247
|
-
"uri" =>
|
248
|
-
"method" =>
|
205
|
+
"uri" => last_request.path_info,
|
206
|
+
"method" => last_request.env["REQUEST_METHOD"],
|
249
207
|
})
|
250
208
|
|
251
209
|
params["response"] = MockDigestRequest.new(params).response(@digest_password)
|
252
210
|
|
253
|
-
|
211
|
+
"Digest #{params}"
|
254
212
|
end
|
255
213
|
|
256
214
|
def retry_with_digest_auth?(env)
|
257
|
-
|
215
|
+
last_response.status == 401 &&
|
258
216
|
digest_auth_configured? &&
|
259
217
|
!env["rack-test.digest_auth_retry"]
|
260
218
|
end
|
data/lib/rack/test/cookie_jar.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "uri"
|
1
2
|
module Rack
|
2
3
|
module Test
|
3
4
|
|
@@ -8,16 +9,22 @@ module Rack
|
|
8
9
|
attr_reader :name, :value
|
9
10
|
|
10
11
|
# :api: private
|
11
|
-
def initialize(raw, default_host)
|
12
|
+
def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
|
13
|
+
@default_host = default_host
|
14
|
+
uri ||= default_uri
|
15
|
+
|
12
16
|
# separate the name / value pair from the cookie options
|
13
17
|
@name_value_raw, options = raw.split(/[;,] */n, 2)
|
14
18
|
|
15
19
|
@name, @value = parse_query(@name_value_raw, ';').to_a.first
|
16
20
|
@options = parse_query(options, ';')
|
17
21
|
|
18
|
-
@options.
|
22
|
+
@options["domain"] ||= (uri.host || default_host)
|
23
|
+
@options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
|
24
|
+
end
|
19
25
|
|
20
|
-
|
26
|
+
def replaces?(other)
|
27
|
+
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
|
21
28
|
end
|
22
29
|
|
23
30
|
# :api: private
|
@@ -35,9 +42,13 @@ module Rack
|
|
35
42
|
@options["domain"]
|
36
43
|
end
|
37
44
|
|
45
|
+
def secure?
|
46
|
+
@options.has_key?("secure")
|
47
|
+
end
|
48
|
+
|
38
49
|
# :api: private
|
39
50
|
def path
|
40
|
-
@options["path"] || "/"
|
51
|
+
@options["path"].strip || "/"
|
41
52
|
end
|
42
53
|
|
43
54
|
# :api: private
|
@@ -52,7 +63,14 @@ module Rack
|
|
52
63
|
|
53
64
|
# :api: private
|
54
65
|
def valid?(uri)
|
55
|
-
uri
|
66
|
+
uri ||= default_uri
|
67
|
+
|
68
|
+
if uri.host.nil?
|
69
|
+
uri.host = @default_host
|
70
|
+
end
|
71
|
+
|
72
|
+
(!secure? || (secure? && uri.scheme == "https")) &&
|
73
|
+
uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) &&
|
56
74
|
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
|
57
75
|
end
|
58
76
|
|
@@ -67,40 +85,70 @@ module Rack
|
|
67
85
|
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
|
68
86
|
end
|
69
87
|
|
88
|
+
protected
|
89
|
+
|
90
|
+
def default_uri
|
91
|
+
URI.parse("//" + @default_host + "/")
|
92
|
+
end
|
93
|
+
|
70
94
|
end
|
71
95
|
|
72
96
|
class CookieJar
|
73
97
|
|
74
98
|
# :api: private
|
75
|
-
def initialize(cookies = [])
|
76
|
-
@
|
77
|
-
@
|
99
|
+
def initialize(cookies = [], default_host = DEFAULT_HOST)
|
100
|
+
@default_host = default_host
|
101
|
+
@cookies = cookies
|
102
|
+
@cookies.sort!
|
78
103
|
end
|
79
104
|
|
80
|
-
def
|
81
|
-
|
105
|
+
def [](name)
|
106
|
+
cookies = hash_for(nil)
|
107
|
+
# TODO: Should be case insensitive
|
108
|
+
cookies[name] && cookies[name].value
|
109
|
+
end
|
82
110
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
cookies << c if c.valid?(uri)
|
88
|
-
end
|
111
|
+
def []=(name, value)
|
112
|
+
# TODO: needs proper escaping
|
113
|
+
merge("#{name}=#{value}")
|
114
|
+
end
|
89
115
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
116
|
+
def merge(raw_cookies, uri = nil)
|
117
|
+
return unless raw_cookies
|
118
|
+
|
119
|
+
raw_cookies.each_line do |raw_cookie|
|
120
|
+
cookie = Cookie.new(raw_cookie, uri, @default_host)
|
121
|
+
self << cookie if cookie.valid?(uri)
|
95
122
|
end
|
123
|
+
end
|
96
124
|
|
97
|
-
|
125
|
+
def <<(new_cookie)
|
126
|
+
@cookies.reject! do |existing_cookie|
|
127
|
+
new_cookie.replaces?(existing_cookie)
|
128
|
+
end
|
98
129
|
|
99
|
-
|
130
|
+
@cookies << new_cookie
|
131
|
+
@cookies.sort!
|
100
132
|
end
|
101
133
|
|
102
134
|
# :api: private
|
103
135
|
def for(uri)
|
136
|
+
hash_for(uri).values.map { |c| c.raw }.join(';')
|
137
|
+
end
|
138
|
+
|
139
|
+
def to_hash
|
140
|
+
cookies = {}
|
141
|
+
|
142
|
+
hash_for(nil).each do |name, cookie|
|
143
|
+
cookies[name] = cookie.value
|
144
|
+
end
|
145
|
+
|
146
|
+
return cookies
|
147
|
+
end
|
148
|
+
|
149
|
+
protected
|
150
|
+
|
151
|
+
def hash_for(uri = nil)
|
104
152
|
cookies = {}
|
105
153
|
|
106
154
|
# The cookies are sorted by most specific first. So, we loop through
|
@@ -108,11 +156,11 @@ module Rack
|
|
108
156
|
# the cookie can be sent to the current URI. It's added to the hash
|
109
157
|
# so that when we are done, the cookies will be unique by name and
|
110
158
|
# we'll have grabbed the most specific to the URI.
|
111
|
-
@
|
112
|
-
cookies[cookie.name] = cookie
|
159
|
+
@cookies.each do |cookie|
|
160
|
+
cookies[cookie.name] = cookie if cookie.matches?(uri)
|
113
161
|
end
|
114
162
|
|
115
|
-
cookies
|
163
|
+
return cookies
|
116
164
|
end
|
117
165
|
|
118
166
|
end
|
data/lib/rack/test/methods.rb
CHANGED
@@ -9,20 +9,31 @@ module Rack
|
|
9
9
|
@_rack_test_session ||= Rack::Test::Session.new(app)
|
10
10
|
end
|
11
11
|
|
12
|
+
def rack_mock_session
|
13
|
+
@_rack_mock_session ||= Rack::MockSession.new(app)
|
14
|
+
end
|
15
|
+
|
12
16
|
METHODS = [
|
13
17
|
:request,
|
18
|
+
|
14
19
|
# HTTP verbs
|
15
20
|
:get,
|
16
21
|
:post,
|
17
22
|
:put,
|
18
23
|
:delete,
|
19
24
|
:head,
|
25
|
+
|
20
26
|
# Redirects
|
21
27
|
:follow_redirect!,
|
28
|
+
|
22
29
|
# Header-related features
|
23
30
|
:header,
|
31
|
+
:set_cookie,
|
32
|
+
:clear_cookies,
|
24
33
|
:authorize,
|
34
|
+
:basic_authorize,
|
25
35
|
:digest_authorize,
|
36
|
+
|
26
37
|
# Expose the last request and response
|
27
38
|
:last_response,
|
28
39
|
:last_request
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Rack
|
2
|
+
module Test
|
3
|
+
|
4
|
+
class MockDigestRequest
|
5
|
+
def initialize(params)
|
6
|
+
@params = params
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(sym)
|
10
|
+
if @params.has_key? k = sym.to_s
|
11
|
+
return @params[k]
|
12
|
+
end
|
13
|
+
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def method
|
18
|
+
@params['method']
|
19
|
+
end
|
20
|
+
|
21
|
+
def response(password)
|
22
|
+
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-test
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bryan Helmkamp
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-05-17 00:00:00 -04:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -26,16 +26,18 @@ files:
|
|
26
26
|
- History.txt
|
27
27
|
- Rakefile
|
28
28
|
- README.rdoc
|
29
|
-
- lib/rack
|
30
|
-
- lib/rack/test
|
29
|
+
- lib/rack/mock_session.rb
|
31
30
|
- lib/rack/test/cookie_jar.rb
|
32
31
|
- lib/rack/test/methods.rb
|
32
|
+
- lib/rack/test/mock_digest_request.rb
|
33
33
|
- lib/rack/test/uploaded_file.rb
|
34
34
|
- lib/rack/test/utils.rb
|
35
35
|
- lib/rack/test.rb
|
36
36
|
- MIT-LICENSE.txt
|
37
37
|
has_rdoc: true
|
38
38
|
homepage: http://github.com/brynary/rack-test
|
39
|
+
licenses: []
|
40
|
+
|
39
41
|
post_install_message:
|
40
42
|
rdoc_options: []
|
41
43
|
|
@@ -56,9 +58,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
56
58
|
requirements: []
|
57
59
|
|
58
60
|
rubyforge_project:
|
59
|
-
rubygems_version: 1.3.
|
61
|
+
rubygems_version: 1.3.3
|
60
62
|
signing_key:
|
61
|
-
specification_version:
|
63
|
+
specification_version: 3
|
62
64
|
summary: Simple testing API built on Rack
|
63
65
|
test_files: []
|
64
66
|
|