mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
6cc03675d3
Currently Active Record can be configured via the environment variable `DATABASE_URL` or by manually injecting a hash of values which is what Rails does, reading in `database.yml` and setting Active Record appropriately. Active Record expects to be able to use `DATABASE_URL` without the use of Rails, and we cannot rip out this functionality without deprecating. This presents a problem though when both config is set, and a `DATABASE_URL` is present. Currently the `DATABASE_URL` should "win" and none of the values in `database.yml` are used. This is somewhat unexpected to me if I were to set values such as `pool` in the `production:` group of `database.yml` they are ignored. There are many ways that active record initiates a connection today: - Stand Alone (without rails) - `rake db:<tasks>` - ActiveRecord.establish_connection - With Rails - `rake db:<tasks>` - `rails <server> | <console>` - `rails dbconsole` We should make all of these behave exactly the same way. The best way to do this is to put all of this logic in one place so it is guaranteed to be used. Here is my prosed matrix of how this behavior should work: ``` No database.yml No DATABASE_URL => Error ``` ``` database.yml present No DATABASE_URL => Use database.yml configuration ``` ``` No database.yml DATABASE_URL present => use DATABASE_URL configuration ``` ``` database.yml present DATABASE_URL present => Merged into `url` sub key. If both specify `url` sub key, the `database.yml` `url` sub key "wins". If other paramaters `adapter` or `database` are specified in YAML, they are discarded as the `url` sub key "wins". ``` ### Implementation Current implementation uses `ActiveRecord::Base.configurations` to resolve and merge all connection information before returning. This is achieved through a utility class: `ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig`. To understand the exact behavior of this class, it is best to review the behavior in activerecord/test/cases/connection_adapters/connection_handler_test.rb though it should match the above proposal.
249 lines
7.4 KiB
Ruby
249 lines
7.4 KiB
Ruby
require 'abstract_unit'
|
|
require 'rails/commands/dbconsole'
|
|
|
|
class Rails::DBConsoleTest < ActiveSupport::TestCase
|
|
|
|
|
|
def setup
|
|
Rails::DBConsole.const_set('APP_PATH', 'rails/all')
|
|
end
|
|
|
|
def teardown
|
|
Rails::DBConsole.send(:remove_const, 'APP_PATH')
|
|
%w[PGUSER PGHOST PGPORT PGPASSWORD DATABASE_URL].each{|key| ENV.delete(key)}
|
|
end
|
|
|
|
def test_config_with_db_config_only
|
|
config_sample = {
|
|
"test"=> {
|
|
"adapter"=> "sqlite3",
|
|
"host"=> "localhost",
|
|
"port"=> "9000",
|
|
"database"=> "foo_test",
|
|
"user"=> "foo",
|
|
"password"=> "bar",
|
|
"pool"=> "5",
|
|
"timeout"=> "3000"
|
|
}
|
|
}
|
|
app_db_config(config_sample)
|
|
assert_equal Rails::DBConsole.new.config, config_sample["test"]
|
|
end
|
|
|
|
def test_config_with_no_db_config
|
|
app_db_config(nil)
|
|
assert_raise(ActiveRecord::AdapterNotSpecified) {
|
|
Rails::DBConsole.new.config
|
|
}
|
|
end
|
|
|
|
def test_config_with_database_url_only
|
|
ENV['DATABASE_URL'] = 'postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000'
|
|
app_db_config(nil)
|
|
expected = {
|
|
"adapter" => "postgresql",
|
|
"host" => "localhost",
|
|
"port" => 9000,
|
|
"database" => "foo_test",
|
|
"username" => "foo",
|
|
"password" => "bar",
|
|
"pool" => "5",
|
|
"timeout" => "3000"
|
|
}.sort
|
|
assert_equal expected, Rails::DBConsole.new.config.sort
|
|
end
|
|
|
|
def test_config_choose_database_url_if_exists
|
|
host = "database-url-host.com"
|
|
ENV['DATABASE_URL'] = "postgresql://foo:bar@#{host}:9000/foo_test?pool=5&timeout=3000"
|
|
sample_config = {
|
|
"test" => {
|
|
"adapter" => "postgresql",
|
|
"host" => "not-the-#{host}",
|
|
"port" => 9000,
|
|
"database" => "foo_test",
|
|
"username" => "foo",
|
|
"password" => "bar",
|
|
"pool" => "5",
|
|
"timeout" => "3000"
|
|
}
|
|
}
|
|
app_db_config(sample_config)
|
|
assert_equal host, Rails::DBConsole.new.config["host"]
|
|
end
|
|
|
|
def test_env
|
|
assert_equal Rails::DBConsole.new.environment, "test"
|
|
|
|
ENV['RAILS_ENV'] = nil
|
|
ENV['RACK_ENV'] = nil
|
|
|
|
Rails.stubs(:respond_to?).with(:env).returns(false)
|
|
assert_equal Rails::DBConsole.new.environment, "development"
|
|
|
|
ENV['RACK_ENV'] = "rack_env"
|
|
assert_equal Rails::DBConsole.new.environment, "rack_env"
|
|
|
|
ENV['RAILS_ENV'] = "rails_env"
|
|
assert_equal Rails::DBConsole.new.environment, "rails_env"
|
|
ensure
|
|
ENV['RAILS_ENV'] = "test"
|
|
end
|
|
|
|
def test_rails_env_is_development_when_argument_is_dev
|
|
Rails::DBConsole.stubs(:available_environments).returns(['development', 'test'])
|
|
options = Rails::DBConsole.new.send(:parse_arguments, ['dev'])
|
|
assert_match('development', options[:environment])
|
|
end
|
|
|
|
def test_rails_env_is_dev_when_argument_is_dev_and_dev_env_is_present
|
|
Rails::DBConsole.stubs(:available_environments).returns(['dev'])
|
|
options = Rails::DBConsole.new.send(:parse_arguments, ['dev'])
|
|
assert_match('dev', options[:environment])
|
|
end
|
|
|
|
def test_mysql
|
|
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], 'db')
|
|
start(adapter: 'mysql', database: 'db')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_mysql_full
|
|
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], '--host=locahost', '--port=1234', '--socket=socket', '--user=user', '--default-character-set=UTF-8', '-p', 'db')
|
|
start(adapter: 'mysql', database: 'db', host: 'locahost', port: 1234, socket: 'socket', username: 'user', password: 'qwerty', encoding: 'UTF-8')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_mysql_include_password
|
|
dbconsole.expects(:find_cmd_and_exec).with(%w[mysql mysql5], '--user=user', '--password=qwerty', 'db')
|
|
start({adapter: 'mysql', database: 'db', username: 'user', password: 'qwerty'}, ['-p'])
|
|
assert !aborted
|
|
end
|
|
|
|
def test_postgresql
|
|
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
|
|
start(adapter: 'postgresql', database: 'db')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_postgresql_full
|
|
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
|
|
start(adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3', host: 'host', port: 5432)
|
|
assert !aborted
|
|
assert_equal 'user', ENV['PGUSER']
|
|
assert_equal 'host', ENV['PGHOST']
|
|
assert_equal '5432', ENV['PGPORT']
|
|
assert_not_equal 'q1w2e3', ENV['PGPASSWORD']
|
|
end
|
|
|
|
def test_postgresql_include_password
|
|
dbconsole.expects(:find_cmd_and_exec).with('psql', 'db')
|
|
start({adapter: 'postgresql', database: 'db', username: 'user', password: 'q1w2e3'}, ['-p'])
|
|
assert !aborted
|
|
assert_equal 'user', ENV['PGUSER']
|
|
assert_equal 'q1w2e3', ENV['PGPASSWORD']
|
|
end
|
|
|
|
def test_sqlite
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite', 'db')
|
|
start(adapter: 'sqlite', database: 'db')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_sqlite3
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', Rails.root.join('db.sqlite3').to_s)
|
|
start(adapter: 'sqlite3', database: 'db.sqlite3')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_sqlite3_mode
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', '-html', Rails.root.join('db.sqlite3').to_s)
|
|
start({adapter: 'sqlite3', database: 'db.sqlite3'}, ['--mode', 'html'])
|
|
assert !aborted
|
|
end
|
|
|
|
def test_sqlite3_header
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', '-header', Rails.root.join('db.sqlite3').to_s)
|
|
start({adapter: 'sqlite3', database: 'db.sqlite3'}, ['--header'])
|
|
end
|
|
|
|
def test_sqlite3_db_absolute_path
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', '/tmp/db.sqlite3')
|
|
start(adapter: 'sqlite3', database: '/tmp/db.sqlite3')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_sqlite3_db_without_defined_rails_root
|
|
Rails.stubs(:respond_to?)
|
|
Rails.expects(:respond_to?).with(:root).once.returns(false)
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlite3', Rails.root.join('../config/db.sqlite3').to_s)
|
|
start(adapter: 'sqlite3', database: 'config/db.sqlite3')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_oracle
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlplus', 'user@db')
|
|
start(adapter: 'oracle', database: 'db', username: 'user', password: 'secret')
|
|
assert !aborted
|
|
end
|
|
|
|
def test_oracle_include_password
|
|
dbconsole.expects(:find_cmd_and_exec).with('sqlplus', 'user/secret@db')
|
|
start({adapter: 'oracle', database: 'db', username: 'user', password: 'secret'}, ['-p'])
|
|
assert !aborted
|
|
end
|
|
|
|
def test_unknown_command_line_client
|
|
start(adapter: 'unknown', database: 'db')
|
|
assert aborted
|
|
assert_match(/Unknown command-line client for db/, output)
|
|
end
|
|
|
|
def test_print_help_short
|
|
stdout = capture(:stdout) do
|
|
start({}, ['-h'])
|
|
end
|
|
assert aborted
|
|
assert_equal '', output
|
|
assert_match(/Usage:.*dbconsole/, stdout)
|
|
end
|
|
|
|
def test_print_help_long
|
|
stdout = capture(:stdout) do
|
|
start({}, ['--help'])
|
|
end
|
|
assert aborted
|
|
assert_equal '', output
|
|
assert_match(/Usage:.*dbconsole/, stdout)
|
|
end
|
|
|
|
attr_reader :aborted, :output
|
|
private :aborted, :output
|
|
|
|
private
|
|
|
|
def app_db_config(results)
|
|
Rails.application.config.stubs(:database_configuration).returns(results || {})
|
|
end
|
|
|
|
def dbconsole
|
|
@dbconsole ||= Rails::DBConsole.new(nil)
|
|
end
|
|
|
|
def start(config = {}, argv = [])
|
|
dbconsole.stubs(config: config.stringify_keys, arguments: argv)
|
|
capture_abort { dbconsole.start }
|
|
end
|
|
|
|
def capture_abort
|
|
@aborted = false
|
|
@output = capture(:stderr) do
|
|
begin
|
|
yield
|
|
rescue SystemExit
|
|
@aborted = true
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|