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.
@@ -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
@@ -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
- class MockDigestRequest
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
- # Initialize a new session for the given Rack app
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
- @app = app
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
- # authorize "bryan", "secret"
133
- def authorize(username, password)
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 ||= "example.org"
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] || {}).any? do |k, v|
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
- # Add the cookies explicitly set by the user
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 ||= "example.org"
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
- @cookie_jar = cookie_jar.merge(uri, last_response.headers["Set-Cookie"])
181
+ @rack_mock_session.request(uri, env)
226
182
 
227
183
  if retry_with_digest_auth?(env)
228
- auth_env = env.merge("HTTP_AUTHORIZATION" => digest_auth_header,
229
- "rack-test.digest_auth_retry" => true)
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 @last_response if block_given?
191
+ yield last_response if block_given?
234
192
 
235
- @last_response
193
+ last_response
236
194
  end
237
195
  end
238
196
 
239
197
  def digest_auth_header
240
- challenge = @last_response["WWW-Authenticate"].split(" ", 2).last
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" => @last_request.path_info,
248
- "method" => @last_request.env["REQUEST_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
- return "Digest #{params}"
211
+ "Digest #{params}"
254
212
  end
255
213
 
256
214
  def retry_with_digest_auth?(env)
257
- @last_response.status == 401 &&
215
+ last_response.status == 401 &&
258
216
  digest_auth_configured? &&
259
217
  !env["rack-test.digest_auth_retry"]
260
218
  end
@@ -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.delete_if { |k, v| !v || v.empty? }
22
+ @options["domain"] ||= (uri.host || default_host)
23
+ @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
24
+ end
19
25
 
20
- @options["domain"] ||= default_host
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.host =~ Regexp.new("#{Regexp.escape(domain)}$") &&
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
- @jar = cookies
77
- @jar.sort!
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 merge(uri, raw_cookies)
81
- return self unless raw_cookies
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
- # Initialize all the the received cookies
84
- cookies = []
85
- raw_cookies.each do |raw|
86
- c = Cookie.new(raw, uri.host)
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
- # Remove all the cookies that will be updated
91
- new_jar = @jar.reject do |existing|
92
- cookies.find do |c|
93
- [c.name, c.domain, c.path] == [existing.name, existing.domain, existing.path]
94
- end
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
- new_jar.concat cookies
125
+ def <<(new_cookie)
126
+ @cookies.reject! do |existing_cookie|
127
+ new_cookie.replaces?(existing_cookie)
128
+ end
98
129
 
99
- return self.class.new(new_jar)
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
- @jar.each do |cookie|
112
- cookies[cookie.name] = cookie.raw if cookie.matches?(uri)
159
+ @cookies.each do |cookie|
160
+ cookies[cookie.name] = cookie if cookie.matches?(uri)
113
161
  end
114
162
 
115
- cookies.values.join(';')
163
+ return cookies
116
164
  end
117
165
 
118
166
  end
@@ -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.2.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-04-26 00:00:00 -04:00
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.1
61
+ rubygems_version: 1.3.3
60
62
  signing_key:
61
- specification_version: 2
63
+ specification_version: 3
62
64
  summary: Simple testing API built on Rack
63
65
  test_files: []
64
66