eol_rackbox 1.1.7

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,30 @@
1
+ class RackBox
2
+
3
+ # Custom RSpec matchers
4
+ module Matchers
5
+
6
+ def self.included base
7
+
8
+ # this should really just be matcher(:foo){ ... }
9
+ # but there's a bit of other meta logic to deal with here
10
+ Object.send :remove_const, :RedirectTo if defined? RedirectTo
11
+ undef redirect_to if defined? redirect_to
12
+
13
+ # the actual matcher logic
14
+ matcher(:redirect_to, base) do |response, url|
15
+ return false unless response['Location']
16
+ if url =~ /^\//
17
+ # looking for a relative match, eg. should redirect_to('/login')
18
+ relative_location = response['Location'].sub(/^https?:\/\//,'').sub(/^[^\/]*/,'')
19
+ # ^ there's probably a helper on Rack or CGI to do this
20
+ relative_location.downcase == url.downcase
21
+ else
22
+ response['Location'].downcase == url.downcase
23
+ end
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,19 @@
1
+ # An evil fix
2
+ #
3
+ # The actual fix
4
+ # has been pulled upstream into Rack
5
+ # but hasn't made it into the Rack gem yet, so we need this fix until the new gem is released
6
+ #
7
+ class Rack::MockRequest
8
+ class << self
9
+
10
+ alias env_for_without_content_length_fix env_for
11
+ def env_for_with_content_length_fix uri = '', opts = {}
12
+ env = env_for_without_content_length_fix uri, opts
13
+ env['CONTENT_LENGTH'] ||= env['rack.input'].length
14
+ env
15
+ end
16
+ alias env_for env_for_with_content_length_fix
17
+
18
+ end
19
+ end
@@ -0,0 +1,33 @@
1
+ # some more Rack extensions to help when testing
2
+ class Rack::MockResponse
3
+
4
+ # TODO checkout Rack::Response::Helpers which implements many of these!
5
+
6
+ # these methods help with RSpec specs so we can ask things like:
7
+ #
8
+ # request('/').should be_successful
9
+ # request('/').should be_redirect
10
+ # request('/').should be_error
11
+ #
12
+
13
+ def success?
14
+ self.status.to_s.start_with?'2' # 200 status codes are successful
15
+ end
16
+
17
+ def redirect?
18
+ self.status.to_s.start_with?'3' # 300 status codes are redirects
19
+ end
20
+
21
+ def client_error?
22
+ self.status.to_s.start_with?'4' # 400 status codes are client errors
23
+ end
24
+
25
+ def server_error?
26
+ self.status.to_s.start_with?'5' # 500 status codes are server errors
27
+ end
28
+
29
+ def error?
30
+ self.status.to_s.start_with?('4') || self.status.to_s.start_with?('5') # 400 & 500 status codes are errors
31
+ end
32
+
33
+ end
@@ -0,0 +1,54 @@
1
+ #
2
+ # little extension to Rack::MockRequest to track cookies
3
+ #
4
+ class Rack::MockRequest
5
+ # cookies is a hash of persistent cookies (by domain)
6
+ # that let you test cookies for your app
7
+ #
8
+ # cookies = {
9
+ # 'example.org' => {
10
+ # 'cookie-name' => 'cookie-value',
11
+ # 'chunky' => 'bacon'
12
+ # }
13
+ # }
14
+ attr_accessor :cookies
15
+
16
+ # shortcut to get cookies for a particular domain
17
+ def cookies_for domain
18
+ @cookies ||= {}
19
+ @cookies[ domain ]
20
+ end
21
+
22
+ # oh geez ... it looks like i basically copy/pasted this. there's gotta be a way to do this that's
23
+ # more resilient to Rack changes to this method. i don't like overriding the whole method!
24
+ #
25
+ def request method = "GET", uri = "", opts = { }
26
+
27
+ env = self.class.env_for(uri, opts.merge(:method => method))
28
+
29
+ unless @cookies.nil? or @cookies.empty? or @cookies[env['SERVER_NAME']].nil? or @cookies[env['SERVER_NAME']].empty?
30
+ env['HTTP_COOKIE'] = @cookies[env['SERVER_NAME']].map{ |k,v| "#{ k }=#{ v }" }.join('; ')
31
+ end
32
+
33
+ if opts[:lint]
34
+ app = Rack::Lint.new(@app)
35
+ else
36
+ app = @app
37
+ end
38
+
39
+ errors = env["rack.errors"]
40
+ response = Rack::MockResponse.new(*(app.call(env) + [errors]))
41
+
42
+ if response.original_headers['Set-Cookie']
43
+ @cookies ||= {}
44
+ @cookies[ env['SERVER_NAME'] ] ||= {}
45
+ response.headers['Set-Cookie'].map{ |str| /(.*); path/.match(str)[1] }.each do |cookie|
46
+ name, value = cookie.split('=').first, cookie.split('=')[1]
47
+ @cookies[ env['SERVER_NAME'] ][ name ] = value
48
+ end
49
+ end
50
+
51
+ response
52
+ end
53
+
54
+ end
@@ -0,0 +1,186 @@
1
+
2
+ # To add blackbox testing to a Rails app,
3
+ # in your spec_helper.rb
4
+ #
5
+ # require 'rackbox'
6
+ #
7
+ # Spec::Runner.configure do |config|
8
+ # config.use_blackbox = true
9
+ # end
10
+ #
11
+ class RackBox
12
+
13
+ # i am an rdoc comment on RackBox's eigenclass
14
+ class << self
15
+
16
+ # to turn on some verbosity / logging, set:
17
+ # RackBox.verbose = true
18
+ attr_accessor :verbose
19
+
20
+ # A port of Merb's request() method, used in tests
21
+ #
22
+ # At the moment, we're using #req instead because #request conflicts
23
+ # with an existing RSpec-Rails method
24
+ #
25
+ # Usage:
26
+ #
27
+ # req '/'
28
+ # req login_path
29
+ # req url_for(:controller => 'login')
30
+ #
31
+ # req '/', :method => :post, :params => { 'chunky' => 'bacon' }
32
+ #
33
+ # req '/', :data => "some XML data to POST"
34
+ #
35
+ # TODO take any additional options and pass them along to the environment, so we can say
36
+ # req '/', :user_agent => 'some custom user agent'
37
+ #
38
+ def req app_or_request, url = nil, options = {}
39
+ puts "RackBox#request url:#{ url.inspect }, options:#{ options.inspect }" if RackBox.verbose
40
+
41
+ # handle RackBox.request '/foo'
42
+ if app_or_request.is_a?(String) && ( url.nil? || url.is_a?(Hash) )
43
+ options = url || {}
44
+ url = app_or_request
45
+ app_or_request = RackBox.app
46
+ end
47
+
48
+ # need to find the request or app
49
+ mock_request = nil
50
+ if app_or_request.is_a? Rack::MockRequest
51
+ mock_request = app_or_request
52
+ elsif app_or_request.nil?
53
+ if RackBox.app.nil?
54
+ raise "Not sure howto to execute a request against app or request: #{ app_or_request.inspect }"
55
+ else
56
+ mock_request = Rack::MockRequest.new(RackBox.app) # default to RackBox.app if nil
57
+ end
58
+ elsif app_or_request.respond_to? :call
59
+ mock_request = Rack::MockRequest.new(app_or_request)
60
+ else
61
+ raise "Not sure howto to execute a request against app or request: #{ app_or_request.inspect }"
62
+ end
63
+
64
+ options[:method] ||= ( options[:params] || options[:data] ) ? :post : :get # if params, default to POST, else default to GET
65
+ options[:params] ||= { }
66
+
67
+ if options[:data]
68
+ # input should be the data we're likely POSTing ... this overrides any params
69
+ input = options[:data]
70
+ else
71
+ # input should be params, if any
72
+ input = RackBox.build_query options[:params]
73
+ end
74
+
75
+ # add HTTP BASIC AUTH support
76
+ #
77
+ # TODO: DRY this up!
78
+ #
79
+ if options[:auth]
80
+ options[:http_basic_authentication] = options[:auth]
81
+ options.delete :auth
82
+ end
83
+ if options[:basic_auth]
84
+ options[:http_basic_authentication] = options[:basic_auth]
85
+ options.delete :basic_auth
86
+ end
87
+ if options[:http_basic_authentication]
88
+ username, password = options[:http_basic_authentication]
89
+ options.delete :http_basic_authentication
90
+ require 'base64'
91
+ # for some reason, nase64 encoding adds a \n
92
+ encoded_username_and_password = Base64.encode64("#{ username }:#{ password }").sub(/\n$/, '')
93
+ options['HTTP_AUTHORIZATION'] = "Basic #{ encoded_username_and_password }"
94
+ end
95
+
96
+ headers = options.dup
97
+ headers.delete :data if headers[:data]
98
+ headers.delete :params if headers[:params]
99
+ headers.delete :method if headers[:method]
100
+
101
+ # merge input
102
+ headers[:input] = input
103
+
104
+ puts " requesting #{ options[:method].to_s.upcase } #{ url.inspect } #{ headers.inspect }" if RackBox.verbose
105
+ mock_request.send options[:method], url, headers
106
+ end
107
+
108
+ alias request req unless defined? request
109
+
110
+ # the Rack appliction to do 'Black Box' testing against
111
+ #
112
+ # To set, in your spec_helper.rb or someplace:
113
+ # RackBox.app = Rack::Adapter::Rails.new :root => '/root/directory/of/rails/app', :environment => 'test'
114
+ #
115
+ # If not explicitly set, uses RAILS_ROOT (if defined?) and RAILS_ENV (if defined?)
116
+ attr_accessor :app
117
+
118
+ def app options = { }
119
+ unless @app and @app.respond_to?:call
120
+
121
+ options = {
122
+ :silent => false
123
+ }.merge(options)
124
+
125
+ if File.file? 'config.ru'
126
+ @app = Rack::Builder.new { eval(File.read('config.ru')) }
127
+
128
+ elsif defined?RAILS_ENV and defined?RAILS_ROOT
129
+ unless defined?Rack::Adapter::Rails
130
+ # TODO this is no longer true ... right? does rails < 2.3 work without Thin
131
+ puts "You need the Rack::Adapter::Rails to run Rails apps with RackBox." +
132
+ " Try: sudo gem install thin" unless options[:silent]
133
+ else
134
+ @app = Rack::Adapter::Rails.new :root => RAILS_ROOT, :environment => RAILS_ENV
135
+ end
136
+
137
+ elsif File.file?('config/routes.rb') && File.file?('config/environment.rb')
138
+ unless defined?Rack::Adapter::Rails
139
+ puts "You need the Rack::Adapter::Rails to run Rails apps with RackBox." +
140
+ " Try: sudo gem install thin" unless options[:silent]
141
+ else
142
+ @app = Rack::Adapter::Rails.new :root => '.', :environment => 'development'
143
+ end
144
+
145
+ else
146
+ puts "RackBox.app not configured." unless options[:silent]
147
+
148
+ end
149
+ end
150
+
151
+ @app
152
+ end
153
+
154
+ # helper method for taking a Hash of params and turning them into POST params
155
+ #
156
+ # >> RackBox.build_query :hello => 'there'
157
+ # => 'hello=there'
158
+ #
159
+ # >> RackBox.build_query :hello => 'there', :foo => 'bar'
160
+ # => 'hello=there&foo=bar'
161
+ #
162
+ # >> RackBox.build_query :user => { :name => 'bob', :password => 'secret' }
163
+ # => 'user[name]=bob&user[password]=secret'
164
+ #
165
+ def build_query params_hash = { }
166
+ # check to make sure no values are Hashes ...
167
+ # if they are, we need to flatten them!
168
+ params_hash.each do |key, value|
169
+ # params_hash :a => { :b => X, :c => Y }
170
+ # needs to be 'a[b]' => X, 'a[b]' => Y
171
+ if value.is_a? Hash
172
+ inner_hash = params_hash.delete key # { :b => X, :c => Y }
173
+ inner_hash.each do |subkey, subvalue|
174
+ new_key = "#{ key }[#{ subkey }]" # a[b] or a[c]
175
+ puts "warning: overwriting query parameter #{ new_key }" if params_hash[new_key]
176
+ params_hash[new_key] = subvalue # 'a[b]' => X or a[c] => Y
177
+ end
178
+ # we really shouldn't keep going thru the #each now that we've altered data!
179
+ return build_query(params_hash)
180
+ end
181
+ end
182
+ Rack::Utils.build_query params_hash
183
+ end
184
+
185
+ end
186
+ end
@@ -0,0 +1,10 @@
1
+ # this should get you up and running for using RackBox with RSpec
2
+ require File.dirname(__FILE__) + '/../rackbox'
3
+
4
+ spec_configuration = nil
5
+ spec_configuration = Spec::Example if defined? Spec::Example
6
+ spec_configuration = Spec::Runner if defined? Spec::Runner
7
+
8
+ spec_configuration.configure do |config|
9
+ config.use_blackbox = true
10
+ end
@@ -0,0 +1,75 @@
1
+ # Extend the RSpec configuration class with a use_blackbox option
2
+ #
3
+ # To add blackbox testing to a Rails app,
4
+ # in your spec_helper.rb
5
+ #
6
+ # require 'rackbox'
7
+ #
8
+ # Spec::Runner.configure do |config|
9
+ # config.use_blackbox = true
10
+ # end
11
+ #
12
+
13
+ spec_configuration_class = nil
14
+ spec_configuration_class = Spec::Example::Configuration if defined? Spec::Example::Configuration
15
+ spec_configuration_class = Spec::Runner::Configuration if defined? Spec::Runner::Configuration
16
+
17
+ if spec_configuration_class
18
+ spec_configuration_class.class_eval do
19
+ # Adds blackbox testing to your Rails application using RackBox.
20
+ #
21
+ # To use, put your 'blackbox' specs into the spec/blackbox
22
+ # directory, eg. spec/blackbox/login_spec.rb
23
+ #
24
+ # In these specs, the RackBox::SpecHelpers#req method will be available to you
25
+ #
26
+ def use_blackbox= bool
27
+ if bool == true
28
+
29
+ before(:all, :type => :blackbox) do
30
+ self.class.instance_eval {
31
+ # include our own helpers, eg. RackBox::SpecHelpers#req
32
+ include RackBox::SpecHelpers
33
+ include RackBox::Matchers
34
+
35
+ # include generated url methods, eg. login_path.
36
+ # default_url_options needs to have a host set for the Urls to work
37
+ if defined?ActionController::UrlWriter
38
+ include ActionController::UrlWriter
39
+ default_url_options[:host] = 'example.com'
40
+ end
41
+
42
+ # if we're not in a Rails app, let's try to load matchers from Webrat
43
+ unless defined?RAILS_ENV
44
+ begin
45
+ require 'webrat'
46
+ require 'webrat/core/matchers'
47
+ include Webrat::HaveTagMatcher
48
+ # include Webrat::HasContent
49
+ rescue LoadError
50
+ puts "Webrat not available. have_tag & other matchers won't be available. to install, sudo gem install webrat"
51
+ end
52
+ end
53
+
54
+ attr_accessor :rackbox_request
55
+ }
56
+ end
57
+
58
+ before(:each, :type => :blackbox) do
59
+
60
+ # i'm sure there's a better way to write this!
61
+ #
62
+ # i believe metaid would write this as:
63
+ # metaclass.class_eval do ... end
64
+ #
65
+ (class << self; self; end).class_eval do
66
+ include RackBox::Matchers
67
+ end
68
+
69
+ @rackbox_request = Rack::MockRequest.new RackBox.app
70
+ end
71
+
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,23 @@
1
+ class RackBox
2
+
3
+ # Helper methods to include in specs that want to use blackbox testing
4
+ #
5
+ # TODO For backwards compatibility, I would like to keep a SpecHelpers
6
+ # module, but this needs to be renamed because this isn't spec
7
+ # specific at all! it needs to be easy to RackBox::App.new(rack_app).request
8
+ # or something like that (something generic)
9
+ #
10
+ # This module has the RackBox::SpecHelpers#request method, which is
11
+ # the main method used by RackBox blackbox tests
12
+ #
13
+ module SpecHelpers
14
+
15
+ # moved logic into RackBox#request, where it can easily be re-used
16
+ def req url, options = {}
17
+ RackBox.request @rackbox_request, url, options
18
+ end
19
+
20
+ alias request req unless defined? request
21
+
22
+ end
23
+ end
@@ -0,0 +1 @@
1
+ # this should get you up and running for using RackBox with test/unit
@@ -0,0 +1,52 @@
1
+ # created by: http://github.com/xdotcommer
2
+ # from: http://github.com/xdotcommer/rspec-custom-matchers/blob/0ecfccd659d5038cdfc88fdc1fee08373e1ee75c/custom_matcher.rb
3
+ class CustomMatcher
4
+ def self.create(class_name, &block)
5
+ klass = Class.new(CustomMatcher)
6
+ klass.send(:define_method, :matcher, &block) if block_given?
7
+ Object.const_set(build_class_name(class_name), klass)
8
+ end
9
+
10
+ def initialize(expected = nil)
11
+ @expected = expected
12
+ end
13
+
14
+ def failure_message
15
+ message
16
+ end
17
+
18
+ def negative_failure_message
19
+ message(false)
20
+ end
21
+
22
+ def matcher(target, expected)
23
+ target == expected
24
+ end
25
+
26
+ def matches?(target)
27
+ @target = target
28
+ if self.method(:matcher).arity == 2
29
+ matcher(@target, @expected)
30
+ else
31
+ matcher(@target)
32
+ end
33
+ end
34
+
35
+ private
36
+ def message(positive = true)
37
+ "#{positive ? 'Expected' : 'Did not expect'} #{@target.inspect} to #{class_display_name} #{@expected.inspect if self.method(:matcher).arity == 2}"
38
+ end
39
+
40
+ def class_display_name
41
+ self.class.to_s.gsub(/[A-Z]/) {|m| ' ' + m.downcase }.lstrip
42
+ end
43
+
44
+ def self.build_class_name(class_name)
45
+ class_name.to_s.split('_').map {|s| s.capitalize}.join
46
+ end
47
+ end
48
+
49
+ def matcher(name, context = self.class, &block)
50
+ klass = CustomMatcher.create(name, &block)
51
+ context.send(:define_method, name) { |*args| klass.new(*args) }
52
+ end