mirror of
https://github.com/sinatra/sinatra
synced 2023-03-27 23:18:01 -04:00
f10e571f4d
We use minitest for Sinatra's test suite but we weren't using its rake task. I've updated the Rakefile to require and use Minitest default rake task to simplify. Another change is to rename the `helper.rb` file to `test_helper.rb` because I think that name is used more in the community and require it directly without calling `File.expand_path`
654 lines
17 KiB
Ruby
654 lines
17 KiB
Ruby
require_relative 'test_helper'
|
|
|
|
class SettingsTest < Minitest::Test
|
|
setup do
|
|
@base = Sinatra.new(Sinatra::Base)
|
|
@base.set :environment => :foo, :app_file => nil
|
|
|
|
@application = Sinatra.new(Sinatra::Application)
|
|
@application.set :environment => :foo, :app_file => nil
|
|
end
|
|
|
|
it 'sets settings to literal values' do
|
|
@base.set(:foo, 'bar')
|
|
assert @base.respond_to?(:foo)
|
|
assert_equal 'bar', @base.foo
|
|
end
|
|
|
|
it 'sets settings to Procs' do
|
|
@base.set(:foo, Proc.new { 'baz' })
|
|
assert @base.respond_to?(:foo)
|
|
assert_equal 'baz', @base.foo
|
|
end
|
|
|
|
it 'sets settings using a block' do
|
|
@base.set(:foo){ 'baz' }
|
|
assert @base.respond_to?(:foo)
|
|
assert_equal 'baz', @base.foo
|
|
end
|
|
|
|
it 'raises an error with a value and a block' do
|
|
assert_raises ArgumentError do
|
|
@base.set(:fiz, 'boom!'){ 'baz' }
|
|
end
|
|
assert !@base.respond_to?(:fiz)
|
|
end
|
|
|
|
it 'raises an error without value and block' do
|
|
assert_raises(ArgumentError) { @base.set(:fiz) }
|
|
assert !@base.respond_to?(:fiz)
|
|
end
|
|
|
|
it 'allows setting a value to the app class' do
|
|
@base.set :base, @base
|
|
assert @base.respond_to?(:base)
|
|
assert_equal @base, @base.base
|
|
end
|
|
|
|
it 'raises an error with the app class as value and a block' do
|
|
assert_raises ArgumentError do
|
|
@base.set(:fiz, @base) { 'baz' }
|
|
end
|
|
assert !@base.respond_to?(:fiz)
|
|
end
|
|
|
|
it "sets multiple settings with a Hash" do
|
|
@base.set :foo => 1234,
|
|
:bar => 'Hello World',
|
|
:baz => Proc.new { 'bizzle' }
|
|
assert_equal 1234, @base.foo
|
|
assert_equal 'Hello World', @base.bar
|
|
assert_equal 'bizzle', @base.baz
|
|
end
|
|
|
|
it 'sets multiple settings using #each' do
|
|
@base.set [["foo", "bar"]]
|
|
assert_equal "bar", @base.foo
|
|
end
|
|
|
|
it 'inherits settings methods when subclassed' do
|
|
@base.set :foo, 'bar'
|
|
@base.set :biz, Proc.new { 'baz' }
|
|
|
|
sub = Class.new(@base)
|
|
assert sub.respond_to?(:foo)
|
|
assert_equal 'bar', sub.foo
|
|
assert sub.respond_to?(:biz)
|
|
assert_equal 'baz', sub.biz
|
|
end
|
|
|
|
it 'overrides settings in subclass' do
|
|
@base.set :foo, 'bar'
|
|
@base.set :biz, Proc.new { 'baz' }
|
|
sub = Class.new(@base)
|
|
sub.set :foo, 'bling'
|
|
assert_equal 'bling', sub.foo
|
|
assert_equal 'bar', @base.foo
|
|
end
|
|
|
|
it 'creates setter methods when first defined' do
|
|
@base.set :foo, 'bar'
|
|
assert @base.respond_to?('foo=')
|
|
@base.foo = 'biz'
|
|
assert_equal 'biz', @base.foo
|
|
end
|
|
|
|
it 'creates predicate methods when first defined' do
|
|
@base.set :foo, 'hello world'
|
|
assert @base.respond_to?(:foo?)
|
|
assert @base.foo?
|
|
@base.set :foo, nil
|
|
assert !@base.foo?
|
|
end
|
|
|
|
it 'uses existing setter methods if detected' do
|
|
class << @base
|
|
def foo
|
|
@foo
|
|
end
|
|
def foo=(value)
|
|
@foo = 'oops'
|
|
end
|
|
end
|
|
|
|
@base.set :foo, 'bam'
|
|
assert_equal 'oops', @base.foo
|
|
end
|
|
|
|
it 'merges values of multiple set calls if those are hashes' do
|
|
@base.set :foo, :a => 1
|
|
sub = Class.new(@base)
|
|
sub.set :foo, :b => 2
|
|
assert_equal({:a => 1, :b => 2}, sub.foo)
|
|
end
|
|
|
|
it 'merging does not affect the superclass' do
|
|
@base.set :foo, :a => 1
|
|
sub = Class.new(@base)
|
|
sub.set :foo, :b => 2
|
|
assert_equal({:a => 1}, @base.foo)
|
|
end
|
|
|
|
it 'is possible to change a value from a hash to something else' do
|
|
@base.set :foo, :a => 1
|
|
@base.set :foo, :bar
|
|
assert_equal(:bar, @base.foo)
|
|
end
|
|
|
|
it 'merges values with values of the superclass if those are hashes' do
|
|
@base.set :foo, :a => 1
|
|
@base.set :foo, :b => 2
|
|
assert_equal({:a => 1, :b => 2}, @base.foo)
|
|
end
|
|
|
|
it "sets multiple settings to true with #enable" do
|
|
@base.enable :sessions, :foo, :bar
|
|
assert @base.sessions
|
|
assert @base.foo
|
|
assert @base.bar
|
|
end
|
|
|
|
it "sets multiple settings to false with #disable" do
|
|
@base.disable :sessions, :foo, :bar
|
|
assert !@base.sessions
|
|
assert !@base.foo
|
|
assert !@base.bar
|
|
end
|
|
|
|
it 'is accessible from instances via #settings' do
|
|
assert_equal :foo, @base.new!.settings.environment
|
|
end
|
|
|
|
it 'is accessible from class via #settings' do
|
|
assert_equal :foo, @base.settings.environment
|
|
end
|
|
|
|
describe 'default_content_type' do
|
|
it 'defaults to html' do
|
|
assert_equal 'text/html', @base.default_content_type
|
|
end
|
|
|
|
it 'can be changed' do
|
|
@base.set :default_content_type, 'application/json'
|
|
@base.get('/') { '{"a":1}' }
|
|
@app = @base
|
|
get '/'
|
|
assert_equal 200, status
|
|
assert_equal 'application/json', response.content_type
|
|
end
|
|
|
|
it 'can be disabled' do
|
|
@base.set :default_content_type, nil
|
|
@base.error(404) { "" }
|
|
@app = @base
|
|
get '/'
|
|
assert_equal 404, status
|
|
assert_nil response.content_type
|
|
assert_empty body
|
|
end
|
|
|
|
it 'may emit content without a content-type (to be sniffed)' do
|
|
@base.set :default_content_type, nil
|
|
@base.get('/') { raise Sinatra::BadRequest, "This is a drill" }
|
|
@app = @base
|
|
get '/'
|
|
assert_equal 400, status
|
|
assert_nil response.content_type
|
|
assert_body "This is a drill"
|
|
end
|
|
end
|
|
|
|
describe 'methodoverride' do
|
|
it 'is disabled on Base' do
|
|
assert ! @base.method_override?
|
|
end
|
|
|
|
it 'is enabled on Application' do
|
|
assert @application.method_override?
|
|
end
|
|
|
|
it 'enables MethodOverride middleware' do
|
|
@base.set :method_override, true
|
|
@base.put('/') { 'okay' }
|
|
@app = @base
|
|
post '/', {'_method'=>'PUT'}, {}
|
|
assert_equal 200, status
|
|
assert_equal 'okay', body
|
|
end
|
|
|
|
it 'is backward compatible with methodoverride' do
|
|
assert ! @base.methodoverride?
|
|
@base.enable :methodoverride
|
|
assert @base.methodoverride?
|
|
end
|
|
|
|
it 'ignores bundler/inline from callers' do
|
|
@application.stub(:caller, ->(_){ ['/path/to/bundler/inline.rb', $0] }) do
|
|
assert_equal File.expand_path($0), File.expand_path(@application.send(:caller_files).first)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'run' do
|
|
it 'is disabled on Base' do
|
|
assert ! @base.run?
|
|
end
|
|
|
|
it 'is enabled on Application except in test environment' do
|
|
assert @application.run?
|
|
|
|
@application.set :environment, :test
|
|
assert ! @application.run?
|
|
end
|
|
end
|
|
|
|
describe 'raise_errors' do
|
|
it 'is enabled on Base only in test' do
|
|
assert ! @base.raise_errors?
|
|
|
|
@base.set(:environment, :test)
|
|
assert @base.raise_errors?
|
|
end
|
|
|
|
it 'is enabled on Application only in test' do
|
|
assert ! @application.raise_errors?
|
|
|
|
@application.set(:environment, :test)
|
|
assert @application.raise_errors?
|
|
end
|
|
end
|
|
|
|
describe 'show_exceptions' do
|
|
it 'is disabled on Base except under development' do
|
|
assert ! @base.show_exceptions?
|
|
@base.environment = :development
|
|
assert @base.show_exceptions?
|
|
end
|
|
|
|
it 'is disabled on Application except in development' do
|
|
assert ! @application.show_exceptions?
|
|
|
|
@application.set(:environment, :development)
|
|
assert @application.show_exceptions?
|
|
end
|
|
|
|
it 'returns a friendly 500' do
|
|
klass = Sinatra.new(Sinatra::Application)
|
|
mock_app(klass) {
|
|
enable :show_exceptions
|
|
|
|
get '/' do
|
|
raise StandardError
|
|
end
|
|
}
|
|
|
|
get '/'
|
|
assert_equal 500, status
|
|
assert body.include?("StandardError")
|
|
assert body.include?("<code>show_exceptions</code> setting")
|
|
end
|
|
|
|
it 'does not attempt to show unparseable query parameters' do
|
|
klass = Sinatra.new(Sinatra::Application)
|
|
mock_app(klass) {
|
|
enable :show_exceptions
|
|
|
|
get '/' do
|
|
raise Sinatra::BadRequest
|
|
end
|
|
}
|
|
|
|
get '/'
|
|
assert_equal 400, status
|
|
refute body.include?('<div id="get">')
|
|
refute body.include?('<div id="post">')
|
|
end
|
|
|
|
it 'does not override app-specified error handling when set to :after_handler' do
|
|
ran = false
|
|
mock_app do
|
|
set :show_exceptions, :after_handler
|
|
error(RuntimeError) { ran = true }
|
|
get('/') { raise RuntimeError }
|
|
end
|
|
|
|
get '/'
|
|
assert_equal 500, status
|
|
assert ran
|
|
end
|
|
|
|
it 'does catch any other exceptions when set to :after_handler' do
|
|
ran = false
|
|
mock_app do
|
|
set :show_exceptions, :after_handler
|
|
error(RuntimeError) { ran = true }
|
|
get('/') { raise ArgumentError }
|
|
end
|
|
|
|
get '/'
|
|
assert_equal 500, status
|
|
assert !ran
|
|
end
|
|
end
|
|
|
|
describe 'dump_errors' do
|
|
it 'is disabled on Base in test' do
|
|
@base.environment = :test
|
|
assert ! @base.dump_errors?
|
|
@base.environment = :development
|
|
assert @base.dump_errors?
|
|
@base.environment = :production
|
|
assert @base.dump_errors?
|
|
end
|
|
|
|
it 'dumps exception with backtrace to rack.errors' do
|
|
klass = Sinatra.new(Sinatra::Application)
|
|
|
|
mock_app(klass) {
|
|
enable :dump_errors
|
|
disable :raise_errors
|
|
|
|
error do
|
|
error = @env['rack.errors'].instance_variable_get(:@error)
|
|
error.rewind
|
|
|
|
error.read
|
|
end
|
|
|
|
get '/' do
|
|
raise
|
|
end
|
|
}
|
|
|
|
get '/'
|
|
assert body.include?("RuntimeError") && body.include?("settings_test.rb")
|
|
end
|
|
|
|
it 'does not dump 404 errors' do
|
|
klass = Sinatra.new(Sinatra::Application)
|
|
|
|
mock_app(klass) {
|
|
enable :dump_errors
|
|
disable :raise_errors
|
|
|
|
error do
|
|
error = @env['rack.errors'].instance_variable_get(:@error)
|
|
error.rewind
|
|
|
|
error.read
|
|
end
|
|
|
|
get '/' do
|
|
raise Sinatra::NotFound
|
|
end
|
|
}
|
|
|
|
get '/'
|
|
assert !body.include?("NotFound") && !body.include?("settings_test.rb")
|
|
end
|
|
end
|
|
|
|
describe 'sessions' do
|
|
it 'is disabled on Base' do
|
|
assert ! @base.sessions?
|
|
end
|
|
|
|
it 'is disabled on Application' do
|
|
assert ! @application.sessions?
|
|
end
|
|
end
|
|
|
|
describe 'logging' do
|
|
it 'is disabled on Base' do
|
|
assert ! @base.logging?
|
|
end
|
|
|
|
it 'is enabled on Application except in test environment' do
|
|
assert @application.logging?
|
|
|
|
@application.set :environment, :test
|
|
assert ! @application.logging
|
|
end
|
|
end
|
|
|
|
describe 'static' do
|
|
it 'is disabled on Base by default' do
|
|
assert ! @base.static?
|
|
end
|
|
|
|
it 'is enabled on Base when public_folder is set and exists' do
|
|
@base.set :environment, :development
|
|
@base.set :public_folder, __dir__
|
|
assert @base.static?
|
|
end
|
|
|
|
it 'is enabled on Base when root is set and root/public_folder exists' do
|
|
@base.set :environment, :development
|
|
@base.set :root, __dir__
|
|
assert @base.static?
|
|
end
|
|
|
|
it 'is disabled on Application by default' do
|
|
assert ! @application.static?
|
|
end
|
|
|
|
it 'is enabled on Application when public_folder is set and exists' do
|
|
@application.set :environment, :development
|
|
@application.set :public_folder, __dir__
|
|
assert @application.static?
|
|
end
|
|
|
|
it 'is enabled on Application when root is set and root/public_folder exists' do
|
|
@application.set :environment, :development
|
|
@application.set :root, __dir__
|
|
assert @application.static?
|
|
end
|
|
|
|
it 'is possible to use Module#public' do
|
|
@base.send(:define_method, :foo) { }
|
|
@base.send(:private, :foo)
|
|
assert !@base.public_method_defined?(:foo)
|
|
@base.send(:public, :foo)
|
|
assert @base.public_method_defined?(:foo)
|
|
end
|
|
|
|
it 'is possible to use the keyword public in a sinatra app' do
|
|
app = Sinatra.new do
|
|
private
|
|
def priv; end
|
|
public
|
|
def pub; end
|
|
end
|
|
assert !app.public_method_defined?(:priv)
|
|
assert app.public_method_defined?(:pub)
|
|
end
|
|
end
|
|
|
|
describe 'bind' do
|
|
it 'defaults to 0.0.0.0' do
|
|
assert_equal '0.0.0.0', @base.bind
|
|
assert_equal '0.0.0.0', @application.bind
|
|
end
|
|
end
|
|
|
|
describe 'port' do
|
|
it 'defaults to 4567' do
|
|
assert_equal 4567, @base.port
|
|
assert_equal 4567, @application.port
|
|
end
|
|
end
|
|
|
|
describe 'server' do
|
|
it 'includes webrick' do
|
|
assert @base.server.include?('webrick')
|
|
assert @application.server.include?('webrick')
|
|
end
|
|
|
|
it 'includes puma' do
|
|
assert @base.server.include?('puma')
|
|
assert @application.server.include?('puma')
|
|
end
|
|
|
|
it 'includes falcon on non-jruby' do
|
|
if RUBY_ENGINE != 'jruby'
|
|
assert @base.server.include?('falcon')
|
|
assert @application.server.include?('falcon')
|
|
else
|
|
assert !@base.server.include?('falcon')
|
|
assert !@application.server.include?('falcon')
|
|
end
|
|
end
|
|
end
|
|
|
|
describe 'app_file' do
|
|
it 'is nil for base classes' do
|
|
assert_nil Sinatra::Base.app_file
|
|
assert_nil Sinatra::Application.app_file
|
|
end
|
|
|
|
it 'defaults to the file subclassing' do
|
|
assert_equal File.expand_path(__FILE__), Sinatra.new.app_file
|
|
end
|
|
end
|
|
|
|
describe 'root' do
|
|
it 'is nil if app_file is not set' do
|
|
assert_nil @base.root
|
|
assert_nil @application.root
|
|
end
|
|
|
|
it 'is equal to the expanded basename of app_file' do
|
|
@base.app_file = __FILE__
|
|
assert_equal File.expand_path(__dir__), @base.root
|
|
|
|
@application.app_file = __FILE__
|
|
assert_equal File.expand_path(__dir__), @application.root
|
|
end
|
|
end
|
|
|
|
describe 'views' do
|
|
it 'is nil if root is not set' do
|
|
assert_nil @base.views
|
|
assert_nil @application.views
|
|
end
|
|
|
|
it 'is set to root joined with views/' do
|
|
@base.root = __dir__
|
|
assert_equal __dir__ + "/views", @base.views
|
|
|
|
@application.root = __dir__
|
|
assert_equal __dir__ + "/views", @application.views
|
|
end
|
|
end
|
|
|
|
describe 'public_folder' do
|
|
it 'is nil if root is not set' do
|
|
assert_nil @base.public_folder
|
|
assert_nil @application.public_folder
|
|
end
|
|
|
|
it 'is set to root joined with public/' do
|
|
@base.root = __dir__
|
|
assert_equal __dir__ + "/public", @base.public_folder
|
|
|
|
@application.root = __dir__
|
|
assert_equal __dir__ + "/public", @application.public_folder
|
|
end
|
|
end
|
|
|
|
describe 'public_dir' do
|
|
it 'is an alias for public_folder' do
|
|
@base.public_dir = __dir__
|
|
assert_equal __dir__, @base.public_dir
|
|
assert_equal @base.public_folder, @base.public_dir
|
|
|
|
@application.public_dir = __dir__
|
|
assert_equal __dir__, @application.public_dir
|
|
assert_equal @application.public_folder, @application.public_dir
|
|
end
|
|
end
|
|
|
|
describe 'lock' do
|
|
it 'is disabled by default' do
|
|
assert ! @base.lock?
|
|
assert ! @application.lock?
|
|
end
|
|
end
|
|
|
|
describe 'protection' do
|
|
class MiddlewareTracker < Rack::Builder
|
|
def self.track
|
|
Rack.send :remove_const, :Builder
|
|
Rack.const_set :Builder, MiddlewareTracker
|
|
MiddlewareTracker.used.clear
|
|
yield
|
|
ensure
|
|
Rack.send :remove_const, :Builder
|
|
Rack.const_set :Builder, MiddlewareTracker.superclass
|
|
end
|
|
|
|
def self.used
|
|
@used ||= []
|
|
end
|
|
|
|
def use(middleware, *)
|
|
MiddlewareTracker.used << middleware
|
|
super
|
|
end
|
|
end
|
|
|
|
it 'sets up Rack::Protection' do
|
|
MiddlewareTracker.track do
|
|
Sinatra::Base.new
|
|
assert_include MiddlewareTracker.used, Rack::Protection
|
|
end
|
|
end
|
|
|
|
it 'sets up Rack::Protection::PathTraversal' do
|
|
MiddlewareTracker.track do
|
|
Sinatra::Base.new
|
|
assert_include MiddlewareTracker.used, Rack::Protection::PathTraversal
|
|
end
|
|
end
|
|
|
|
it 'does not set up Rack::Protection::PathTraversal when disabling it' do
|
|
MiddlewareTracker.track do
|
|
Sinatra.new { set :protection, :except => :path_traversal }.new
|
|
assert_include MiddlewareTracker.used, Rack::Protection
|
|
assert !MiddlewareTracker.used.include?(Rack::Protection::PathTraversal)
|
|
end
|
|
end
|
|
|
|
it 'sets up RemoteToken if sessions are enabled' do
|
|
MiddlewareTracker.track do
|
|
Sinatra.new { enable :sessions }.new
|
|
assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken
|
|
end
|
|
end
|
|
|
|
it 'sets up RemoteToken if sessions are enabled with a custom session store' do
|
|
MiddlewareTracker.track do
|
|
Sinatra.new {
|
|
enable :sessions
|
|
set :session_store, Rack::Session::Pool
|
|
}.new
|
|
assert_include MiddlewareTracker.used, Rack::Session::Pool
|
|
assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken
|
|
end
|
|
end
|
|
|
|
it 'does not set up RemoteToken if sessions are disabled' do
|
|
MiddlewareTracker.track do
|
|
Sinatra.new.new
|
|
assert !MiddlewareTracker.used.include?(Rack::Protection::RemoteToken)
|
|
end
|
|
end
|
|
|
|
it 'sets up RemoteToken if it is configured to' do
|
|
MiddlewareTracker.track do
|
|
Sinatra.new { set :protection, :session => true }.new
|
|
assert_include MiddlewareTracker.used, Rack::Protection::RemoteToken
|
|
end
|
|
end
|
|
end
|
|
end
|