require File.expand_path('../helper', __FILE__) 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?("show_exceptions 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?('
') refute body.include?('
') 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 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