rack-test 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
|