1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/railties/test/commands/dbconsole_test.rb
schneems 6cc03675d3 Ensure Active Record connection consistency
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.
2014-01-09 16:35:37 -06:00

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