josh-rack-test 0.4.1

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.
@@ -0,0 +1,170 @@
1
+ require "uri"
2
+ module Rack
3
+ module Test
4
+
5
+ class Cookie
6
+ include Rack::Utils
7
+
8
+ # :api: private
9
+ attr_reader :name, :value
10
+
11
+ # :api: private
12
+ def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
13
+ @default_host = default_host
14
+ uri ||= default_uri
15
+
16
+ # separate the name / value pair from the cookie options
17
+ @name_value_raw, options = raw.split(/[;,] */n, 2)
18
+
19
+ @name, @value = parse_query(@name_value_raw, ';').to_a.first
20
+ @options = parse_query(options, ';')
21
+
22
+ @options["domain"] ||= (uri.host || default_host)
23
+ @options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
24
+ end
25
+
26
+ def replaces?(other)
27
+ [name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
28
+ end
29
+
30
+ # :api: private
31
+ def raw
32
+ @name_value_raw
33
+ end
34
+
35
+ # :api: private
36
+ def empty?
37
+ @value.nil? || @value.empty?
38
+ end
39
+
40
+ # :api: private
41
+ def domain
42
+ @options["domain"]
43
+ end
44
+
45
+ def secure?
46
+ @options.has_key?("secure")
47
+ end
48
+
49
+ # :api: private
50
+ def path
51
+ @options["path"].strip || "/"
52
+ end
53
+
54
+ # :api: private
55
+ def expires
56
+ Time.parse(@options["expires"]) if @options["expires"]
57
+ end
58
+
59
+ # :api: private
60
+ def expired?
61
+ expires && expires < Time.now
62
+ end
63
+
64
+ # :api: private
65
+ def valid?(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) &&
74
+ uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
75
+ end
76
+
77
+ # :api: private
78
+ def matches?(uri)
79
+ ! expired? && valid?(uri)
80
+ end
81
+
82
+ # :api: private
83
+ def <=>(other)
84
+ # Orders the cookies from least specific to most
85
+ [name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
86
+ end
87
+
88
+ protected
89
+
90
+ def default_uri
91
+ URI.parse("//" + @default_host + "/")
92
+ end
93
+
94
+ end
95
+
96
+ class CookieJar
97
+
98
+ # :api: private
99
+ def initialize(cookies = [], default_host = DEFAULT_HOST)
100
+ @default_host = default_host
101
+ @cookies = cookies
102
+ @cookies.sort!
103
+ end
104
+
105
+ def [](name)
106
+ cookies = hash_for(nil)
107
+ # TODO: Should be case insensitive
108
+ cookies[name] && cookies[name].value
109
+ end
110
+
111
+ def []=(name, value)
112
+ # TODO: needs proper escaping
113
+ merge("#{name}=#{value}")
114
+ end
115
+
116
+ def merge(raw_cookies, uri = nil)
117
+ return unless raw_cookies
118
+
119
+ raw_cookies = raw_cookies.split("\n") if raw_cookies.is_a? String
120
+ raw_cookies.each do |raw_cookie|
121
+ cookie = Cookie.new(raw_cookie, uri, @default_host)
122
+ self << cookie if cookie.valid?(uri)
123
+ end
124
+ end
125
+
126
+ def <<(new_cookie)
127
+ @cookies.reject! do |existing_cookie|
128
+ new_cookie.replaces?(existing_cookie)
129
+ end
130
+
131
+ @cookies << new_cookie
132
+ @cookies.sort!
133
+ end
134
+
135
+ # :api: private
136
+ def for(uri)
137
+ hash_for(uri).values.map { |c| c.raw }.join(';')
138
+ end
139
+
140
+ def to_hash
141
+ cookies = {}
142
+
143
+ hash_for(nil).each do |name, cookie|
144
+ cookies[name] = cookie.value
145
+ end
146
+
147
+ return cookies
148
+ end
149
+
150
+ protected
151
+
152
+ def hash_for(uri = nil)
153
+ cookies = {}
154
+
155
+ # The cookies are sorted by most specific first. So, we loop through
156
+ # all the cookies in order and add it to a hash by cookie name if
157
+ # the cookie can be sent to the current URI. It's added to the hash
158
+ # so that when we are done, the cookies will be unique by name and
159
+ # we'll have grabbed the most specific to the URI.
160
+ @cookies.each do |cookie|
161
+ cookies[cookie.name] = cookie if cookie.matches?(uri)
162
+ end
163
+
164
+ return cookies
165
+ end
166
+
167
+ end
168
+
169
+ end
170
+ end
@@ -0,0 +1,73 @@
1
+ require "forwardable"
2
+
3
+ module Rack
4
+ module Test
5
+ module Methods
6
+ extend Forwardable
7
+
8
+ def rack_mock_session(name = :default)
9
+ return build_rack_mock_session unless name
10
+
11
+ @_rack_mock_sessions ||= {}
12
+ @_rack_mock_sessions[name] ||= build_rack_mock_session
13
+ end
14
+
15
+ def build_rack_mock_session
16
+ Rack::MockSession.new(app)
17
+ end
18
+
19
+ def rack_test_session(name = :default)
20
+ return build_rack_test_session(name) unless name
21
+
22
+ @_rack_test_sessions ||= {}
23
+ @_rack_test_sessions[name] ||= build_rack_test_session(name)
24
+ end
25
+
26
+ def build_rack_test_session(name)
27
+ Rack::Test::Session.new(rack_mock_session(name))
28
+ end
29
+
30
+ def current_session
31
+ rack_test_session(_current_session_names.last)
32
+ end
33
+
34
+ def with_session(name)
35
+ _current_session_names.push(name)
36
+ yield rack_test_session(name)
37
+ _current_session_names.pop
38
+ end
39
+
40
+ def _current_session_names
41
+ @_current_session_names ||= [:default]
42
+ end
43
+
44
+ METHODS = [
45
+ :request,
46
+
47
+ # HTTP verbs
48
+ :get,
49
+ :post,
50
+ :put,
51
+ :delete,
52
+ :head,
53
+
54
+ # Redirects
55
+ :follow_redirect!,
56
+
57
+ # Header-related features
58
+ :header,
59
+ :set_cookie,
60
+ :clear_cookies,
61
+ :authorize,
62
+ :basic_authorize,
63
+ :digest_authorize,
64
+
65
+ # Expose the last request and response
66
+ :last_response,
67
+ :last_request
68
+ ]
69
+
70
+ def_delegators :current_session, *METHODS
71
+ end
72
+ end
73
+ end
@@ -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
@@ -0,0 +1,36 @@
1
+ require "tempfile"
2
+
3
+ module Rack
4
+ module Test
5
+
6
+ class UploadedFile
7
+ # The filename, *not* including the path, of the "uploaded" file
8
+ attr_reader :original_filename
9
+
10
+ # The content type of the "uploaded" file
11
+ attr_accessor :content_type
12
+
13
+ def initialize(path, content_type = "text/plain", binary = false)
14
+ raise "#{path} file does not exist" unless ::File.exist?(path)
15
+ @content_type = content_type
16
+ @original_filename = ::File.basename(path)
17
+ @tempfile = Tempfile.new(@original_filename)
18
+ @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
19
+ @tempfile.binmode if binary
20
+ FileUtils.copy_file(path, @tempfile.path)
21
+ end
22
+
23
+ def path
24
+ @tempfile.path
25
+ end
26
+
27
+ alias_method :local_path, :path
28
+
29
+ def method_missing(method_name, *args, &block) #:nodoc:
30
+ @tempfile.__send__(method_name, *args, &block)
31
+ end
32
+
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,99 @@
1
+ module Rack
2
+ module Test
3
+
4
+ module Utils
5
+ include Rack::Utils
6
+
7
+ def build_nested_query(value, prefix = nil)
8
+ case value
9
+ when Array
10
+ value.map do |v|
11
+ build_nested_query(v, "#{prefix}[]")
12
+ end.join("&")
13
+ when Hash
14
+ value.map do |k, v|
15
+ build_nested_query(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
16
+ end.join("&")
17
+ else
18
+ "#{prefix}=#{escape(value)}"
19
+ end
20
+ end
21
+
22
+ module_function :build_nested_query
23
+
24
+ def build_multipart(params, first = true)
25
+ if first
26
+ unless params.is_a?(Hash)
27
+ raise ArgumentError, "value must be a Hash"
28
+ end
29
+
30
+ multipart = false
31
+ query = lambda { |value|
32
+ case value
33
+ when Array
34
+ value.each(&query)
35
+ when Hash
36
+ value.values.each(&query)
37
+ when UploadedFile
38
+ multipart = true
39
+ end
40
+ }
41
+ params.values.each(&query)
42
+ return nil unless multipart
43
+ end
44
+
45
+ flattened_params = Hash.new
46
+
47
+ params.each do |key, value|
48
+ k = first ? key.to_s : "[#{key}]"
49
+
50
+ case value
51
+ when Array
52
+ value.map { |v|
53
+ build_multipart(v, false).each { |subkey, subvalue|
54
+ flattened_params["#{k}[]#{subkey}"] = subvalue
55
+ }
56
+ }
57
+ when Hash
58
+ build_multipart(value, false).each { |subkey, subvalue|
59
+ flattened_params[k + subkey] = subvalue
60
+ }
61
+ else
62
+ flattened_params[k] = value
63
+ end
64
+ end
65
+
66
+ if first
67
+ flattened_params.map { |name, file|
68
+ if file.respond_to?(:original_filename)
69
+ ::File.open(file.path, "rb") do |f|
70
+ f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
71
+ <<-EOF
72
+ --#{MULTIPART_BOUNDARY}\r
73
+ Content-Disposition: form-data; name="#{name}"; filename="#{escape(file.original_filename)}"\r
74
+ Content-Type: #{file.content_type}\r
75
+ Content-Length: #{::File.stat(file.path).size}\r
76
+ \r
77
+ #{f.read}\r
78
+ EOF
79
+ end
80
+ else
81
+ <<-EOF
82
+ --#{MULTIPART_BOUNDARY}\r
83
+ Content-Disposition: form-data; name="#{name}"\r
84
+ \r
85
+ #{file}\r
86
+ EOF
87
+ end
88
+ }.join + "--#{MULTIPART_BOUNDARY}--\r"
89
+ else
90
+ flattened_params
91
+ end
92
+ end
93
+
94
+ module_function :build_multipart
95
+
96
+ end
97
+
98
+ end
99
+ end
data/rack-test.gemspec ADDED
@@ -0,0 +1,70 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rack-test}
8
+ s.version = "0.4.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Bryan Helmkamp"]
12
+ s.date = %q{2009-08-06}
13
+ s.email = %q{bryan@brynary.com}
14
+ s.extra_rdoc_files = [
15
+ "MIT-LICENSE.txt",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".document",
20
+ ".gitignore",
21
+ "History.txt",
22
+ "MIT-LICENSE.txt",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "lib/rack/mock_session.rb",
26
+ "lib/rack/test.rb",
27
+ "lib/rack/test/cookie_jar.rb",
28
+ "lib/rack/test/methods.rb",
29
+ "lib/rack/test/mock_digest_request.rb",
30
+ "lib/rack/test/uploaded_file.rb",
31
+ "lib/rack/test/utils.rb",
32
+ "rack-test.gemspec",
33
+ "spec/fixtures/config.ru",
34
+ "spec/fixtures/fake_app.rb",
35
+ "spec/fixtures/foo.txt",
36
+ "spec/rack/test/cookie_spec.rb",
37
+ "spec/rack/test/digest_auth_spec.rb",
38
+ "spec/rack/test/multipart_spec.rb",
39
+ "spec/rack/test/utils_spec.rb",
40
+ "spec/rack/test_spec.rb",
41
+ "spec/rcov.opts",
42
+ "spec/spec.opts",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+ s.homepage = %q{http://github.com/brynary/rack-test}
46
+ s.rdoc_options = ["--charset=UTF-8"]
47
+ s.require_paths = ["lib"]
48
+ s.rubyforge_project = %q{rack-test}
49
+ s.rubygems_version = %q{1.3.4}
50
+ s.summary = %q{Simple testing API built on Rack}
51
+ s.test_files = [
52
+ "spec/fixtures/fake_app.rb",
53
+ "spec/rack/test/cookie_spec.rb",
54
+ "spec/rack/test/digest_auth_spec.rb",
55
+ "spec/rack/test/multipart_spec.rb",
56
+ "spec/rack/test/utils_spec.rb",
57
+ "spec/rack/test_spec.rb",
58
+ "spec/spec_helper.rb"
59
+ ]
60
+
61
+ if s.respond_to? :specification_version then
62
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
63
+ s.specification_version = 3
64
+
65
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
66
+ else
67
+ end
68
+ else
69
+ end
70
+ end