diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2014-01-10 11:00:30 -0800 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2014-01-10 11:00:30 -0800 |
commit | caa981d88112f019ade868f75af6b5f399c244a4 (patch) | |
tree | b760cd5a28612eae612f6055680083cff206b043 /activerecord/test/cases | |
parent | 71d19f15171e59b5dfddd66f3fa3a234c3f7911d (diff) | |
parent | bd1bf2c5b8480fae0379890347d58b9fe8231e3e (diff) | |
download | rails-caa981d88112f019ade868f75af6b5f399c244a4.tar.gz rails-caa981d88112f019ade868f75af6b5f399c244a4.tar.bz2 rails-caa981d88112f019ade868f75af6b5f399c244a4.zip |
Merge branch 'master' into laurocaetano-fix_send_file
* master: (536 commits)
doc, API example on how to use `Model#exists?` with multiple IDs. [ci skip]
Restore DATABASE_URL even if it's nil in connection_handler test
[ci skip] - error_messages_for has been deprecated since 2.3.8 - lets reduce any confusion for users
Ensure Active Record connection consistency
Revert "ask the fixture set for the sql statements"
Check `respond_to` before delegation due to: https://github.com/ruby/ruby/commit/d781caaf313b8649948c107bba277e5ad7307314
Adding Hash#compact and Hash#compact! methods
MySQL version 4.1 was EOL on December 31, 2009 We should at least recommend modern versions of MySQL to users.
clear cache on body close so that cache remains during rendering
add a more restricted codepath for templates fixes #13390
refactor generator tests to use block form of Tempfile
Fix typo [ci skip]
Move finish_template as the last public method in the generator
Minor typos fix [ci skip]
make `change_column_null` reversible. Closes #13576.
create/drop test and development databases only if RAILS_ENV is nil
Revert "Speedup String#to"
typo fix in test name. [ci skip].
`core_ext/string/access.rb` test what we are documenting.
Fix typo in image_tag documentation
...
Conflicts:
actionpack/CHANGELOG.md
Diffstat (limited to 'activerecord/test/cases')
51 files changed, 1107 insertions, 477 deletions
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 595edc6263..b67e70ec7e 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -92,7 +92,7 @@ module ActiveRecord ) end ensure - ActiveRecord::Base.establish_connection 'arunit' + ActiveRecord::Base.establish_connection :arunit end end end @@ -178,6 +178,10 @@ module ActiveRecord result = @connection.select_all "SELECT * FROM posts" assert result.is_a?(ActiveRecord::Result) end + + test "type_to_sql returns a String for unmapped types" do + assert_equal "special_db_type", @connection.type_to_sql(:special_db_type) + end end class AdapterTestWithoutTransaction < ActiveRecord::TestCase @@ -187,7 +191,7 @@ module ActiveRecord end def setup - Klass.establish_connection 'arunit' + Klass.establish_connection :arunit @connection = Klass.connection end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index a1b41b6991..5cd5d8ac5f 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -71,7 +71,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_exec_no_binds @connection.exec_query('drop table if exists ex') @connection.exec_query(<<-eosql) - CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, + CREATE TABLE `ex` (`id` int(11) auto_increment PRIMARY KEY, `data` varchar(255)) eosql result = @connection.exec_query('SELECT id, data FROM ex') @@ -93,7 +93,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_exec_with_binds @connection.exec_query('drop table if exists ex') @connection.exec_query(<<-eosql) - CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, + CREATE TABLE `ex` (`id` int(11) auto_increment PRIMARY KEY, `data` varchar(255)) eosql @connection.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') @@ -109,7 +109,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_exec_typecasts_bind_vals @connection.exec_query('drop table if exists ex') @connection.exec_query(<<-eosql) - CREATE TABLE `ex` (`id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, + CREATE TABLE `ex` (`id` int(11) auto_increment PRIMARY KEY, `data` varchar(255)) eosql @connection.exec_query('INSERT INTO ex (id, data) VALUES (1, "foo")') diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index 9ad0744aee..578f6301bd 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -10,12 +10,20 @@ module ActiveRecord @conn.exec_query('drop table if exists ex') @conn.exec_query(<<-eosql) CREATE TABLE `ex` ( - `id` int(11) DEFAULT NULL auto_increment PRIMARY KEY, + `id` int(11) auto_increment PRIMARY KEY, `number` integer, `data` varchar(255)) eosql end + def test_bad_connection_mysql + assert_raise ActiveRecord::NoDatabaseError do + configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + connection = ActiveRecord::Base.mysql_connection(configuration) + connection.exec_query('drop table if exists ex') + end + end + def test_valid_column column = @conn.columns('ex').find { |col| col.name == 'id' } assert @conn.valid_type?(column.type) @@ -75,7 +83,7 @@ module ActiveRecord @conn.exec_query('drop table if exists ex_with_non_standard_pk') @conn.exec_query(<<-eosql) CREATE TABLE `ex_with_non_standard_pk` ( - `code` INT(11) DEFAULT NULL auto_increment, + `code` INT(11) auto_increment, PRIMARY KEY (`code`)) eosql pk, seq = @conn.pk_and_sequence_for('ex_with_non_standard_pk') @@ -87,7 +95,7 @@ module ActiveRecord @conn.exec_query('drop table if exists ex_with_custom_index_type_pk') @conn.exec_query(<<-eosql) CREATE TABLE `ex_with_custom_index_type_pk` ( - `id` INT(11) DEFAULT NULL auto_increment, + `id` INT(11) auto_increment, PRIMARY KEY USING BTREE (`id`)) eosql pk, seq = @conn.pk_and_sequence_for('ex_with_custom_index_type_pk') diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 8dc1df1851..9b7202c915 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -13,6 +13,14 @@ class MysqlConnectionTest < ActiveRecord::TestCase super end + def test_bad_connection + assert_raise ActiveRecord::NoDatabaseError do + configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'inexistent_activerecord_unittest') + connection = ActiveRecord::Base.mysql2_connection(configuration) + connection.exec_query('drop table if exists ex') + end + end + def test_no_automatic_reconnection_after_timeout assert @connection.active? @connection.update('set @@wait_timeout=1') diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 68ed361aeb..1cd356e868 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -10,15 +10,15 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain - assert_match %(developers | const), explain + assert_match %r(developers |.* const), explain end def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain - assert_match %(developers | const), explain + assert_match %r(developers |.* const), explain assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain - assert_match %(audit_logs | ALL), explain + assert_match %r(audit_logs |.* ALL), explain end end end diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 5db60ff8a0..43c9116b5a 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -65,6 +65,15 @@ module ActiveRecord assert_nil index_c.using assert_equal :fulltext, index_c.type end + + def test_drop_temporary_table + @connection.transaction do + @connection.create_table(:temp_table, temporary: true) + # if it doesn't properly say DROP TEMPORARY TABLE, the transaction commit + # will complain that no transaction is active + @connection.drop_table(:temp_table, temporary: true) + end + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb index 9536cceb1d..d71e2aa2bb 100644 --- a/activerecord/test/cases/adapters/postgresql/array_test.rb +++ b/activerecord/test/cases/adapters/postgresql/array_test.rb @@ -10,12 +10,12 @@ class PostgresqlArrayTest < ActiveRecord::TestCase def setup @connection = ActiveRecord::Base.connection - @connection.transaction do - @connection.create_table('pg_arrays') do |t| - t.string 'tags', array: true - t.integer 'ratings', array: true - end + @connection.transaction do + @connection.create_table('pg_arrays') do |t| + t.string 'tags', array: true + t.integer 'ratings', array: true end + end @column = PgArray.columns.find { |c| c.name == 'tags' } end @@ -26,6 +26,12 @@ class PostgresqlArrayTest < ActiveRecord::TestCase def test_column assert_equal :string, @column.type assert @column.array + assert_not @column.text? + + ratings_column = PgArray.columns_hash['ratings'] + assert_equal :integer, ratings_column.type + assert ratings_column.array + assert_not ratings_column.number? end def test_change_column_with_array @@ -50,8 +56,6 @@ class PostgresqlArrayTest < ActiveRecord::TestCase end def test_type_cast_array - assert @column - data = '{1,2,3}' oid_type = @column.instance_variable_get('@oid_type').subtype # we are getting the instance variable in this test, but in the @@ -66,6 +70,12 @@ class PostgresqlArrayTest < ActiveRecord::TestCase assert_equal([nil], @column.type_cast('{NULL}')) end + def test_type_cast_integers + x = PgArray.new(ratings: ['1', '2']) + assert x.save! + assert_equal(['1', '2'], x.ratings) + end + def test_rewrite @connection.execute "insert into pg_arrays (tags) VALUES ('{1,2,3}')" x = PgArray.first @@ -118,6 +128,16 @@ class PostgresqlArrayTest < ActiveRecord::TestCase assert_equal("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]", record.attribute_for_inspect(:ratings)) end + def test_update_all + pg_array = PgArray.create! tags: ["one", "two", "three"] + + PgArray.update_all tags: ["four", "five"] + assert_equal ["four", "five"], pg_array.reload.tags + + PgArray.update_all tags: [] + assert_equal [], pg_array.reload.tags + end + private def assert_cycle field, array # test creation diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 01de202d11..04a458fbce 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -3,9 +3,6 @@ require "cases/helper" class PostgresqlArray < ActiveRecord::Base end -class PostgresqlRange < ActiveRecord::Base -end - class PostgresqlTsvector < ActiveRecord::Base end @@ -46,104 +43,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase @connection.execute("INSERT INTO postgresql_arrays (id, commission_by_quarter, nicknames) VALUES (1, '{35000,21000,18000,17000}', '{foo,bar,baz}')") @first_array = PostgresqlArray.find(1) - @connection.execute <<_SQL if @connection.supports_ranges? - INSERT INTO postgresql_ranges ( - date_range, - num_range, - ts_range, - tstz_range, - int4_range, - int8_range - ) VALUES ( - '[''2012-01-02'', ''2012-01-04'']', - '[0.1, 0.2]', - '[''2010-01-01 14:30'', ''2011-01-01 14:30'']', - '[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']', - '[1, 10]', - '[10, 100]' - ) -_SQL - - @connection.execute <<_SQL if @connection.supports_ranges? - INSERT INTO postgresql_ranges ( - date_range, - num_range, - ts_range, - tstz_range, - int4_range, - int8_range - ) VALUES ( - '(''2012-01-02'', ''2012-01-04'')', - '[0.1, 0.2)', - '[''2010-01-01 14:30'', ''2011-01-01 14:30'')', - '[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')', - '(1, 10)', - '(10, 100)' - ) -_SQL - - @connection.execute <<_SQL if @connection.supports_ranges? - INSERT INTO postgresql_ranges ( - date_range, - num_range, - ts_range, - tstz_range, - int4_range, - int8_range - ) VALUES ( - '(''2012-01-02'',]', - '[0.1,]', - '[''2010-01-01 14:30'',]', - '[''2010-01-01 14:30:00+05'',]', - '(1,]', - '(10,]' - ) -_SQL - - @connection.execute <<_SQL if @connection.supports_ranges? - INSERT INTO postgresql_ranges ( - date_range, - num_range, - ts_range, - tstz_range, - int4_range, - int8_range - ) VALUES ( - '[,]', - '[,]', - '[,]', - '[,]', - '[,]', - '[,]' - ) -_SQL - - @connection.execute <<_SQL if @connection.supports_ranges? - INSERT INTO postgresql_ranges ( - date_range, - num_range, - ts_range, - tstz_range, - int4_range, - int8_range - ) VALUES ( - '(''2012-01-02'', ''2012-01-02'')', - '(0.1, 0.1)', - '(''2010-01-01 14:30'', ''2010-01-01 14:30'')', - '(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')', - '(1, 1)', - '(10, 10)' - ) -_SQL - - if @connection.supports_ranges? - @first_range = PostgresqlRange.find(1) - @second_range = PostgresqlRange.find(2) - @third_range = PostgresqlRange.find(3) - @fourth_range = PostgresqlRange.find(4) - @empty_range = PostgresqlRange.find(5) - end - @connection.execute("INSERT INTO postgresql_tsvectors (id, text_vector) VALUES (1, ' ''text'' ''vector'' ')") @first_tsvector = PostgresqlTsvector.find(1) @@ -230,187 +129,6 @@ _SQL assert_equal "'text' 'vector'", @first_tsvector.text_vector end - if ActiveRecord::Base.connection.supports_ranges? - def test_data_type_of_range_types - assert_equal :daterange, @first_range.column_for_attribute(:date_range).type - assert_equal :numrange, @first_range.column_for_attribute(:num_range).type - assert_equal :tsrange, @first_range.column_for_attribute(:ts_range).type - assert_equal :tstzrange, @first_range.column_for_attribute(:tstz_range).type - assert_equal :int4range, @first_range.column_for_attribute(:int4_range).type - assert_equal :int8range, @first_range.column_for_attribute(:int8_range).type - end - - def test_int4range_values - assert_equal 1...11, @first_range.int4_range - assert_equal 2...10, @second_range.int4_range - assert_equal 2...Float::INFINITY, @third_range.int4_range - assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range) - assert_nil @empty_range.int4_range - end - - def test_int8range_values - assert_equal 10...101, @first_range.int8_range - assert_equal 11...100, @second_range.int8_range - assert_equal 11...Float::INFINITY, @third_range.int8_range - assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range) - assert_nil @empty_range.int8_range - end - - def test_daterange_values - assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range - assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range - assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range - assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range) - assert_nil @empty_range.date_range - end - - def test_numrange_values - assert_equal BigDecimal.new('0.1')..BigDecimal.new('0.2'), @first_range.num_range - assert_equal BigDecimal.new('0.1')...BigDecimal.new('0.2'), @second_range.num_range - assert_equal BigDecimal.new('0.1')...BigDecimal.new('Infinity'), @third_range.num_range - assert_equal BigDecimal.new('-Infinity')...BigDecimal.new('Infinity'), @fourth_range.num_range - assert_nil @empty_range.num_range - end - - def test_tsrange_values - tz = ::ActiveRecord::Base.default_timezone - assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)..Time.send(tz, 2011, 1, 1, 14, 30, 0), @first_range.ts_range - assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 1, 1, 14, 30, 0), @second_range.ts_range - assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.ts_range) - assert_nil @empty_range.ts_range - end - - def test_tstzrange_values - assert_equal Time.parse('2010-01-01 09:30:00 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), @first_range.tstz_range - assert_equal Time.parse('2010-01-01 09:30:00 UTC')...Time.parse('2011-01-01 17:30:00 UTC'), @second_range.tstz_range - assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.tstz_range) - assert_nil @empty_range.tstz_range - end - - def test_create_tstzrange - tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT') - range = PostgresqlRange.new(:tstz_range => tstzrange) - assert range.save - assert range.reload - assert_equal range.tstz_range, tstzrange - assert_equal range.tstz_range, Time.parse('2010-01-01 13:30:00 UTC')...Time.parse('2011-02-02 19:30:00 UTC') - end - - def test_update_tstzrange - new_tstzrange = Time.parse('2010-01-01 14:30:00 CDT')...Time.parse('2011-02-02 14:30:00 CET') - @first_range.tstz_range = new_tstzrange - assert @first_range.save - assert @first_range.reload - assert_equal new_tstzrange, @first_range.tstz_range - @first_range.tstz_range = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000') - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.tstz_range - end - - def test_create_tsrange - tz = ::ActiveRecord::Base.default_timezone - tsrange = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0) - range = PostgresqlRange.new(:ts_range => tsrange) - assert range.save - assert range.reload - assert_equal range.ts_range, tsrange - end - - def test_update_tsrange - tz = ::ActiveRecord::Base.default_timezone - new_tsrange = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0) - @first_range.ts_range = new_tsrange - assert @first_range.save - assert @first_range.reload - assert_equal new_tsrange, @first_range.ts_range - @first_range.ts_range = Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2010, 1, 1, 14, 30, 0) - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.ts_range - end - - def test_create_numrange - numrange = BigDecimal.new('0.5')...BigDecimal.new('1') - range = PostgresqlRange.new(:num_range => numrange) - assert range.save - assert range.reload - assert_equal range.num_range, numrange - end - - def test_update_numrange - new_numrange = BigDecimal.new('0.5')...BigDecimal.new('1') - @first_range.num_range = new_numrange - assert @first_range.save - assert @first_range.reload - assert_equal new_numrange, @first_range.num_range - @first_range.num_range = BigDecimal.new('0.5')...BigDecimal.new('0.5') - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.num_range - end - - def test_create_daterange - daterange = Range.new(Date.new(2012, 1, 1), Date.new(2013, 1, 1), true) - range = PostgresqlRange.new(:date_range => daterange) - assert range.save - assert range.reload - assert_equal range.date_range, daterange - end - - def test_update_daterange - new_daterange = Date.new(2012, 2, 3)...Date.new(2012, 2, 10) - @first_range.date_range = new_daterange - assert @first_range.save - assert @first_range.reload - assert_equal new_daterange, @first_range.date_range - @first_range.date_range = Date.new(2012, 2, 3)...Date.new(2012, 2, 3) - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.date_range - end - - def test_create_int4range - int4range = Range.new(3, 50, true) - range = PostgresqlRange.new(:int4_range => int4range) - assert range.save - assert range.reload - assert_equal range.int4_range, int4range - end - - def test_update_int4range - new_int4range = 6...10 - @first_range.int4_range = new_int4range - assert @first_range.save - assert @first_range.reload - assert_equal new_int4range, @first_range.int4_range - @first_range.int4_range = 3...3 - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.int4_range - end - - def test_create_int8range - int8range = Range.new(30, 50, true) - range = PostgresqlRange.new(:int8_range => int8range) - assert range.save - assert range.reload - assert_equal range.int8_range, int8range - end - - def test_update_int8range - new_int8range = 60000...10000000 - @first_range.int8_range = new_int8range - assert @first_range.save - assert @first_range.reload - assert_equal new_int8range, @first_range.int8_range - @first_range.int8_range = 39999...39999 - assert @first_range.save - assert @first_range.reload - assert_nil @first_range.int8_range - end - end - def test_money_values assert_equal 567.89, @first_money.wealth assert_equal(-567.89, @second_money.wealth) diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 2845413575..d8782f5eaa 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -70,6 +70,23 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase Hstore.reset_column_information end + def test_hstore_migration + hstore_migration = Class.new(ActiveRecord::Migration) do + def change + change_table("hstores") do |t| + t.hstore :keys + end + end + end + + hstore_migration.new.suppress_messages do + hstore_migration.migrate(:up) + assert_includes @connection.columns(:hstores).map(&:name), "keys" + hstore_migration.migrate(:down) + assert_not_includes @connection.columns(:hstores).map(&:name), "keys" + end + end + def test_cast_value_on_write x = Hstore.new tags: {"bool" => true, "number" => 5} assert_equal({"bool" => "true", "number" => "5"}, x.tags) @@ -206,6 +223,16 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase def test_multiline assert_cycle("a\nb" => "c\nd") end + + def test_update_all + hstore = Hstore.create! tags: { "one" => "two" } + + Hstore.update_all tags: { "three" => "four" } + assert_equal({ "three" => "four" }, hstore.reload.tags) + + Hstore.update_all tags: { } + assert_equal({ }, hstore.reload.tags) + end end private diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index c33c7ef968..01e7334aad 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -121,4 +121,14 @@ class PostgresqlJSONTest < ActiveRecord::TestCase x = JsonDataType.first assert_equal "640×1136", x.resolution end + + def test_update_all + json = JsonDataType.create! payload: { "one" => "two" } + + JsonDataType.update_all payload: { "three" => "four" } + assert_equal({ "three" => "four" }, json.reload.payload) + + JsonDataType.update_all payload: { } + assert_equal({ }, json.reload.payload) + end end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 8b017760b1..131080913c 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -10,6 +10,14 @@ module ActiveRecord @connection.exec_query('create table ex(id serial primary key, number integer, data character varying(255))') end + def test_bad_connection + assert_raise ActiveRecord::NoDatabaseError do + configuration = ActiveRecord::Base.configurations['arunit'].merge(database: 'should_not_exist-cinco-dog-db') + connection = ActiveRecord::Base.postgresql_connection(configuration) + connection.exec_query('drop table if exists ex') + end + end + def test_valid_column column = @connection.columns('ex').find { |col| col.name == 'id' } assert @connection.valid_type?(column.type) @@ -62,6 +70,18 @@ module ActiveRecord assert_equal expect, id end + def test_multiline_insert_sql + id = @connection.insert_sql(<<-SQL) + insert into ex( + number) + values( + 5152 + ) + SQL + expect = @connection.query('select max(id) from ex').first.first + assert_equal expect, id + end + def test_insert_sql_with_returning_disabled connection = connection_without_insert_returning id = connection.insert_sql("insert into postgresql_partitioned_table_parent (number) VALUES (1)") diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb new file mode 100644 index 0000000000..a56b8ac791 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -0,0 +1,245 @@ +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +if ActiveRecord::Base.connection.supports_ranges? + class PostgresqlRange < ActiveRecord::Base + self.table_name = "postgresql_ranges" + end + + class PostgresqlRangeTest < ActiveRecord::TestCase + def teardown + @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + end + + def setup + @connection = ActiveRecord::Base.connection + begin + @connection.transaction do + @connection.create_table('postgresql_ranges') do |t| + t.daterange :date_range + t.numrange :num_range + t.tsrange :ts_range + t.tstzrange :tstz_range + t.int4range :int4_range + t.int8range :int8_range + end + end + rescue ActiveRecord::StatementInvalid + return skip "do not test on PG without range" + end + + insert_range(id: 101, + date_range: "[''2012-01-02'', ''2012-01-04'']", + num_range: "[0.1, 0.2]", + ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'']", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']", + int4_range: "[1, 10]", + int8_range: "[10, 100]") + + insert_range(id: 102, + date_range: "(''2012-01-02'', ''2012-01-04'')", + num_range: "[0.1, 0.2)", + ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", + int4_range: "(1, 10)", + int8_range: "(10, 100)") + + insert_range(id: 103, + date_range: "(''2012-01-02'',]", + num_range: "[0.1,]", + ts_range: "[''2010-01-01 14:30'',]", + tstz_range: "[''2010-01-01 14:30:00+05'',]", + int4_range: "(1,]", + int8_range: "(10,]") + + insert_range(id: 104, + date_range: "[,]", + num_range: "[,]", + ts_range: "[,]", + tstz_range: "[,]", + int4_range: "[,]", + int8_range: "[,]") + + insert_range(id: 105, + date_range: "(''2012-01-02'', ''2012-01-02'')", + num_range: "(0.1, 0.1)", + ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')", + tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", + int4_range: "(1, 1)", + int8_range: "(10, 10)") + + @new_range = PostgresqlRange.new + @first_range = PostgresqlRange.find(101) + @second_range = PostgresqlRange.find(102) + @third_range = PostgresqlRange.find(103) + @fourth_range = PostgresqlRange.find(104) + @empty_range = PostgresqlRange.find(105) + end + + def test_data_type_of_range_types + assert_equal :daterange, @first_range.column_for_attribute(:date_range).type + assert_equal :numrange, @first_range.column_for_attribute(:num_range).type + assert_equal :tsrange, @first_range.column_for_attribute(:ts_range).type + assert_equal :tstzrange, @first_range.column_for_attribute(:tstz_range).type + assert_equal :int4range, @first_range.column_for_attribute(:int4_range).type + assert_equal :int8range, @first_range.column_for_attribute(:int8_range).type + end + + def test_int4range_values + assert_equal 1...11, @first_range.int4_range + assert_equal 2...10, @second_range.int4_range + assert_equal 2...Float::INFINITY, @third_range.int4_range + assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range) + assert_nil @empty_range.int4_range + end + + def test_int8range_values + assert_equal 10...101, @first_range.int8_range + assert_equal 11...100, @second_range.int8_range + assert_equal 11...Float::INFINITY, @third_range.int8_range + assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range) + assert_nil @empty_range.int8_range + end + + def test_daterange_values + assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range + assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range + assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range + assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range) + assert_nil @empty_range.date_range + end + + def test_numrange_values + assert_equal BigDecimal.new('0.1')..BigDecimal.new('0.2'), @first_range.num_range + assert_equal BigDecimal.new('0.1')...BigDecimal.new('0.2'), @second_range.num_range + assert_equal BigDecimal.new('0.1')...BigDecimal.new('Infinity'), @third_range.num_range + assert_equal BigDecimal.new('-Infinity')...BigDecimal.new('Infinity'), @fourth_range.num_range + assert_nil @empty_range.num_range + end + + def test_tsrange_values + tz = ::ActiveRecord::Base.default_timezone + assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)..Time.send(tz, 2011, 1, 1, 14, 30, 0), @first_range.ts_range + assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 1, 1, 14, 30, 0), @second_range.ts_range + assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.ts_range) + assert_nil @empty_range.ts_range + end + + def test_tstzrange_values + assert_equal Time.parse('2010-01-01 09:30:00 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), @first_range.tstz_range + assert_equal Time.parse('2010-01-01 09:30:00 UTC')...Time.parse('2011-01-01 17:30:00 UTC'), @second_range.tstz_range + assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.tstz_range) + assert_nil @empty_range.tstz_range + end + + def test_create_tstzrange + tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT') + round_trip(@new_range, :tstz_range, tstzrange) + assert_equal @new_range.tstz_range, tstzrange + assert_equal @new_range.tstz_range, Time.parse('2010-01-01 13:30:00 UTC')...Time.parse('2011-02-02 19:30:00 UTC') + end + + def test_update_tstzrange + assert_equal_round_trip(@first_range, :tstz_range, + Time.parse('2010-01-01 14:30:00 CDT')...Time.parse('2011-02-02 14:30:00 CET')) + assert_nil_round_trip(@first_range, :tstz_range, + Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2010-01-01 13:30:00 +0000')) + end + + def test_create_tsrange + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@new_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0)) + end + + def test_update_tsrange + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2011, 2, 2, 14, 30, 0)) + assert_nil_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0)...Time.send(tz, 2010, 1, 1, 14, 30, 0)) + end + + def test_create_numrange + assert_equal_round_trip(@new_range, :num_range, + BigDecimal.new('0.5')...BigDecimal.new('1')) + end + + def test_update_numrange + assert_equal_round_trip(@first_range, :num_range, + BigDecimal.new('0.5')...BigDecimal.new('1')) + assert_nil_round_trip(@first_range, :num_range, + BigDecimal.new('0.5')...BigDecimal.new('0.5')) + end + + def test_create_daterange + assert_equal_round_trip(@new_range, :date_range, + Range.new(Date.new(2012, 1, 1), Date.new(2013, 1, 1), true)) + end + + def test_update_daterange + assert_equal_round_trip(@first_range, :date_range, + Date.new(2012, 2, 3)...Date.new(2012, 2, 10)) + assert_nil_round_trip(@first_range, :date_range, + Date.new(2012, 2, 3)...Date.new(2012, 2, 3)) + end + + def test_create_int4range + assert_equal_round_trip(@new_range, :int4_range, Range.new(3, 50, true)) + end + + def test_update_int4range + assert_equal_round_trip(@first_range, :int4_range, 6...10) + assert_nil_round_trip(@first_range, :int4_range, 3...3) + end + + def test_create_int8range + assert_equal_round_trip(@new_range, :int8_range, Range.new(30, 50, true)) + end + + def test_update_int8range + assert_equal_round_trip(@first_range, :int8_range, 60000...10000000) + assert_nil_round_trip(@first_range, :int8_range, 39999...39999) + end + + private + def assert_equal_round_trip(range, attribute, value) + round_trip(range, attribute, value) + assert_equal value, range.public_send(attribute) + end + + def assert_nil_round_trip(range, attribute, value) + round_trip(range, attribute, value) + assert_nil range.public_send(attribute) + end + + def round_trip(range, attribute, value) + range.public_send "#{attribute}=", value + assert range.save + assert range.reload + end + + def insert_range(values) + @connection.execute <<-SQL + INSERT INTO postgresql_ranges ( + id, + date_range, + num_range, + ts_range, + tstz_range, + int4_range, + int8_range + ) VALUES ( + #{values[:id]}, + '#{values[:date_range]}', + '#{values[:num_range]}', + '#{values[:ts_range]}', + '#{values[:tstz_range]}', + '#{values[:int4_range]}', + '#{values[:int8_range]}' + ) + SQL + end + end +end diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb index a7b2764fc1..ba89487838 100644 --- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb @@ -95,6 +95,13 @@ module ActiveRecord end }.new assert_equal 10, @conn.type_cast(quoted_id_obj, nil) + + quoted_id_obj = Class.new { + def quoted_id + "'zomg'" + end + } + assert_raise(TypeError) { @conn.type_cast(quoted_id_obj, nil) } end end end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index ce7c869eec..0598ff25f8 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require "cases/helper" require 'models/owner' +require 'tempfile' module ActiveRecord module ConnectionAdapters @@ -25,6 +26,34 @@ module ActiveRecord ActiveSupport::Notifications.subscribe('sql.active_record', @subscriber) end + def test_bad_connection + assert_raise ActiveRecord::NoDatabaseError do + connection = ActiveRecord::Base.sqlite3_connection(adapter: "sqlite3", database: "/tmp/should/_not/_exist/-cinco-dog.db") + connection.exec_query('drop table if exists ex') + end + end + + def test_connect_with_url + original_connection = ActiveRecord::Base.remove_connection + tf = Tempfile.open 'whatever' + url = "sqlite3://#{tf.path}" + ActiveRecord::Base.establish_connection(url) + assert ActiveRecord::Base.connection + ensure + tf.close + tf.unlink + ActiveRecord::Base.establish_connection(original_connection) + end + + def test_connect_memory_with_url + original_connection = ActiveRecord::Base.remove_connection + url = "sqlite3:///:memory:" + ActiveRecord::Base.establish_connection(url) + assert ActiveRecord::Base.connection + ensure + ActiveRecord::Base.establish_connection(original_connection) + end + def test_valid_column column = @conn.columns('items').find { |col| col.name == 'id' } assert @conn.valid_type?(column.type) diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 7c913bc78b..3205d0c28b 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -356,6 +356,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_queries(2) { line_item.destroy } end + def test_belongs_to_with_touch_option_on_destroy_with_destroyed_parent + line_item = LineItem.create! + invoice = Invoice.create!(line_items: [line_item]) + invoice.destroy + + assert_queries(1) { line_item.destroy } + end + def test_belongs_to_with_touch_option_on_touch_and_reassigned_parent line_item = LineItem.create! Invoice.create!(line_items: [line_item]) @@ -578,6 +586,19 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_nil essay.writer_id end + def test_polymorphic_assignment_with_nil + essay = Essay.new + assert_nil essay.writer_id + assert_nil essay.writer_type + + essay.writer_id = 1 + essay.writer_type = 'Author' + + essay.writer = nil + assert_nil essay.writer_id + assert_nil essay.writer_type + end + def test_belongs_to_proxy_should_not_respond_to_private_methods assert_raise(NoMethodError) { companies(:first_firm).private_method } assert_raise(NoMethodError) { companies(:second_client).firm.private_method } diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index 811d91f849..71c0609df5 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -174,4 +174,15 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase sink = Vertex.all.merge!(:includes=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC').first assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first } end + + def test_eager_association_loading_with_cascaded_interdependent_one_level_and_two_levels + authors_relation = Author.all.merge!(includes: [:comments, { posts: :categorizations }], order: "authors.id") + authors = authors_relation.to_a + assert_equal 3, authors.size + assert_equal 10, authors[0].comments.size + assert_equal 1, authors[1].comments.size + assert_equal 5, authors[0].posts.size + assert_equal 3, authors[1].posts.size + assert_equal 3, authors[0].posts.collect { |post| post.categorizations.size }.inject(0) { |sum, i| sum+i } + end end diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index f8f2832ab1..4c1fdfdd9a 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -75,6 +75,7 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase private def extend!(model) - ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { } + builder = ActiveRecord::Associations::Builder::HasMany.new(model, :association_name, nil, {}) { } + builder.define_extensions(model) end end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index be928ec8ee..8aee7ff40e 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -570,6 +570,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert !developer.special_projects.include?(other_project) end + def test_symbol_join_table + developer = Developer.first + sp = developer.sym_special_projects.create("name" => "omg") + developer.reload + assert_includes developer.sym_special_projects, sp + end + def test_update_attributes_after_push_without_duplicate_join_table_rows developer = Developer.new("name" => "Kano") project = SpecialProject.create("name" => "Special Project") diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index bfb80afa61..e45efb0161 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -318,9 +318,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_belongs_to_sanity c = Client.new - assert_nil c.firm - - flunk "belongs_to failed if check" if c.firm + assert_nil c.firm, "belongs_to failed sanity check on new object" end def test_find_ids @@ -457,7 +455,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_select_query_method - assert_equal ['id'], posts(:welcome).comments.select(:id).first.attributes.keys + assert_equal ['id', 'body'], posts(:welcome).comments.select(:id, :body).first.attributes.keys + end + + def test_select_with_block + assert_equal [1], posts(:welcome).comments.select { |c| c.id == 1 }.map(&:id) end def test_select_without_foreign_key @@ -1781,12 +1783,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal [original_child], car.reload.failed_bulbs end - + test 'updates counter cache when default scope is given' do topic = DefaultRejectedTopic.create approved: true assert_difference "topic.reload.replies_count", 1 do topic.approved_replies.create! end - end + end end diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index dffee42e7d..a9efa6d86a 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -42,7 +42,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase end def test_join_association_conditions_support_string_and_arel_expressions - assert_equal 0, Author.joins(:welcome_posts_with_comment).count + assert_equal 0, Author.joins(:welcome_posts_with_one_comment).count assert_equal 1, Author.joins(:welcome_posts_with_comments).count end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 9c66ed354e..6c581a432f 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -767,8 +767,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase # that by defining a 'foo' method in the generated methods module for B. # (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].) def test_inherited_custom_accessors - klass = Class.new(ActiveRecord::Base) do - self.table_name = "topics" + klass = new_topic_like_ar_class do self.abstract_class = true def title; "omg"; end def title=(val); self.author_name = val; end @@ -783,8 +782,40 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal "lol", topic.author_name end + def test_on_the_fly_super_invokable_generated_attribute_methods_via_method_missing + klass = new_topic_like_ar_class do + def title + super + '!' + end + end + + real_topic = topics(:first) + assert_equal real_topic.title + '!', klass.find(real_topic.id).title + end + + def test_on_the_fly_super_invokable_generated_predicate_attribute_methods_via_method_missing + klass = new_topic_like_ar_class do + def title? + !super + end + end + + real_topic = topics(:first) + assert_equal !real_topic.title?, klass.find(real_topic.id).title? + end + private + def new_topic_like_ar_class(&block) + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'topics' + class_eval(&block) + end + + assert_empty klass.generated_attribute_methods.instance_methods(false) + klass + end + def cached_columns Topic.columns.map(&:name) - Topic.serialized_attributes.keys end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index cde188f6c3..cb8e564da1 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -121,6 +121,10 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 1, Topic.limit(1).to_a.length end + def test_limit_should_take_value_from_latest_limit + assert_equal 1, Topic.limit(2).limit(1).to_a.length + end + def test_invalid_limit assert_raises(ArgumentError) do Topic.limit("asdfadf").to_a diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 2c41656b3d..2f6913167d 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -211,6 +211,10 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 19.83, NumericData.sum(:bank_balance) end + def test_should_return_type_casted_values_with_group_and_expression + assert_equal 0.5, Account.group(:firm_name).sum('0.01 * credit_limit')['37signals'] + end + def test_should_group_by_summed_field_with_conditions c = Account.where('firm_id > 1').group(:firm_id).sum(:credit_limit) assert_nil c[1] diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 3e33b30144..318cc5a32c 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -2,6 +2,133 @@ require "cases/helper" module ActiveRecord module ConnectionAdapters + + class MergeAndResolveDefaultUrlConfigTest < ActiveRecord::TestCase + + def klass + ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig + end + + def setup + @previous_database_url = ENV.delete("DATABASE_URL") + end + + def teardown + ENV["DATABASE_URL"] = @previous_database_url + end + + def test_string_connection + config = { "production" => "postgres://localhost/foo" } + actual = klass.new(config).resolve + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost" + } + } + assert_equal expected, actual + end + + def test_url_sub_key + config = { "production" => { "url" => "postgres://localhost/foo" } } + actual = klass.new(config).resolve + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost" + } + } + assert_equal expected, actual + end + + def test_hash + config = { "production" => { "adapter" => "postgres", "database" => "foo" } } + actual = klass.new(config).resolve + assert_equal config, actual + end + + def test_blank + config = {} + actual = klass.new(config).resolve + assert_equal config, actual + end + + def test_blank_with_database_url + ENV['DATABASE_URL'] = "postgres://localhost/foo" + + config = {} + actual = klass.new(config).resolve + expected = { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost" } + assert_equal expected, actual["production"] + assert_equal expected, actual["development"] + assert_equal expected, actual["test"] + assert_equal nil, actual[:production] + assert_equal nil, actual[:development] + assert_equal nil, actual[:test] + end + + def test_sting_with_database_url + ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" + + config = { "production" => "postgres://localhost/foo" } + actual = klass.new(config).resolve + + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost" + } + } + assert_equal expected, actual + end + + def test_url_sub_key_with_database_url + ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" + + config = { "production" => { "url" => "postgres://localhost/foo" } } + actual = klass.new(config).resolve + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost" + } + } + assert_equal expected, actual + end + + def test_merge_no_conflicts_with_database_url + ENV['DATABASE_URL'] = "postgres://localhost/foo" + + config = {"production" => { "pool" => "5" } } + actual = klass.new(config).resolve + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost", + "pool" => "5" + } + } + assert_equal expected, actual + end + + def test_merge_conflicts_with_database_url + ENV['DATABASE_URL'] = "postgres://localhost/foo" + + config = {"production" => { "adapter" => "NOT-POSTGRES", "database" => "NOT-FOO", "pool" => "5" } } + actual = klass.new(config).resolve + expected = { "production" => + { "adapter" => "postgresql", + "database" => "foo", + "host" => "localhost", + "pool" => "5" + } + } + assert_equal expected, actual + end + end + class ConnectionHandlerTest < ActiveRecord::TestCase def setup @klass = Class.new(Base) { def self.name; 'klass'; end } diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb index c8dfc3244b..fdd1914cba 100644 --- a/activerecord/test/cases/connection_specification/resolver_test.rb +++ b/activerecord/test/cases/connection_specification/resolver_test.rb @@ -4,58 +4,92 @@ module ActiveRecord module ConnectionAdapters class ConnectionSpecification class ResolverTest < ActiveRecord::TestCase - def resolve(spec) - Resolver.new(spec, {}).spec.config + def resolve(spec, config={}) + Resolver.new(config).resolve(spec) + end + + def spec(spec, config={}) + Resolver.new(config).spec(spec) end def test_url_invalid_adapter - assert_raises(LoadError) do - resolve 'ridiculous://foo?encoding=utf8' + error = assert_raises(LoadError) do + spec 'ridiculous://foo?encoding=utf8' end + + assert_match "Could not load 'active_record/connection_adapters/ridiculous_adapter'", error.message end # The abstract adapter is used simply to bypass the bit of code that # checks that the adapter file can be required in. + def test_url_from_environment + spec = resolve :production, 'production' => 'abstract://foo?encoding=utf8' + assert_equal({ + "adapter" => "abstract", + "host" => "foo", + "encoding" => "utf8" }, spec) + end + + def test_url_sub_key + spec = resolve :production, 'production' => {"url" => 'abstract://foo?encoding=utf8'} + assert_equal({ + "adapter" => "abstract", + "host" => "foo", + "encoding" => "utf8" }, spec) + end + + def test_url_sub_key_merges_correctly + hash = {"url" => 'abstract://foo?encoding=utf8&', "adapter" => "sqlite3", "host" => "bar", "pool" => "3"} + spec = resolve :production, 'production' => hash + assert_equal({ + "adapter" => "abstract", + "host" => "foo", + "encoding" => "utf8", + "pool" => "3" }, spec) + end + def test_url_host_no_db spec = resolve 'abstract://foo?encoding=utf8' assert_equal({ - adapter: "abstract", - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "abstract", + "host" => "foo", + "encoding" => "utf8" }, spec) end def test_url_host_db spec = resolve 'abstract://foo/bar?encoding=utf8' assert_equal({ - adapter: "abstract", - database: "bar", - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "abstract", + "database" => "bar", + "host" => "foo", + "encoding" => "utf8" }, spec) end def test_url_port spec = resolve 'abstract://foo:123?encoding=utf8' assert_equal({ - adapter: "abstract", - port: 123, - host: "foo", - encoding: "utf8" }, spec) + "adapter" => "abstract", + "port" => 123, + "host" => "foo", + "encoding" => "utf8" }, spec) end def test_encoded_password password = 'am@z1ng_p@ssw0rd#!' encoded_password = URI.encode_www_form_component(password) spec = resolve "abstract://foo:#{encoded_password}@localhost/bar" - assert_equal password, spec[:password] + assert_equal password, spec["password"] end - def test_descriptive_error_message_when_adapter_is_missing - error = assert_raise(LoadError) do - resolve(adapter: 'non-existing') - end + def test_url_host_db_for_sqlite3 + spec = resolve 'sqlite3://foo:bar@dburl:9000/foo_test' + assert_equal('/foo_test', spec["database"]) + end - assert_match "Could not load 'active_record/connection_adapters/non-existing_adapter'", error.message + def test_url_host_memory_db_for_sqlite3 + spec = resolve 'sqlite3://foo:bar@dburl:9000/:memory:' + assert_equal(':memory:', spec["database"]) end end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 9d7f57bf85..df4183c065 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -584,6 +584,14 @@ class DirtyTest < ActiveRecord::TestCase end end + def test_datetime_attribute_doesnt_change_if_zone_is_modified_in_string + time_in_paris = Time.utc(2014, 1, 1, 12, 0, 0).in_time_zone('Paris') + pirate = Pirate.create!(:catchphrase => 'rrrr', :created_on => time_in_paris) + + pirate.created_on = pirate.created_on.in_time_zone('Tokyo').to_s + assert !pirate.created_on_changed? + end + test "partial insert" do with_partial_writes Person do jon = nil diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb index 35e8a98156..c09e58fbf1 100644 --- a/activerecord/test/cases/enum_test.rb +++ b/activerecord/test/cases/enum_test.rb @@ -16,7 +16,7 @@ class EnumTest < ActiveRecord::TestCase assert @book.unread? end - test "query state with symbol" do + test "query state with strings" do assert_equal "proposed", @book.status assert_equal "unread", @book.read_status end @@ -58,9 +58,34 @@ class EnumTest < ActiveRecord::TestCase assert_equal "'unknown' is not a valid status", e.message end + test "assign nil value" do + @book.status = nil + assert @book.status.nil? + end + + test "assign empty string value" do + @book.status = '' + assert @book.status.nil? + end + + test "assign long empty string value" do + @book.status = ' ' + assert @book.status.nil? + end + test "constant to access the mapping" do assert_equal 0, Book::STATUS[:proposed] assert_equal 1, Book::STATUS["written"] assert_equal 2, Book::STATUS[:published] end + + test "building new objects with enum scopes" do + assert Book.written.build.written? + assert Book.read.build.read? + end + + test "creating new objects with enum scopes" do + assert Book.written.create.written? + assert Book.read.create.read? + end end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 735f804184..5125d5df2a 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -12,6 +12,7 @@ require 'models/developer' require 'models/customer' require 'models/toy' require 'models/matey' +require 'models/dog' class FinderTest < ActiveRecord::TestCase fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :customers, :categories, :categorizations @@ -641,6 +642,13 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordNotFound) { Topic.find_by_title!("The First Topic!") } end + def test_find_by_on_attribute_that_is_a_reserved_word + dog_alias = 'Dog' + dog = Dog.create(alias: dog_alias) + + assert_equal dog, Dog.find_by_alias(dog_alias) + end + def test_find_by_one_attribute_that_is_an_alias assert_equal topics(:first), Topic.find_by_heading("The First Topic") assert_nil Topic.find_by_heading("The First Topic!") @@ -720,6 +728,15 @@ class FinderTest < ActiveRecord::TestCase assert_raise(ArgumentError) { Topic.find_by_title_and_author_name("The First Topic") } end + def test_find_last_with_offset + devs = Developer.order('id') + + assert_equal devs[2], Developer.offset(2).first + assert_equal devs[-3], Developer.offset(2).last + assert_equal devs[-3], Developer.offset(2).last + assert_equal devs[-3], Developer.offset(2).order('id DESC').first + end + def test_find_by_nil_attribute topic = Topic.find_by_last_read nil assert_not_nil topic @@ -837,10 +854,8 @@ class FinderTest < ActiveRecord::TestCase end def test_with_limiting_with_custom_select - skip 'broken test' if current_adapter?(:MysqlAdapter) - posts = Post.references(:authors).merge( - :includes => :author, :select => ' posts.*, authors.id as "author_id"', + :includes => :author, :select => 'posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id' ).to_a assert_equal 3, posts.size diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb index a029fedbd3..92efa8aca7 100644 --- a/activerecord/test/cases/fixture_set/file_test.rb +++ b/activerecord/test/cases/fixture_set/file_test.rb @@ -68,6 +68,61 @@ module ActiveRecord end end + def test_render_context_helper + ActiveRecord::FixtureSet.context_class.class_eval do + def fixture_helper + "Fixture helper" + end + end + yaml = "one:\n name: <%= fixture_helper %>\n" + tmp_yaml ['curious', 'yml'], yaml do |t| + golden = + [["one", {"name" => "Fixture helper"}]] + assert_equal golden, File.open(t.path) { |fh| fh.to_a } + end + ActiveRecord::FixtureSet.context_class.class_eval do + remove_method :fixture_helper + end + end + + def test_render_context_lookup_scope + yaml = <<END +one: + ActiveRecord: <%= defined? ActiveRecord %> + ActiveRecord_FixtureSet: <%= defined? ActiveRecord::FixtureSet %> + FixtureSet: <%= defined? FixtureSet %> + ActiveRecord_FixtureSet_File: <%= defined? ActiveRecord::FixtureSet::File %> + File: <%= File.name %> +END + + golden = [['one', { + 'ActiveRecord' => 'constant', + 'ActiveRecord_FixtureSet' => 'constant', + 'FixtureSet' => nil, + 'ActiveRecord_FixtureSet_File' => 'constant', + 'File' => 'File' + }]] + + tmp_yaml ['curious', 'yml'], yaml do |t| + assert_equal golden, File.open(t.path) { |fh| fh.to_a } + end + end + + # Make sure that each fixture gets its own rendering context so that + # fixtures are independent. + def test_independent_render_contexts + yaml1 = "<% def leaked_method; 'leak'; end %>\n" + yaml2 = "one:\n name: <%= leaked_method %>\n" + tmp_yaml ['leaky', 'yml'], yaml1 do |t1| + tmp_yaml ['curious', 'yml'], yaml2 do |t2| + File.open(t1.path) { |fh| fh.to_a } + assert_raises(NameError) do + File.open(t2.path) { |fh| fh.to_a } + end + end + end + end + private def tmp_yaml(name, contents) t = Tempfile.new name diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index 34e8f1be0f..3758224b0c 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -20,6 +20,9 @@ Thread.abort_on_exception = true # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true +# Disable available locale checks to avoid warnings running the test suite. +I18n.enforce_available_locales = false + # Connect to the database ARTest.connect @@ -149,23 +152,6 @@ end load_schema -class << Time - unless method_defined? :now_before_time_travel - alias_method :now_before_time_travel, :now - end - - def now - (@now ||= nil) || now_before_time_travel - end - - def travel_to(time, &block) - @now = time - block.call - ensure - @now = nil - end -end - class SQLSubscriber attr_reader :logged attr_reader :payloads @@ -183,7 +169,6 @@ class SQLSubscriber def finish(name, id, payload); end end - module InTimeZone private diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 73cf99a5d7..cb0d374ef7 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -176,14 +176,14 @@ class InheritanceTest < ActiveRecord::TestCase e = assert_raises(NotImplementedError) do AbstractCompany.new end - assert_equal("AbstractCompany is an abstract class and can not be instantiated.", e.message) + assert_equal("AbstractCompany is an abstract class and cannot be instantiated.", e.message) end def test_new_with_ar_base e = assert_raises(NotImplementedError) do ActiveRecord::Base.new end - assert_equal("ActiveRecord::Base is an abstract class and can not be instantiated.", e.message) + assert_equal("ActiveRecord::Base is an abstract class and cannot be instantiated.", e.message) end def test_new_with_invalid_type diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index 07ffcef875..2e71b1a40d 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + require 'cases/helper' require 'models/company' require 'models/developer' @@ -46,6 +48,12 @@ class IntegrationTest < ActiveRecord::TestCase assert_equal '4-ab-ab-ab-ab-ab-ab', firm.to_param end + def test_to_param_class_method_multibyte_character + firm = Firm.find(4) + firm.name = "戦場ヶ原 ひたぎ" + assert_equal '4', firm.to_param + end + def test_to_param_class_method_uses_default_if_blank firm = Firm.find(4) firm.name = nil diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb index e43e256d24..294f2eb9fe 100644 --- a/activerecord/test/cases/migration/change_schema_test.rb +++ b/activerecord/test/cases/migration/change_schema_test.rb @@ -308,6 +308,22 @@ module ActiveRecord assert_equal 2000, connection.select_values("SELECT money FROM testings").first.to_i end + def test_change_column_null + testing_table_with_only_foo_attribute do + notnull_migration = Class.new(ActiveRecord::Migration) do + def change + change_column_null :testings, :foo, false + end + end + notnull_migration.new.suppress_messages do + notnull_migration.migrate(:up) + assert_equal false, connection.columns(:testings).find{ |c| c.name == "foo"}.null + notnull_migration.migrate(:down) + assert connection.columns(:testings).find{ |c| c.name == "foo"}.null + end + end + end + def test_column_exists connection.create_table :testings do |t| t.column :foo, :string diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb index 8065541bfe..c1d7cd5874 100644 --- a/activerecord/test/cases/migration/change_table_test.rb +++ b/activerecord/test/cases/migration/change_table_test.rb @@ -5,7 +5,7 @@ module ActiveRecord class Migration class TableTest < ActiveRecord::TestCase def setup - @connection = MiniTest::Mock.new + @connection = Minitest::Mock.new end def teardown diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 1b205d372f..35b656ee43 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -4,7 +4,8 @@ module ActiveRecord class Migration class CommandRecorderTest < ActiveRecord::TestCase def setup - @recorder = CommandRecorder.new + connection = ActiveRecord::Base.connection + @recorder = CommandRecorder.new(connection) end def test_respond_to_delegates diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 7c3988859f..0363bf1048 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -79,6 +79,10 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::Migrator.migrations_paths = old_path end + def test_migration_version + ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) + end + def test_create_table_with_force_true_does_not_drop_nonexisting_table if Person.connection.table_exists?(:testings2) Person.connection.drop_table :testings2 @@ -443,6 +447,32 @@ class MigrationTest < ActiveRecord::TestCase Person.connection.drop_table :binary_testings rescue nil end + def test_create_table_with_query + Person.connection.drop_table :table_from_query_testings rescue nil + Person.connection.create_table(:person, force: true) + + Person.connection.create_table :table_from_query_testings, as: "SELECT id FROM person" + + columns = Person.connection.columns(:table_from_query_testings) + assert_equal 1, columns.length + assert_equal "id", columns.first.name + + Person.connection.drop_table :table_from_query_testings rescue nil + end + + def test_create_table_with_query_from_relation + Person.connection.drop_table :table_from_query_testings rescue nil + Person.connection.create_table(:person, force: true) + + Person.connection.create_table :table_from_query_testings, as: Person.select(:id) + + columns = Person.connection.columns(:table_from_query_testings) + assert_equal 1, columns.length + assert_equal "id", columns.first.name + + Person.connection.drop_table :table_from_query_testings rescue nil + end + if current_adapter? :OracleAdapter def test_create_table_with_custom_sequence_name # table name is 29 chars, the standard sequence name will @@ -743,7 +773,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps" @existing_migrations = Dir[@migrations_path + "/*.rb"] - Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do + travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") @@ -768,7 +798,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps" sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2" - Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do + travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, sources) assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") @@ -788,7 +818,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps" @existing_migrations = Dir[@migrations_path + "/*.rb"] - Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do + travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) assert File.exist?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb") assert File.exist?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb") @@ -863,7 +893,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase @migrations_path = MIGRATIONS_ROOT + "/non_existing" @existing_migrations = [] - Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do + travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") @@ -878,7 +908,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase @migrations_path = MIGRATIONS_ROOT + "/empty" @existing_migrations = [] - Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do + travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"}) assert File.exist?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb") assert File.exist?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb") diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb index 2e386a172a..3831de6ae3 100644 --- a/activerecord/test/cases/multiple_db_test.rb +++ b/activerecord/test/cases/multiple_db_test.rb @@ -101,7 +101,7 @@ class MultipleDbTest < ActiveRecord::TestCase College.first.courses.first end ensure - ActiveRecord::Base.establish_connection 'arunit' + ActiveRecord::Base.establish_connection :arunit end end end diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 6cd3e2154e..6f1e518f45 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -152,6 +152,20 @@ class PersistenceTest < ActiveRecord::TestCase assert_equal original_errors, client.errors end + def test_dupd_becomes_persists_changes_from_the_original + original = topics(:first) + copy = original.dup.becomes(Reply) + copy.save! + assert_equal "The First Topic", Topic.find(copy.id).title + end + + def test_becomes_includes_changed_attributes + company = Company.new(name: "37signals") + client = company.becomes(Client) + assert_equal "37signals", client.name + assert_equal %w{name}, client.changed + end + def test_delete_many original_count = Topic.count Topic.delete(deleting = [1, 2]) diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 136fda664c..5566563116 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -134,6 +134,15 @@ class QueryCacheTest < ActiveRecord::TestCase end end + def test_find_queries_with_multi_cache_blocks + Task.cache do + Task.cache do + assert_queries(2) { Task.find(1); Task.find(2) } + end + assert_queries(0) { Task.find(1); Task.find(1); Task.find(2) } + end + end + def test_count_queries_with_cache Task.cache do assert_queries(1) { Task.count; Task.count } diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index 13677797cf..9b2bfed039 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -6,95 +6,63 @@ module ActiveRecord class DelegationTest < ActiveRecord::TestCase fixtures :posts - def assert_responds(target, method) - assert target.respond_to?(method) - assert_nothing_raised do - method_arity = target.to_a.method(method).arity + def call_method(target, method) + method_arity = target.to_a.method(method).arity - if method_arity.zero? - target.send(method) - elsif method_arity < 0 - if method == :shuffle! - target.send(method) - else - target.send(method, 1) - end + if method_arity.zero? + target.public_send(method) + elsif method_arity < 0 + if method == :shuffle! + target.public_send(method) else - raise NotImplementedError + target.public_send(method, 1) end + elsif method_arity == 1 + target.public_send(method, 1) + else + raise NotImplementedError end end end - class DelegationAssociationTest < DelegationTest - def target - Post.first.comments - end + module DelegationWhitelistBlacklistTests + ARRAY_DELEGATES = [ + :+, :-, :|, :&, :[], + :all?, :collect, :detect, :each, :each_cons, :each_with_index, + :exclude?, :find_all, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :partition, :reject, :reverse, + :sample, :second, :sort, :sort_by, :third, + :to_ary, :to_set, :to_xml, :to_yaml + ] - [:map, :collect].each do |method| - test "##{method} is delegated" do - assert_responds(target, method) - assert_equal(target.pluck(:body), target.send(method) {|post| post.body }) - end - - test "##{method}! is not delegated" do - assert_deprecated do - assert_responds(target, "#{method}!") - end + ARRAY_DELEGATES.each do |method| + define_method "test_delegates_#{method}_to_Array" do + assert_respond_to target, method end end - [:compact!, :flatten!, :reject!, :reverse!, :rotate!, - :shuffle!, :slice!, :sort!, :sort_by!].each do |method| - test "##{method} delegation is deprecated" do - assert_deprecated do - assert_responds(target, method) - end - end - end - - [:select!, :uniq!].each do |method| - test "##{method} is implemented" do - assert_responds(target, method) + ActiveRecord::Delegation::BLACKLISTED_ARRAY_METHODS.each do |method| + define_method "test_#{method}_is_not_delegated_to_Array" do + assert_raises(NoMethodError) { call_method(target, method) } end end end - class DelegationRelationTest < DelegationTest - fixtures :comments + class DelegationAssociationTest < DelegationTest + include DelegationWhitelistBlacklistTests def target - Comment.where(body: 'Normal type') + Post.first.comments end + end - [:map, :collect].each do |method| - test "##{method} is delegated" do - assert_responds(target, method) - assert_equal(target.pluck(:body), target.send(method) {|post| post.body }) - end - - test "##{method}! is not delegated" do - assert_deprecated do - assert_responds(target, "#{method}!") - end - end - end + class DelegationRelationTest < DelegationTest + include DelegationWhitelistBlacklistTests - [:compact!, :flatten!, :reject!, :reverse!, :rotate!, - :shuffle!, :slice!, :sort!, :sort_by!].each do |method| - test "##{method} delegation is deprecated" do - assert_deprecated do - assert_responds(target, method) - end - end - end + fixtures :comments - [:select!, :uniq!].each do |method| - test "##{method} triggers an immutable error" do - assert_raises ActiveRecord::ImmutableRelation do - assert_responds(target, method) - end - end + def target + Comment.all end end end diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index d44b4dfe3d..fd2420cb88 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -23,6 +23,12 @@ module ActiveRecord assert_equal([expected], relation.where_values) end + def test_not_with_nil + assert_raise ArgumentError do + Post.where.not(nil) + end + end + def test_not_in expected = Arel::Nodes::NotIn.new(Post.arel_table[@name], %w[hello goodbye]) relation = Post.where.not(title: %w[hello goodbye]) diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 56e4605ccc..937f226b1d 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -35,6 +35,21 @@ module ActiveRecord assert_equal Post.where(author_id: 1).to_sql, Post.where(author: author).to_sql end + def test_belongs_to_nil_where + assert_equal Post.where(author_id: nil).to_sql, Post.where(author: nil).to_sql + end + + def test_belongs_to_array_value_where + assert_equal Post.where(author_id: [1,2]).to_sql, Post.where(author: [1,2]).to_sql + end + + def test_belongs_to_nested_relation_where + expected = Post.where(author_id: Author.where(id: [1,2])).to_sql + actual = Post.where(author: Author.where(id: [1,2])).to_sql + + assert_equal expected, actual + end + def test_belongs_to_nested_where parent = Comment.new parent.id = 1 @@ -55,6 +70,25 @@ module ActiveRecord assert_equal expected.to_sql, actual.to_sql end + def test_polymorphic_nested_array_where + treasure = Treasure.new + treasure.id = 1 + hidden = HiddenTreasure.new + hidden.id = 2 + + expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: [treasure, hidden]) + actual = PriceEstimate.where(estimate_of: [treasure, hidden]) + + assert_equal expected.to_sql, actual.to_sql + end + + def test_polymorphic_nested_relation_where + expected = PriceEstimate.where(estimate_of_type: 'Treasure', estimate_of_id: Treasure.where(id: [1,2])) + actual = PriceEstimate.where(estimate_of: Treasure.where(id: [1,2])) + + assert_equal expected.to_sql, actual.to_sql + end + def test_polymorphic_sti_shallow_where treasure = HiddenTreasure.new treasure.id = 1 diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 70d113fb39..15611656fd 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -207,6 +207,16 @@ module ActiveRecord assert_equal 3, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length end + def test_relation_merging_with_joins_as_join_dependency_pick_proper_parent + post = Post.create!(title: "haha", body: "huhu") + comment = post.comments.create!(body: "hu") + 3.times { comment.ratings.create! } + + relation = Post.joins(:comments).merge Comment.joins(:ratings) + + assert_equal 3, relation.where(id: post.id).pluck(:id).size + end + def test_respond_to_for_non_selected_element post = Post.select(:title).first assert_equal false, post.respond_to?(:body), "post should not respond_to?(:body) since invoking it raises exception" @@ -221,6 +231,5 @@ module ActiveRecord posts_with_special_comments_with_ratings = Post.group("posts.id").joins(:special_comments).merge(special_comments_with_ratings) assert_equal 3, authors(:david).posts.merge(posts_with_special_comments_with_ratings).count.length end - end end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index baa3acf3fb..031da8e6d6 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -151,6 +151,11 @@ class RelationTest < ActiveRecord::TestCase assert_equal relation.to_a, Comment.select('a.*').from(relation, :a).to_a end + def test_finding_with_subquery_without_select + relation = Topic.where(:approved => true) + assert_equal relation.to_a, Topic.from(relation).to_a + end + def test_finding_with_conditions assert_equal ["David"], Author.where(:name => 'David').map(&:name) assert_equal ['Mary'], Author.where(["name = ?", 'Mary']).map(&:name) @@ -295,7 +300,7 @@ class RelationTest < ActiveRecord::TestCase def test_null_relation_calculations_methods assert_no_queries do assert_equal 0, Developer.none.count - assert_equal 0, Developer.none.calculate(:count, nil) + assert_equal 0, Developer.none.calculate(:count, nil, {}) assert_equal nil, Developer.none.calculate(:average, 'salary') end end @@ -1506,26 +1511,55 @@ class RelationTest < ActiveRecord::TestCase assert !Post.all.respond_to?(:by_lifo) end - class OMGTopic < ActiveRecord::Base - self.table_name = 'topics' + test "merge collapses wheres from the LHS only" do + left = Post.where(title: "omg").where(comments_count: 1) + right = Post.where(title: "wtf").where(title: "bbq") - def self.__omg__ - "omgtopic" - end + expected = [left.where_values[1]] + right.where_values + merged = left.merge(right) + + assert_equal expected, merged.where_values + assert !merged.to_sql.include?("omg") + assert merged.to_sql.include?("wtf") + assert merged.to_sql.include?("bbq") end - test "delegations do not clash across classes" do - begin - class ::Array - def __omg__ - "array" - end - end + def test_merging_removes_rhs_bind_parameters + left = Post.where(id: Arel::Nodes::BindParam.new('?')) + column = Post.columns_hash['id'] + left.bind_values += [[column, 20]] + right = Post.where(id: 10) - assert_equal "array", Topic.all.__omg__ - assert_equal "omgtopic", OMGTopic.all.__omg__ - ensure - Array.send(:remove_method, :__omg__) - end + merged = left.merge(right) + assert_equal [], merged.bind_values + end + + def test_merging_keeps_lhs_bind_parameters + column = Post.columns_hash['id'] + binds = [[column, 20]] + + right = Post.where(id: Arel::Nodes::BindParam.new('?')) + right.bind_values += binds + left = Post.where(id: 10) + + merged = left.merge(right) + assert_equal binds, merged.bind_values + end + + def test_merging_reorders_bind_params + post = Post.first + id_column = Post.columns_hash['id'] + title_column = Post.columns_hash['title'] + + bv = Post.connection.substitute_at id_column, 0 + + right = Post.where(id: bv) + right.bind_values += [[id_column, post.id]] + + left = Post.where(title: bv) + left.bind_values += [[title_column, post.title]] + + merged = left.merge(right) + assert_equal post, merged.first end end diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 11dd32bfb8..71754cf0a2 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -178,7 +178,7 @@ class DefaultScopingTest < ActiveRecord::TestCase def test_unscope_select expected = Developer.order('salary ASC').collect { |dev| dev.name } - received = Developer.order('salary DESC').reverse_order.select(:name => "Jamis").unscope(:select).collect { |dev| dev.name } + received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect { |dev| dev.name } assert_equal expected, received expected_2 = Developer.all.collect { |dev| dev.id } diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb index 0c9f7ccd55..6f632b4d8d 100644 --- a/activerecord/test/cases/store_test.rb +++ b/activerecord/test/cases/store_test.rb @@ -162,4 +162,8 @@ class StoreTest < ActiveRecord::TestCase assert_equal [:color], first_model.stored_attributes[:data] assert_equal [:width, :height], second_model.stored_attributes[:data] end + + test "YAML coder initializes the store when a Nil value is given" do + assert_equal({}, @john.params) + end end diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index a9a114328c..bf9e14fa4f 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -129,11 +129,22 @@ module ActiveRecord ) end - def test_creates_test_database_when_environment_is_database + def test_creates_test_and_development_databases_when_env_was_not_specified ActiveRecord::Tasks::DatabaseTasks.expects(:create). with('database' => 'dev-db') ActiveRecord::Tasks::DatabaseTasks.expects(:create). with('database' => 'test-db') + ENV.expects(:[]).with('RAILS_ENV').returns(nil) + + ActiveRecord::Tasks::DatabaseTasks.create_current( + ActiveSupport::StringInquirer.new('development') + ) + end + + def test_creates_only_development_database_when_rails_env_is_development + ActiveRecord::Tasks::DatabaseTasks.expects(:create). + with('database' => 'dev-db') + ENV.expects(:[]).with('RAILS_ENV').returns('development') ActiveRecord::Tasks::DatabaseTasks.create_current( ActiveSupport::StringInquirer.new('development') @@ -143,7 +154,7 @@ module ActiveRecord def test_establishes_connection_for_the_given_environment ActiveRecord::Tasks::DatabaseTasks.stubs(:create).returns true - ActiveRecord::Base.expects(:establish_connection).with('development') + ActiveRecord::Base.expects(:establish_connection).with(:development) ActiveRecord::Tasks::DatabaseTasks.create_current( ActiveSupport::StringInquirer.new('development') @@ -239,11 +250,22 @@ module ActiveRecord ) end - def test_creates_test_database_when_environment_is_database + def test_drops_test_and_development_databases_when_env_was_not_specified ActiveRecord::Tasks::DatabaseTasks.expects(:drop). with('database' => 'dev-db') ActiveRecord::Tasks::DatabaseTasks.expects(:drop). with('database' => 'test-db') + ENV.expects(:[]).with('RAILS_ENV').returns(nil) + + ActiveRecord::Tasks::DatabaseTasks.drop_current( + ActiveSupport::StringInquirer.new('development') + ) + end + + def test_drops_only_development_database_when_rails_env_is_development + ActiveRecord::Tasks::DatabaseTasks.expects(:drop). + with('database' => 'dev-db') + ENV.expects(:[]).with('RAILS_ENV').returns('development') ActiveRecord::Tasks::DatabaseTasks.drop_current( ActiveSupport::StringInquirer.new('development') diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 2953b2b2be..717e0e1866 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -272,36 +272,62 @@ class TimestampTest < ActiveRecord::TestCase assert_not_equal time, old_pet.updated_at end - def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record - klass = Class.new(ActiveRecord::Base) do - def self.name; 'Toy'; end + def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_within_same_class + car_class = Class.new(ActiveRecord::Base) do + def self.name; 'Car'; end end - wheel_klass = Class.new(ActiveRecord::Base) do + wheel_class = Class.new(ActiveRecord::Base) do def self.name; 'Wheel'; end belongs_to :wheelable, :polymorphic => true, :touch => true end - toy1 = klass.find(1) - toy2 = klass.find(2) + car1 = car_class.find(1) + car2 = car_class.find(2) - wheel = wheel_klass.new - wheel.wheelable = toy1 - wheel.save! + wheel = wheel_class.create!(wheelable: car1) time = 3.days.ago.at_beginning_of_hour - toy1.update_columns(updated_at: time) - toy2.update_columns(updated_at: time) + car1.update_columns(updated_at: time) + car2.update_columns(updated_at: time) - wheel.wheelable = toy2 + wheel.wheelable = car2 wheel.save! - toy1.reload - toy2.reload + assert_not_equal time, car1.reload.updated_at + assert_not_equal time, car2.reload.updated_at + end + + def test_changing_parent_of_a_record_touches_both_new_and_old_polymorphic_parent_record_changes_with_other_class + car_class = Class.new(ActiveRecord::Base) do + def self.name; 'Car'; end + end + + toy_class = Class.new(ActiveRecord::Base) do + def self.name; 'Toy'; end + end + + wheel_class = Class.new(ActiveRecord::Base) do + def self.name; 'Wheel'; end + belongs_to :wheelable, :polymorphic => true, :touch => true + end + + car = car_class.find(1) + toy = toy_class.find(3) + + wheel = wheel_class.create!(wheelable: car) + + time = 3.days.ago.at_beginning_of_hour + + car.update_columns(updated_at: time) + toy.update_columns(updated_at: time) + + wheel.wheelable = toy + wheel.save! - assert_not_equal time, toy1.updated_at - assert_not_equal time, toy2.updated_at + assert_not_equal time, car.reload.updated_at + assert_not_equal time, toy.reload.updated_at end def test_clearing_association_touches_the_old_record diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb index 84c16fb109..f89c26532d 100644 --- a/activerecord/test/cases/transaction_isolation_test.rb +++ b/activerecord/test/cases/transaction_isolation_test.rb @@ -28,8 +28,8 @@ if ActiveRecord::Base.connection.supports_transaction_isolation? end setup do - Tag.establish_connection 'arunit' - Tag2.establish_connection 'arunit' + Tag.establish_connection :arunit + Tag2.establish_connection :arunit Tag.destroy_all end diff --git a/activerecord/test/cases/validations/presence_validation_test.rb b/activerecord/test/cases/validations/presence_validation_test.rb index 1de8934406..3790d3c8cf 100644 --- a/activerecord/test/cases/validations/presence_validation_test.rb +++ b/activerecord/test/cases/validations/presence_validation_test.rb @@ -3,6 +3,8 @@ require "cases/helper" require 'models/man' require 'models/face' require 'models/interest' +require 'models/speedometer' +require 'models/dashboard' class PresenceValidationTest < ActiveRecord::TestCase class Boy < Man; end @@ -48,4 +50,18 @@ class PresenceValidationTest < ActiveRecord::TestCase i2.mark_for_destruction assert b.invalid? end + + def test_validates_presence_doesnt_convert_to_array + Speedometer.validates_presence_of :dashboard + + dash = Dashboard.new + + # dashboard has to_a method + def dash.to_a; ['(/)', '(\)']; end + + s = Speedometer.new + s.dashboard = dash + + assert_nothing_raised { s.valid? } + end end diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 5f55696c1d..de618902aa 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -56,13 +56,11 @@ class ValidationsTest < ActiveRecord::TestCase assert_raise(ActiveRecord::RecordInvalid) { WrongReply.create! } assert_raise(ActiveRecord::RecordInvalid) { WrongReply.new.save! } - begin - r = WrongReply.new + r = WrongReply.new + invalid = assert_raise ActiveRecord::RecordInvalid do r.save! - flunk - rescue ActiveRecord::RecordInvalid => invalid - assert_equal r, invalid.record end + assert_equal r, invalid.record end def test_exception_on_create_bang_many |