diff options
Diffstat (limited to 'activerecord/test/cases')
49 files changed, 603 insertions, 395 deletions
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 1712ff0ac6..62579a4a7a 100644 --- a/activerecord/test/cases/adapter_test.rb +++ b/activerecord/test/cases/adapter_test.rb @@ -36,6 +36,21 @@ module ActiveRecord assert !@connection.table_exists?(nil) end + def test_data_sources + data_sources = @connection.data_sources + assert data_sources.include?("accounts") + assert data_sources.include?("authors") + assert data_sources.include?("tasks") + assert data_sources.include?("topics") + end + + def test_data_source_exists? + assert @connection.data_source_exists?("accounts") + assert @connection.data_source_exists?(:accounts) + assert_not @connection.data_source_exists?("nonexistingtable") + assert_not @connection.data_source_exists?(nil) + end + def test_indexes idx_name = "accounts_idx" @@ -63,7 +78,7 @@ module ActiveRecord end end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter, :Mysql2Adapter) def test_charset assert_not_nil @connection.charset assert_not_equal 'character_set_database', @connection.charset diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index f0fd95ac16..0b5c9e1798 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -100,17 +100,15 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase assert_equal "DROP TABLE `people`", drop_table(:people) end - if current_adapter?(:MysqlAdapter, :Mysql2Adapter) - def test_create_mysql_database_with_encoding - assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) - assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) - end + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end - def test_recreate_mysql_database_with_encoding - create_database(:luca, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) - end + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) end def test_add_column diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index ddbc007b87..8b62998964 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -183,7 +183,7 @@ class MysqlConnectionTest < ActiveRecord::MysqlTestCase def with_example_table(&block) definition ||= <<-SQL - `id` int(11) auto_increment PRIMARY KEY, + `id` int auto_increment PRIMARY KEY, `data` varchar(255) SQL super(@connection, 'ex', definition, &block) diff --git a/activerecord/test/cases/adapters/mysql/explain_test.rb b/activerecord/test/cases/adapters/mysql/explain_test.rb new file mode 100644 index 0000000000..c44c1e6648 --- /dev/null +++ b/activerecord/test/cases/adapters/mysql/explain_test.rb @@ -0,0 +1,21 @@ +require "cases/helper" +require 'models/developer' +require 'models/computer' + +class MysqlExplainTest < ActiveRecord::MysqlTestCase + fixtures :developers + + 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 %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 %r(developers |.* const), explain + assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` = 1), explain + assert_match %r(audit_logs |.* ALL), explain + end +end diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb index b804cb45b9..29573d8e0d 100644 --- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb +++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb @@ -1,4 +1,3 @@ - require "cases/helper" require 'support/ddl_helper' @@ -66,14 +65,6 @@ module ActiveRecord end end - def test_tables_quoting - @conn.tables(nil, "foo-bar", nil) - flunk - rescue => e - # assertion for *quoted* database properly - assert_match(/database 'foo-bar'/, e.inspect) - end - def test_pk_and_sequence_for with_example_table do pk, seq = @conn.pk_and_sequence_for('ex') @@ -83,7 +74,7 @@ module ActiveRecord end def test_pk_and_sequence_for_with_non_standard_primary_key - with_example_table '`code` INT(11) auto_increment, PRIMARY KEY (`code`)' do + with_example_table '`code` INT auto_increment, PRIMARY KEY (`code`)' do pk, seq = @conn.pk_and_sequence_for('ex') assert_equal 'code', pk assert_equal @conn.default_sequence_name('ex', 'code'), seq @@ -91,7 +82,7 @@ module ActiveRecord end def test_pk_and_sequence_for_with_custom_index_type_pk - with_example_table '`id` INT(11) auto_increment, PRIMARY KEY USING BTREE (`id`)' do + with_example_table '`id` INT auto_increment, PRIMARY KEY USING BTREE (`id`)' do pk, seq = @conn.pk_and_sequence_for('ex') assert_equal 'id', pk assert_equal @conn.default_sequence_name('ex', 'id'), seq @@ -99,7 +90,7 @@ module ActiveRecord end def test_composite_primary_key - with_example_table '`id` INT(11), `number` INT(11), foo INT(11), PRIMARY KEY (`id`, `number`)' do + with_example_table '`id` INT, `number` INT, foo INT, PRIMARY KEY (`id`, `number`)' do assert_nil @conn.primary_key('ex') end end @@ -141,7 +132,7 @@ module ActiveRecord def with_example_table(definition = nil, &block) definition ||= <<-SQL - `id` int(11) auto_increment PRIMARY KEY, + `id` int auto_increment PRIMARY KEY, `number` integer, `data` varchar(255) SQL diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb index 6be36566de..0d1f968022 100644 --- a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb @@ -3,7 +3,7 @@ require 'cases/helper' class MysqlStatementPoolTest < ActiveRecord::MysqlTestCase if Process.respond_to?(:fork) def test_cache_is_per_pid - cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new nil, 10 + cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new(10) cache['foo'] = 'bar' assert_equal 'bar', cache['foo'] diff --git a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb index ed9398a918..84c5394c2e 100644 --- a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb @@ -1,6 +1,8 @@ require "cases/helper" +require "support/schema_dumping_helper" class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase + include SchemaDumpingHelper self.use_transactional_tests = false class UnsignedType < ActiveRecord::Base @@ -9,12 +11,15 @@ class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase setup do @connection = ActiveRecord::Base.connection @connection.create_table("unsigned_types", force: true) do |t| - t.column :unsigned_integer, "int unsigned" + t.integer :unsigned_integer, unsigned: true + t.bigint :unsigned_bigint, unsigned: true + t.float :unsigned_float, unsigned: true + t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2 end end teardown do - @connection.drop_table "unsigned_types" + @connection.drop_table "unsigned_types", if_exists: true end test "unsigned int max value is in range" do @@ -26,5 +31,35 @@ class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase assert_raise(RangeError) do UnsignedType.create(unsigned_integer: -10) end + assert_raise(RangeError) do + UnsignedType.create(unsigned_bigint: -10) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_float: -10.0) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_decimal: -10.0) + end + end + + test "schema definition can use unsigned as the type" do + @connection.change_table("unsigned_types") do |t| + t.unsigned_integer :unsigned_integer_t + t.unsigned_bigint :unsigned_bigint_t + t.unsigned_float :unsigned_float_t + t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2 + end + + @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column| + assert column.unsigned? + end + end + + test "schema dump includes unsigned option" do + schema = dump_table_schema "unsigned_types" + assert_match %r{t.integer\s+"unsigned_integer",\s+limit: 4,\s+unsigned: true$}, schema + assert_match %r{t.integer\s+"unsigned_bigint",\s+limit: 8,\s+unsigned: true$}, schema + assert_match %r{t.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema + assert_match %r{t.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema end end diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 6558d60aa1..31dc69a45b 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -100,17 +100,15 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase assert_equal "DROP TABLE `people`", drop_table(:people) end - if current_adapter?(:Mysql2Adapter) - def test_create_mysql_database_with_encoding - assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) - assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) - end + def test_create_mysql_database_with_encoding + assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt) + assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci}) + end - def test_recreate_mysql_database_with_encoding - create_database(:luca, {:charset => 'latin1'}) - assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) - end + def test_recreate_mysql_database_with_encoding + create_database(:luca, {:charset => 'latin1'}) + assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'}) end def test_add_column diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 880a2123d2..faf2acb9cb 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -36,14 +36,6 @@ module ActiveRecord assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist") end - def test_tables_quoting - @connection.tables(nil, "foo-bar", nil) - flunk - rescue => e - # assertion for *quoted* database properly - assert_match(/database 'foo-bar'/, e.inspect) - end - def test_dump_indexes index_a_name = 'index_key_tests_on_snack' index_b_name = 'index_key_tests_on_pizza' diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb index 9e06db2519..a6f6dd21bb 100644 --- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb +++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb @@ -1,6 +1,8 @@ require "cases/helper" +require "support/schema_dumping_helper" class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase + include SchemaDumpingHelper self.use_transactional_tests = false class UnsignedType < ActiveRecord::Base @@ -9,12 +11,15 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase setup do @connection = ActiveRecord::Base.connection @connection.create_table("unsigned_types", force: true) do |t| - t.column :unsigned_integer, "int unsigned" + t.integer :unsigned_integer, unsigned: true + t.bigint :unsigned_bigint, unsigned: true + t.float :unsigned_float, unsigned: true + t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2 end end teardown do - @connection.drop_table "unsigned_types" + @connection.drop_table "unsigned_types", if_exists: true end test "unsigned int max value is in range" do @@ -26,5 +31,35 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase assert_raise(RangeError) do UnsignedType.create(unsigned_integer: -10) end + assert_raise(RangeError) do + UnsignedType.create(unsigned_bigint: -10) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_float: -10.0) + end + assert_raise(ActiveRecord::StatementInvalid) do + UnsignedType.create(unsigned_decimal: -10.0) + end + end + + test "schema definition can use unsigned as the type" do + @connection.change_table("unsigned_types") do |t| + t.unsigned_integer :unsigned_integer_t + t.unsigned_bigint :unsigned_bigint_t + t.unsigned_float :unsigned_float_t + t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2 + end + + @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column| + assert column.unsigned? + end + end + + test "schema dump includes unsigned option" do + schema = dump_table_schema "unsigned_types" + assert_match %r{t.integer\s+"unsigned_integer",\s+limit: 4,\s+unsigned: true$}, schema + assert_match %r{t.integer\s+"unsigned_bigint",\s+limit: 8,\s+unsigned: true$}, schema + assert_match %r{t.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema + assert_match %r{t.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema 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 6e6850c4a9..e361521155 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -123,6 +123,20 @@ module ActiveRecord assert_equal expect.to_i, result.rows.first.first end + def test_exec_insert_default_values_with_returning_disabled_and_no_sequence_name_given + connection = connection_without_insert_returning + result = connection.exec_insert("insert into postgresql_partitioned_table_parent DEFAULT VALUES", nil, [], 'id') + expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first + assert_equal expect.to_i, result.rows.first.first + end + + def test_exec_insert_default_values_quoted_schema_with_returning_disabled_and_no_sequence_name_given + connection = connection_without_insert_returning + result = connection.exec_insert('insert into "public"."postgresql_partitioned_table_parent" DEFAULT VALUES', nil, [], 'id') + expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first + assert_equal expect.to_i, result.rows.first.first + end + def test_sql_for_insert_with_returning_disabled connection = connection_without_insert_returning result = connection.sql_for_insert('sql', nil, nil, nil, 'binds') diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index bee612d8d3..f89a394f96 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -5,9 +5,11 @@ require 'support/schema_dumping_helper' module PGSchemaHelper def with_schema_search_path(schema_search_path) @connection.schema_search_path = schema_search_path + @connection.schema_cache.clear! yield if block_given? ensure @connection.schema_search_path = "'$user', public" + @connection.schema_cache.clear! end end diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb index ef324183a7..559b951109 100644 --- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb @@ -6,7 +6,7 @@ module ActiveRecord::ConnectionAdapters if Process.respond_to?(:fork) def test_cache_is_per_pid - cache = StatementPool.new nil, 10 + cache = StatementPool.new(10) cache['foo'] = 'bar' assert_equal 'bar', cache['foo'] diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 02b67f901f..938350627f 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -500,7 +500,9 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase line_item = LineItem.create! invoice = Invoice.create!(line_items: [line_item]) initial = invoice.updated_at - line_item.touch + travel(1.second) do + line_item.touch + end assert_not_equal initial, invoice.reload.updated_at 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 d160c30375..20af436e02 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 @@ -95,6 +95,15 @@ class DeveloperWithExtendOption < Developer has_and_belongs_to_many :projects, extend: NamedExtension end +class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base + self.table_name = 'projects' + has_and_belongs_to_many :developers, -> { unscope(where: 'name') }, + class_name: "LazyBlockDeveloperCalledDavid", + join_table: "developers_projects", + foreign_key: "project_id", + association_foreign_key: "developer_id" +end + class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers @@ -936,4 +945,16 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase end assert_equal 1, professor.courses.count end + + def test_habtm_scope_can_unscope + project = ProjectUnscopingDavidDefaultScope.new + project.save! + + developer = LazyBlockDeveloperCalledDavid.new(name: "Not David") + developer.save! + project.developers << developer + + projects = ProjectUnscopingDavidDefaultScope.includes(:developers).where(id: project.id) + assert_equal 1, projects.first.developers.size + end end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 0beaf0056a..cd19a7a5bc 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -170,7 +170,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase part = ShipPart.create(name: 'cockpit') updated_at = part.updated_at - ship.parts << part + travel(1.second) do + ship.parts << part + end assert_equal part.ship, ship assert_not_equal part.updated_at, updated_at diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb index 423b8238b1..ece4dab539 100644 --- a/activerecord/test/cases/associations/inverse_associations_test.rb +++ b/activerecord/test/cases/associations/inverse_associations_test.rb @@ -13,6 +13,9 @@ require 'models/mixed_case_monkey' require 'models/admin' require 'models/admin/account' require 'models/admin/user' +require 'models/developer' +require 'models/company' +require 'models/project' class AutomaticInverseFindingTests < ActiveRecord::TestCase fixtures :ratings, :comments, :cars @@ -198,6 +201,16 @@ class InverseAssociationTests < ActiveRecord::TestCase belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club) assert_nil belongs_to_ref.inverse_of end + + def test_this_inverse_stuff + firm = Firm.create!(name: 'Adequate Holdings') + Project.create!(name: 'Project 1', firm: firm) + Developer.create!(name: 'Gorbypuff', firm: firm) + + new_project = Project.last + assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present" + assert new_project.lead_developer.present?, "Expected lead developer to be present on the project" + end end class InverseHasOneTests < ActiveRecord::TestCase diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index e2608e3670..52d197718e 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -175,9 +175,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert_equal category_attrs , category.attributes_before_type_cast end - if current_adapter?(:MysqlAdapter) + if current_adapter?(:MysqlAdapter, :Mysql2Adapter) def test_read_attributes_before_type_cast_on_boolean - bool = Boolean.create({ "value" => false }) + bool = Boolean.create!({ "value" => false }) if RUBY_PLATFORM =~ /java/ # JRuby will return the value before typecast as string assert_equal "0", bool.reload.attributes_before_type_cast["value"] @@ -542,9 +542,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase developer.save! - assert_equal "50000", developer.salary_before_type_cast - assert_equal 1337, developer.name_before_type_cast - assert_equal 50000, developer.salary assert_equal "1337", developer.name end diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index 9d927481ec..5a0e463a48 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -29,7 +29,7 @@ module ActiveRecord assert_equal :bar, attributes[:bar].name end - test "duping creates a new hash and dups each attribute" do + test "duping creates a new hash, but does not dup the attributes" do builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) attributes = builder.build_from_database(foo: 1, bar: 'foo') @@ -43,6 +43,24 @@ module ActiveRecord assert_equal 1, attributes[:foo].value assert_equal 2, duped[:foo].value + assert_equal 'foobar', attributes[:bar].value + assert_equal 'foobar', duped[:bar].value + end + + test "deep_duping creates a new hash and dups each attribute" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new) + attributes = builder.build_from_database(foo: 1, bar: 'foo') + + # Ensure the type cast value is cached + attributes[:foo].value + attributes[:bar].value + + duped = attributes.deep_dup + duped.write_from_database(:foo, 2) + duped[:bar].value << 'bar' + + assert_equal 1, attributes[:foo].value + assert_equal 2, duped[:foo].value assert_equal 'foo', attributes[:bar].value assert_equal 'foobar', duped[:bar].value end @@ -160,6 +178,9 @@ module ActiveRecord return if value.nil? value + " from database" end + + def assert_valid_value(*) + end end test "write_from_database sets the attribute with database typecasting" do @@ -207,5 +228,16 @@ module ActiveRecord assert_equal [:foo], attributes.accessed end + + test "#map returns a new attribute set with the changes applied" do + builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new) + attributes = builder.build_from_database(foo: "1", bar: "2") + new_attributes = attributes.map do |attr| + attr.with_cast_value(attr.value + 1) + end + + assert_equal 2, new_attributes.fetch_value(:foo) + assert_equal 3, new_attributes.fetch_value(:bar) + end end end diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb index 0ec368f51d..a24a4fc6a4 100644 --- a/activerecord/test/cases/attribute_test.rb +++ b/activerecord/test/cases/attribute_test.rb @@ -4,7 +4,6 @@ module ActiveRecord class AttributeTest < ActiveRecord::TestCase setup do @type = Minitest::Mock.new - @type.expect(:==, false, [false]) end teardown do @@ -108,6 +107,9 @@ module ActiveRecord def deserialize(value) value + " from database" end + + def assert_valid_value(*) + end end test "with_value_from_user returns a new attribute with the value from the user" do @@ -180,12 +182,65 @@ module ActiveRecord assert attribute.has_been_read? end + test "an attribute is not changed if it hasn't been assigned or mutated" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + + refute attribute.changed? + end + + test "an attribute is changed if it's been assigned a new value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + changed = attribute.with_value_from_user(2) + + assert changed.changed? + end + + test "an attribute is not changed if it's assigned the same value" do + attribute = Attribute.from_database(:foo, 1, Type::Value.new) + unchanged = attribute.with_value_from_user(1) + + refute unchanged.changed? + end + test "an attribute can not be mutated if it has not been read, and skips expensive calculations" do type_which_raises_from_all_methods = Object.new attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods) - assert_not attribute.changed_in_place_from?("bar") + assert_not attribute.changed_in_place? + end + + test "an attribute is changed if it has been mutated" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + attribute.value << "!" + + assert attribute.changed_in_place? + assert attribute.changed? + end + + test "an attribute can forget its changes" do + attribute = Attribute.from_database(:foo, "bar", Type::String.new) + changed = attribute.with_value_from_user("foo") + forgotten = changed.forgetting_assignment + + assert changed.changed? # sanity check + refute forgotten.changed? + end + + test "with_value_from_user validates the value" do + type = Type::Value.new + type.define_singleton_method(:assert_valid_value) do |value| + if value == 1 + raise ArgumentError + end + end + + attribute = Attribute.from_database(:foo, 1, type) + assert_equal 1, attribute.value + assert_equal 2, attribute.with_value_from_user(2).value + assert_raises ArgumentError do + attribute.with_value_from_user(1) + end end end end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 3aa0849cb5..dbbcaa075d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -204,7 +204,7 @@ class BasicsTest < ActiveRecord::TestCase ) # For adapters which support microsecond resolution. - if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) || mysql_56? + if subsecond_precision_supported? assert_equal 11, Topic.find(1).written_on.sec assert_equal 223300, Topic.find(1).written_on.usec assert_equal 9900, Topic.find(2).written_on.usec @@ -1571,4 +1571,22 @@ class BasicsTest < ActiveRecord::TestCase assert_not topic.id_changed? end + + test "ignored columns are not present in columns_hash" do + cache_columns = Developer.connection.schema_cache.columns_hash(Developer.table_name) + assert_includes cache_columns.keys, 'first_name' + refute_includes Developer.columns_hash.keys, 'first_name' + end + + test "ignored columns have no attribute methods" do + refute Developer.new.respond_to?(:first_name) + refute Developer.new.respond_to?(:first_name=) + refute Developer.new.respond_to?(:first_name?) + end + + test "ignored columns don't prevent explicit declaration of attribute methods" do + assert Developer.new.respond_to?(:last_name) + assert Developer.new.respond_to?(:last_name=) + assert Developer.new.respond_to?(:last_name?) + end end diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index aa10817527..d904b802fa 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -681,4 +681,36 @@ class CalculationsTest < ActiveRecord::TestCase end assert block_called end + + def test_having_with_strong_parameters + protected_params = Class.new do + attr_reader :permitted + alias :permitted? :permitted + + def initialize(parameters) + @parameters = parameters + @permitted = false + end + + def to_h + @parameters + end + + def permit! + @permitted = true + self + end + end + + params = protected_params.new(credit_limit: '50') + + assert_raises(ActiveModel::ForbiddenAttributesError) do + Account.group(:id).having(params) + end + + result = Account.group(:id).having(params.permit!) + assert_equal 50, result[0].credit_limit + assert_equal 50, result[1].credit_limit + assert_equal 50, result[2].credit_limit + end end diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb index 80244d1439..2749273884 100644 --- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb +++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb @@ -22,6 +22,10 @@ module ActiveRecord assert_lookup_type :string, "SET('one', 'two', 'three')" end + def test_set_type_with_value_matching_other_type + assert_lookup_type :string, "SET('unicode', '8bit', 'none', 'time')" + end + def test_enum_type_with_value_matching_other_type assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')" end diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb index c7531f5418..db832fe55d 100644 --- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb +++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb @@ -29,7 +29,7 @@ module ActiveRecord def test_clearing @cache.columns('posts') @cache.columns_hash('posts') - @cache.tables('posts') + @cache.data_sources('posts') @cache.primary_keys('posts') @cache.clear! @@ -40,17 +40,22 @@ module ActiveRecord def test_dump_and_load @cache.columns('posts') @cache.columns_hash('posts') - @cache.tables('posts') + @cache.data_sources('posts') @cache.primary_keys('posts') @cache = Marshal.load(Marshal.dump(@cache)) assert_equal 11, @cache.columns('posts').size assert_equal 11, @cache.columns_hash('posts').size - assert @cache.tables('posts') + assert @cache.data_sources('posts') assert_equal 'id', @cache.primary_keys('posts') end + def test_table_methods_deprecation + assert_deprecated { assert @cache.table_exists?('posts') } + assert_deprecated { assert @cache.tables('posts') } + assert_deprecated { @cache.clear_table_cache!('posts') } + end end end end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index f5aaf22e13..cd1967c373 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -89,7 +89,7 @@ class DirtyTest < ActiveRecord::TestCase target = Class.new(ActiveRecord::Base) target.table_name = 'pirates' - pirate = target.create + pirate = target.create! pirate.created_on = pirate.created_on assert !pirate.created_on_changed? end @@ -467,8 +467,10 @@ class DirtyTest < ActiveRecord::TestCase topic.save! updated_at = topic.updated_at - topic.content[:hello] = 'world' - topic.save! + travel(1.second) do + topic.content[:hello] = 'world' + topic.save! + end assert_not_equal updated_at, topic.updated_at assert_equal 'world', topic.content[:hello] @@ -521,6 +523,9 @@ class DirtyTest < ActiveRecord::TestCase assert_equal Hash.new, pirate.previous_changes pirate = Pirate.find_by_catchphrase("arrr") + + travel(1.second) + pirate.catchphrase = "Me Maties!" pirate.save! @@ -532,6 +537,9 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.previous_changes.key?('created_on') pirate = Pirate.find_by_catchphrase("Me Maties!") + + travel(1.second) + pirate.catchphrase = "Thar She Blows!" pirate.save @@ -542,6 +550,8 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.previous_changes.key?('parrot_id') assert !pirate.previous_changes.key?('created_on') + travel(1.second) + pirate = Pirate.find_by_catchphrase("Thar She Blows!") pirate.update(catchphrase: "Ahoy!") @@ -552,6 +562,8 @@ class DirtyTest < ActiveRecord::TestCase assert !pirate.previous_changes.key?('parrot_id') assert !pirate.previous_changes.key?('created_on') + travel(1.second) + pirate = Pirate.find_by_catchphrase("Ahoy!") pirate.update_attribute(:catchphrase, "Ninjas suck!") @@ -561,6 +573,8 @@ class DirtyTest < ActiveRecord::TestCase assert_not_nil pirate.previous_changes['updated_on'][1] assert !pirate.previous_changes.key?('parrot_id') assert !pirate.previous_changes.key?('created_on') + ensure + travel_back end if ActiveRecord::Base.connection.supports_migrations? @@ -578,6 +592,7 @@ class DirtyTest < ActiveRecord::TestCase end def test_datetime_attribute_can_be_updated_with_fractional_seconds + skip "Fractional seconds are not supported" unless subsecond_precision_supported? in_time_zone 'Paris' do target = Class.new(ActiveRecord::Base) target.table_name = 'topics' diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 0dc884497c..307b68764e 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -701,12 +701,12 @@ class FinderTest < ActiveRecord::TestCase end def test_bind_arity - assert_nothing_raised { bind '' } + assert_nothing_raised { bind '' } assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 } assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' } - assert_nothing_raised { bind '?', 1 } - assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 } + assert_nothing_raised { bind '?', 1 } + assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 } end def test_named_bind_variables @@ -721,6 +721,12 @@ class FinderTest < ActiveRecord::TestCase assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on end + def test_named_bind_arity + assert_nothing_raised { bind "name = :name", { name: "37signals" } } + assert_nothing_raised { bind "name = :name", { name: "37signals", id: 1 } } + assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", { id: 1 } } + end + class SimpleEnumerable include Enumerable diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb index 92efa8aca7..242e7a9bec 100644 --- a/activerecord/test/cases/fixture_set/file_test.rb +++ b/activerecord/test/cases/fixture_set/file_test.rb @@ -123,6 +123,18 @@ END end end + def test_removes_fixture_config_row + File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh| + assert_equal(['second_welcome'], fh.each.map { |name, _| name }) + end + end + + def test_extracts_model_class_from_config_row + File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh| + assert_equal 'Post', fh.model_class + end + end + private def tmp_yaml(name, contents) t = Tempfile.new name diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index e3c4bd9f4a..a0eaa66e94 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -7,15 +7,16 @@ require 'models/binary' require 'models/book' require 'models/bulb' require 'models/category' +require 'models/comment' require 'models/company' require 'models/computer' require 'models/course' require 'models/developer' +require 'models/doubloon' require 'models/joke' require 'models/matey' require 'models/parrot' require 'models/pirate' -require 'models/doubloon' require 'models/post' require 'models/randomly_named_c1' require 'models/reply' @@ -516,6 +517,38 @@ class OverRideFixtureMethodTest < ActiveRecord::TestCase end end +class FixtureWithSetModelClassTest < ActiveRecord::TestCase + fixtures :other_posts, :other_comments + + # Set to false to blow away fixtures cache and ensure our fixtures are loaded + # and thus takes into account the +set_model_class+. + self.use_transactional_tests = false + + def test_uses_fixture_class_defined_in_yaml + assert_kind_of Post, other_posts(:second_welcome) + end + + def test_loads_the_associations_to_fixtures_with_set_model_class + post = other_posts(:second_welcome) + comment = other_comments(:second_greetings) + assert_equal [comment], post.comments + assert_equal post, comment.post + end +end + +class SetFixtureClassPrevailsTest < ActiveRecord::TestCase + set_fixture_class bad_posts: Post + fixtures :bad_posts + + # Set to false to blow away fixtures cache and ensure our fixtures are loaded + # and thus takes into account the +set_model_class+. + self.use_transactional_tests = false + + def test_uses_set_fixture_class + assert_kind_of Post, bad_posts(:bad_welcome) + end +end + class CheckSetTableNameFixturesTest < ActiveRecord::TestCase set_fixture_class :funny_jokes => Joke fixtures :funny_jokes diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index b61a5126e0..8773986882 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -46,10 +46,10 @@ def in_memory_db? ActiveRecord::Base.connection_pool.spec.config[:database] == ":memory:" end -def mysql_56? - current_adapter?(:MysqlAdapter, :Mysql2Adapter) && - ActiveRecord::Base.connection.send(:version) >= '5.6.0' && - ActiveRecord::Base.connection.send(:version) < '5.7.0' +def subsecond_precision_supported? + !current_adapter?(:MysqlAdapter, :Mysql2Adapter) || + (ActiveRecord::Base.connection.send(:version) >= '5.6.0' && + ActiveRecord::Base.connection.send(:version) < '5.7.0') end def mysql_enforcing_gtid_consistency? diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index 018b7b0d8f..9169207b0a 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -96,7 +96,9 @@ class IntegrationTest < ActiveRecord::TestCase owner.update_column :updated_at, Time.current key = owner.cache_key - assert pet.touch + travel(1.second) do + assert pet.touch + end assert_not_equal key, owner.reload.cache_key end @@ -125,6 +127,7 @@ class IntegrationTest < ActiveRecord::TestCase end def test_cache_key_format_is_precise_enough + skip("Subsecond precision is not supported") unless subsecond_precision_supported? dev = Developer.first key = dev.cache_key dev.touch diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb index 5bc0898f33..ad85684c0b 100644 --- a/activerecord/test/cases/migration/helper.rb +++ b/activerecord/test/cases/migration/helper.rb @@ -28,7 +28,7 @@ module ActiveRecord super TestModel.reset_table_name TestModel.reset_sequence_name - connection.drop_table :test_models rescue nil + connection.drop_table :test_models, if_exists: true end private diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb index 1594f99852..4f0da999d8 100644 --- a/activerecord/test/cases/migration/references_foreign_key_test.rb +++ b/activerecord/test/cases/migration/references_foreign_key_test.rb @@ -32,6 +32,14 @@ module ActiveRecord assert_equal [], @connection.foreign_keys("testings") end + test "foreign keys can be created in one query" do + assert_queries(1) do + @connection.create_table :testings do |t| + t.references :testing_parent, foreign_key: true + end + end + end + test "options hash can be passed" do @connection.change_table :testing_parents do |t| t.integer :other_id diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index 0745a37ee9..5e4ba47988 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -241,6 +241,33 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase end end +class CompositePrimaryKeyTest < ActiveRecord::TestCase + include SchemaDumpingHelper + + self.use_transactional_tests = false + + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t| + t.string :region + t.integer :code + end + end + + def teardown + @connection.drop_table(:barcodes, if_exists: true) + end + + def test_composite_primary_key + assert_equal ["region", "code"], @connection.primary_keys("barcodes") + end + + def test_collectly_dump_composite_primary_key + schema = dump_table_schema "barcodes" + assert_match %r{create_table "barcodes", primary_key: \["region", "code"\]}, schema + end +end + if current_adapter?(:MysqlAdapter, :Mysql2Adapter) class PrimaryKeyWithAnsiQuotesTest < ActiveRecord::TestCase self.use_transactional_tests = false @@ -300,11 +327,12 @@ if current_adapter?(:PostgreSQLAdapter, :MysqlAdapter, :Mysql2Adapter) if current_adapter?(:MysqlAdapter, :Mysql2Adapter) test "primary key column type with options" do - @connection.create_table(:widgets, id: :primary_key, limit: 8, force: true) + @connection.create_table(:widgets, id: :primary_key, limit: 8, unsigned: true, force: true) column = @connection.columns(:widgets).find { |c| c.name == 'id' } assert column.auto_increment? assert_equal :integer, column.type assert_equal 8, column.limit + assert column.unsigned? end end end diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb index 6af31017d6..c3a1471205 100644 --- a/activerecord/test/cases/relation/where_test.rb +++ b/activerecord/test/cases/relation/where_test.rb @@ -276,5 +276,31 @@ module ActiveRecord assert_equal essays(:david_modest_proposal), essay end + + def test_where_with_strong_parameters + protected_params = Class.new do + attr_reader :permitted + alias :permitted? :permitted + + def initialize(parameters) + @parameters = parameters + @permitted = false + end + + def to_h + @parameters + end + + def permit! + @permitted = true + self + end + end + + author = authors(:david) + params = protected_params.new(name: author.name) + assert_raises(ActiveModel::ForbiddenAttributesError) { Author.where(params) } + assert_equal author, Author.where(params.permit!).first + end end end diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index 37d3965022..b0fb905760 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -243,18 +243,23 @@ module ActiveRecord end def test_select_quotes_when_using_from_clause - if sqlite3_version_includes_quoting_bug? - skip <<-ERROR.squish - You are using an outdated version of SQLite3 which has a bug in - quoted column names. Please update SQLite3 and rebuild the sqlite3 - ruby gem - ERROR - end + skip_if_sqlite3_version_includes_quoting_bug quoted_join = ActiveRecord::Base.connection.quote_table_name("join") selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join) assert_equal Post.pluck(:id), selected end + def test_selecting_aliased_attribute_quotes_column_name_when_from_is_used + skip_if_sqlite3_version_includes_quoting_bug + klass = Class.new(ActiveRecord::Base) do + self.table_name = :test_with_keyword_column_name + alias_attribute :description, :desc + end + klass.create!(description: "foo") + + assert_equal ["foo"], klass.select(:description).from(klass.all).map(&:desc) + end + def test_relation_merging_with_merged_joins_as_strings join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id" special_comments_with_ratings = SpecialComment.joins join_string @@ -292,6 +297,16 @@ module ActiveRecord private + def skip_if_sqlite3_version_includes_quoting_bug + if sqlite3_version_includes_quoting_bug? + skip <<-ERROR.squish + You are using an outdated version of SQLite3 which has a bug in + quoted column names. Please update SQLite3 and rebuild the sqlite3 + ruby gem + ERROR + end + end + def sqlite3_version_includes_quoting_bug? if current_adapter?(:SQLite3Adapter) selected_quoted_column_names = ActiveRecord::Base.connection.exec_query( diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb index 262e0abc22..14e392ac30 100644 --- a/activerecord/test/cases/sanitize_test.rb +++ b/activerecord/test/cases/sanitize_test.rb @@ -9,11 +9,11 @@ class SanitizeTest < ActiveRecord::TestCase def test_sanitize_sql_array_handles_string_interpolation quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi") - assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"]) - assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".mb_chars]) + assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"]) + assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars]) quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper") - assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"]) - assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".mb_chars]) + assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"]) + assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars]) end def test_sanitize_sql_array_handles_bind_variables diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index d0deb4c273..a93fa57257 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -270,15 +270,16 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) end - def test_warn_when_external_structure_dump_fails + def test_warn_when_external_structure_dump_command_execution_fails filename = "awesome-file.sql" - Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(false) + Kernel.expects(:system) + .with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db") + .returns(false) - warnings = capture(:stderr) do + e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) - end - - assert_match(/Could not dump the database structure/, warnings) + } + assert_match(/^failed to execute: `mysqldump`$/, e.message) end def test_structure_dump_with_port_number @@ -311,6 +312,7 @@ module ActiveRecord def test_structure_load filename = "awesome-file.sql" Kernel.expects(:system).with('mysql', '--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db") + .returns(true) ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) end diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index 7761ea5612..47e664f4e7 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -103,7 +103,7 @@ module ActiveRecord # ignored SQL, or better yet, use a different notification for the queries # instead examining the SQL content. oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im] - mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /] + mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im] postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i] sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im] diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb index 3f4baf8378..1970fe82d0 100644 --- a/activerecord/test/cases/test_fixtures_test.rb +++ b/activerecord/test/cases/test_fixtures_test.rb @@ -28,7 +28,7 @@ class TestFixturesTest < ActiveRecord::TestCase assert_equal true, @klass.use_transactional_tests end - def test_use_transactional_tests_can_be_overriden + def test_use_transactional_tests_can_be_overridden @klass.use_transactional_tests = "foobar" assert_equal "foobar", @klass.use_transactional_tests diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index 5dab32995c..970f6bcf4a 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -84,7 +84,9 @@ class TimestampTest < ActiveRecord::TestCase def test_touching_an_attribute_updates_timestamp previously_created_at = @developer.created_at - @developer.touch(:created_at) + travel(1.second) do + @developer.touch(:created_at) + end assert !@developer.created_at_changed? , 'created_at should not be changed' assert !@developer.changed?, 'record should not be changed' @@ -199,8 +201,10 @@ class TimestampTest < ActiveRecord::TestCase owner = pet.owner previously_owner_updated_at = owner.updated_at - pet.name = "Fluffy the Third" - pet.save + travel(1.second) do + pet.name = "Fluffy the Third" + pet.save + end assert_not_equal previously_owner_updated_at, pet.owner.updated_at end @@ -210,7 +214,9 @@ class TimestampTest < ActiveRecord::TestCase owner = pet.owner previously_owner_updated_at = owner.updated_at - pet.destroy + travel(1.second) do + pet.destroy + end assert_not_equal previously_owner_updated_at, pet.owner.updated_at end @@ -254,8 +260,10 @@ class TimestampTest < ActiveRecord::TestCase owner.update_columns(happy_at: 3.days.ago) previously_owner_updated_at = owner.updated_at - pet.name = "I'm a parrot" - pet.save + travel(1.second) do + pet.name = "I'm a parrot" + pet.save + end assert_not_equal previously_owner_updated_at, pet.owner.updated_at end diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb index b3c42c8e42..7058f4fbe2 100644 --- a/activerecord/test/cases/touch_later_test.rb +++ b/activerecord/test/cases/touch_later_test.rb @@ -72,7 +72,7 @@ class TouchLaterTest < ActiveRecord::TestCase end def test_touch_touches_immediately_with_a_custom_time - time = Time.now.utc - 25.days + time = (Time.now.utc - 25.days).change(nsec: 0) topic = Topic.create!(updated_at: time, created_at: time) assert_equal time, topic.updated_at assert_equal time, topic.created_at diff --git a/activerecord/test/cases/type/date_time_test.rb b/activerecord/test/cases/type/date_time_test.rb new file mode 100644 index 0000000000..bc4900e1c2 --- /dev/null +++ b/activerecord/test/cases/type/date_time_test.rb @@ -0,0 +1,14 @@ +require "cases/helper" +require "models/task" + +module ActiveRecord + module Type + class IntegerTest < ActiveRecord::TestCase + def test_datetime_seconds_precision_applied_to_timestamp + skip "This test is invalid if subsecond precision isn't supported" unless subsecond_precision_supported? + p = Task.create!(starting: ::Time.now) + assert_equal p.starting.usec, p.reload.starting.usec + end + end + end +end diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb deleted file mode 100644 index 01ee36b892..0000000000 --- a/activerecord/test/cases/type/decimal_test.rb +++ /dev/null @@ -1,56 +0,0 @@ -require "cases/helper" - -module ActiveRecord - module Type - class DecimalTest < ActiveRecord::TestCase - def test_type_cast_decimal - type = Decimal.new - assert_equal BigDecimal.new("0"), type.cast(BigDecimal.new("0")) - assert_equal BigDecimal.new("123"), type.cast(123.0) - assert_equal BigDecimal.new("1"), type.cast(:"1") - end - - def test_type_cast_decimal_from_float_with_large_precision - type = Decimal.new(precision: ::Float::DIG + 2) - assert_equal BigDecimal.new("123.0"), type.cast(123.0) - end - - def test_type_cast_from_float_with_unspecified_precision - type = Decimal.new - assert_equal 22.68.to_d, type.cast(22.68) - end - - def test_type_cast_decimal_from_rational_with_precision - type = Decimal.new(precision: 2) - assert_equal BigDecimal("0.33"), type.cast(Rational(1, 3)) - end - - def test_type_cast_decimal_from_rational_with_precision_and_scale - type = Decimal.new(precision: 4, scale: 2) - assert_equal BigDecimal("0.33"), type.cast(Rational(1, 3)) - end - - def test_type_cast_decimal_from_rational_without_precision_defaults_to_18_36 - type = Decimal.new - assert_equal BigDecimal("0.333333333333333333E0"), type.cast(Rational(1, 3)) - end - - def test_type_cast_decimal_from_object_responding_to_d - value = Object.new - def value.to_d - BigDecimal.new("1") - end - type = Decimal.new - assert_equal BigDecimal("1"), type.cast(value) - end - - def test_changed? - type = Decimal.new - - assert type.changed?(5.0, 5.0, '5.0wibble') - assert_not type.changed?(5.0, 5.0, '5.0') - assert_not type.changed?(-5.0, -5.0, '-5.0') - end - end - end -end diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb index 0dcdbd0667..c0932d5357 100644 --- a/activerecord/test/cases/type/integer_test.rb +++ b/activerecord/test/cases/type/integer_test.rb @@ -4,112 +4,12 @@ require "models/company" module ActiveRecord module Type class IntegerTest < ActiveRecord::TestCase - test "simple values" do - type = Type::Integer.new - assert_equal 1, type.cast(1) - assert_equal 1, type.cast('1') - assert_equal 1, type.cast('1ignore') - assert_equal 0, type.cast('bad1') - assert_equal 0, type.cast('bad') - assert_equal 1, type.cast(1.7) - assert_equal 0, type.cast(false) - assert_equal 1, type.cast(true) - assert_nil type.cast(nil) - end - - test "random objects cast to nil" do - type = Type::Integer.new - assert_nil type.cast([1,2]) - assert_nil type.cast({1 => 2}) - assert_nil type.cast(1..2) - end - test "casting ActiveRecord models" do type = Type::Integer.new firm = Firm.create(:name => 'Apple') assert_nil type.cast(firm) end - test "casting objects without to_i" do - type = Type::Integer.new - assert_nil type.cast(::Object.new) - end - - test "casting nan and infinity" do - type = Type::Integer.new - assert_nil type.cast(::Float::NAN) - assert_nil type.cast(1.0/0.0) - end - - test "casting booleans for database" do - type = Type::Integer.new - assert_equal 1, type.serialize(true) - assert_equal 0, type.serialize(false) - end - - test "changed?" do - type = Type::Integer.new - - assert type.changed?(5, 5, '5wibble') - assert_not type.changed?(5, 5, '5') - assert_not type.changed?(5, 5, '5.0') - assert_not type.changed?(-5, -5, '-5') - assert_not type.changed?(-5, -5, '-5.0') - assert_not type.changed?(nil, nil, nil) - end - - test "values below int min value are out of range" do - assert_raises(::RangeError) do - Integer.new.serialize(-2147483649) - end - end - - test "values above int max value are out of range" do - assert_raises(::RangeError) do - Integer.new.serialize(2147483648) - end - end - - test "very small numbers are out of range" do - assert_raises(::RangeError) do - Integer.new.serialize(-9999999999999999999999999999999) - end - end - - test "very large numbers are out of range" do - assert_raises(::RangeError) do - Integer.new.serialize(9999999999999999999999999999999) - end - end - - test "normal numbers are in range" do - type = Integer.new - assert_equal(0, type.serialize(0)) - assert_equal(-1, type.serialize(-1)) - assert_equal(1, type.serialize(1)) - end - - test "int max value is in range" do - assert_equal(2147483647, Integer.new.serialize(2147483647)) - end - - test "int min value is in range" do - assert_equal(-2147483648, Integer.new.serialize(-2147483648)) - end - - test "columns with a larger limit have larger ranges" do - type = Integer.new(limit: 8) - - assert_equal(9223372036854775807, type.serialize(9223372036854775807)) - assert_equal(-9223372036854775808, type.serialize(-9223372036854775808)) - assert_raises(::RangeError) do - type.serialize(-9999999999999999999999999999999) - end - assert_raises(::RangeError) do - type.serialize(9999999999999999999999999999999) - end - end - test "values which are out of range can be re-assigned" do klass = Class.new(ActiveRecord::Base) do self.table_name = 'posts' diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb index 56e9bf434d..6fe6d46711 100644 --- a/activerecord/test/cases/type/string_test.rb +++ b/activerecord/test/cases/type/string_test.rb @@ -2,20 +2,6 @@ require 'cases/helper' module ActiveRecord class StringTypeTest < ActiveRecord::TestCase - test "type casting" do - type = Type::String.new - assert_equal "t", type.cast(true) - assert_equal "f", type.cast(false) - assert_equal "123", type.cast(123) - end - - test "values are duped coming out" do - s = "foo" - type = Type::String.new - assert_not_same s, type.cast(s) - assert_not_same s, type.deserialize(s) - end - test "string mutations are detected" do klass = Class.new(Base) klass.table_name = 'authors' diff --git a/activerecord/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb deleted file mode 100644 index f2c910eade..0000000000 --- a/activerecord/test/cases/type/unsigned_integer_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -require "cases/helper" - -module ActiveRecord - module Type - class UnsignedIntegerTest < ActiveRecord::TestCase - test "unsigned int max value is in range" do - assert_equal(4294967295, UnsignedInteger.new.serialize(4294967295)) - end - - test "minus value is out of range" do - assert_raises(::RangeError) do - UnsignedInteger.new.serialize(-1) - end - end - end - end -end diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb index 9b1859c2ce..81fcf04a27 100644 --- a/activerecord/test/cases/types_test.rb +++ b/activerecord/test/cases/types_test.rb @@ -3,111 +3,6 @@ require "cases/helper" module ActiveRecord module ConnectionAdapters class TypesTest < ActiveRecord::TestCase - def test_type_cast_boolean - type = Type::Boolean.new - assert type.cast('').nil? - assert type.cast(nil).nil? - - assert type.cast(true) - assert type.cast(1) - assert type.cast('1') - assert type.cast('t') - assert type.cast('T') - assert type.cast('true') - assert type.cast('TRUE') - assert type.cast('on') - assert type.cast('ON') - assert type.cast(' ') - assert type.cast("\u3000\r\n") - assert type.cast("\u0000") - assert type.cast('SOMETHING RANDOM') - - # explicitly check for false vs nil - assert_equal false, type.cast(false) - assert_equal false, type.cast(0) - assert_equal false, type.cast('0') - assert_equal false, type.cast('f') - assert_equal false, type.cast('F') - assert_equal false, type.cast('false') - assert_equal false, type.cast('FALSE') - assert_equal false, type.cast('off') - assert_equal false, type.cast('OFF') - end - - def test_type_cast_float - type = Type::Float.new - assert_equal 1.0, type.cast("1") - end - - def test_changing_float - type = Type::Float.new - - assert type.changed?(5.0, 5.0, '5wibble') - assert_not type.changed?(5.0, 5.0, '5') - assert_not type.changed?(5.0, 5.0, '5.0') - assert_not type.changed?(nil, nil, nil) - end - - def test_type_cast_binary - type = Type::Binary.new - assert_equal nil, type.cast(nil) - assert_equal "1", type.cast("1") - assert_equal 1, type.cast(1) - end - - def test_type_cast_time - type = Type::Time.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast('') - assert_equal nil, type.cast('ABC') - - time_string = Time.now.utc.strftime("%T") - assert_equal time_string, type.cast(time_string).strftime("%T") - end - - def test_type_cast_datetime_and_timestamp - type = Type::DateTime.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast('') - assert_equal nil, type.cast(' ') - assert_equal nil, type.cast('ABC') - - datetime_string = Time.now.utc.strftime("%FT%T") - assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T") - end - - def test_type_cast_date - type = Type::Date.new - assert_equal nil, type.cast(nil) - assert_equal nil, type.cast('') - assert_equal nil, type.cast(' ') - assert_equal nil, type.cast('ABC') - - date_string = Time.now.utc.strftime("%F") - assert_equal date_string, type.cast(date_string).strftime("%F") - end - - def test_type_cast_duration_to_integer - type = Type::Integer.new - assert_equal 1800, type.cast(30.minutes) - assert_equal 7200, type.cast(2.hours) - end - - def test_string_to_time_with_timezone - [:utc, :local].each do |zone| - with_timezone_config default: zone do - type = Type::DateTime.new - assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT") - end - end - end - - def test_type_equality - assert_equal Type::Value.new, Type::Value.new - assert_not_equal Type::Value.new, Type::Integer.new - assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2) - end - def test_attributes_which_are_invalid_for_database_can_still_be_reassigned type_which_cannot_go_to_the_database = Type::Value.new def type_which_cannot_go_to_the_database.serialize(*) diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index a429d06aad..d04f4f7ce7 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -168,4 +168,15 @@ class ValidationsTest < ActiveRecord::TestCase ensure Topic.reset_column_information end + + def test_acceptance_validator_doesnt_require_db_connection + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'posts' + end + klass.reset_column_information + + assert_no_queries do + klass.validates_acceptance_of(:foo) + end + end end diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb index 1eb1430065..e80d8bd584 100644 --- a/activerecord/test/cases/view_test.rb +++ b/activerecord/test/cases/view_test.rb @@ -1,7 +1,9 @@ require "cases/helper" require "models/book" +require "support/schema_dumping_helper" module ViewBehavior + include SchemaDumpingHelper extend ActiveSupport::Concern included do @@ -31,11 +33,26 @@ module ViewBehavior assert_equal ["Ruby for Rails"], books.map(&:name) end + def test_views + assert_equal [Ebook.table_name], @connection.views + end + + def test_view_exists + view_name = Ebook.table_name + assert @connection.view_exists?(view_name), "'#{view_name}' view should exist" + end + def test_table_exists view_name = Ebook.table_name + # TODO: switch this assertion around once we changed #tables to not return views. assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" end + def test_views_ara_valid_data_sources + view_name = Ebook.table_name + assert @connection.data_source_exists?(view_name), "'#{view_name}' should be a data source" + end + def test_column_definitions assert_equal([["id", :integer], ["name", :string], @@ -53,6 +70,11 @@ module ViewBehavior end assert_nil model.primary_key end + + def test_does_not_dump_view_as_table + schema = dump_table_schema "ebooks" + assert_no_match %r{create_table "ebooks"}, schema + end end if ActiveRecord::Base.connection.supports_views? @@ -70,6 +92,7 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase end class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase + include SchemaDumpingHelper fixtures :books class Paperback < ActiveRecord::Base; end @@ -91,6 +114,15 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase assert_equal ["Agile Web Development with Rails"], books.map(&:name) end + def test_views + assert_equal [Paperback.table_name], @connection.views + end + + def test_view_exists + view_name = Paperback.table_name + assert @connection.view_exists?(view_name), "'#{view_name}' view should exist" + end + def test_table_exists view_name = Paperback.table_name assert @connection.table_exists?(view_name), "'#{view_name}' table should exist" @@ -109,6 +141,11 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase def test_does_not_have_a_primary_key assert_nil Paperback.primary_key end + + def test_does_not_dump_view_as_table + schema = dump_table_schema "paperbacks" + assert_no_match %r{create_table "paperbacks"}, schema + end end # sqlite dose not support CREATE, INSERT, and DELETE for VIEW |