eol_rackbox 1.1.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -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