mirror of
https://github.com/TracksApp/tracks.git
synced 2026-02-23 07:34:08 +01:00
unfreeze rails 2.3.9
This commit is contained in:
parent
6443adac78
commit
dea6dbe4da
1916 changed files with 0 additions and 240923 deletions
|
|
@ -1 +0,0 @@
|
|||
# Logfile created on Wed Oct 31 16:05:13 +0000 2007 by logger.rb/1.5.2.9
|
||||
BIN
vendor/rails/activerecord/test/assets/flowers.jpg
vendored
BIN
vendor/rails/activerecord/test/assets/flowers.jpg
vendored
Binary file not shown.
|
Before Width: | Height: | Size: 5.7 KiB |
|
|
@ -1,24 +0,0 @@
|
|||
# The filename begins with "aaa" to ensure this is the first test.
|
||||
require "cases/helper"
|
||||
|
||||
class AAACreateTablesTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_load_schema
|
||||
eval(File.read(SCHEMA_ROOT + "/schema.rb"))
|
||||
if File.exists?(adapter_specific_schema_file)
|
||||
eval(File.read(adapter_specific_schema_file))
|
||||
end
|
||||
assert true
|
||||
end
|
||||
|
||||
def test_drop_and_create_courses_table
|
||||
eval(File.read(SCHEMA_ROOT + "/schema2.rb"))
|
||||
assert true
|
||||
end
|
||||
|
||||
private
|
||||
def adapter_specific_schema_file
|
||||
SCHEMA_ROOT + '/' + ActiveRecord::Base.connection.adapter_name.downcase + '_specific_schema.rb'
|
||||
end
|
||||
end
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class ActiveSchemaTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
||||
alias_method :execute_without_stub, :execute
|
||||
def execute(sql, name = nil) return sql end
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
||||
remove_method :execute
|
||||
alias_method :execute, :execute_without_stub
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_index
|
||||
# add_index calls index_exists? which can't work since execute is stubbed
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:define_method, :index_exists?) do |*|
|
||||
false
|
||||
end
|
||||
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`)"
|
||||
assert_equal expected, add_index(:people, :last_name, :length => nil)
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name` ON `people` (`last_name`(10))"
|
||||
assert_equal expected, add_index(:people, :last_name, :length => 10)
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(15))"
|
||||
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => 15)
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`)"
|
||||
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15})
|
||||
|
||||
expected = "CREATE INDEX `index_people_on_last_name_and_first_name` ON `people` (`last_name`(15), `first_name`(10))"
|
||||
assert_equal expected, add_index(:people, [:last_name, :first_name], :length => {:last_name => 15, :first_name => 10})
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.send(:remove_method, :index_exists?)
|
||||
end
|
||||
|
||||
def test_drop_table
|
||||
assert_equal "DROP TABLE `people`", drop_table(:people)
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
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
|
||||
end
|
||||
|
||||
def test_add_column
|
||||
assert_equal "ALTER TABLE `people` ADD `last_name` varchar(255)", add_column(:people, :last_name, :string)
|
||||
end
|
||||
|
||||
def test_add_column_with_limit
|
||||
assert_equal "ALTER TABLE `people` ADD `key` varchar(32)", add_column(:people, :key, :string, :limit => 32)
|
||||
end
|
||||
|
||||
def test_drop_table_with_specific_database
|
||||
assert_equal "DROP TABLE `otherdb`.`people`", drop_table('otherdb.people')
|
||||
end
|
||||
|
||||
def test_add_timestamps
|
||||
with_real_execute do
|
||||
begin
|
||||
ActiveRecord::Base.connection.create_table :delete_me do |t|
|
||||
end
|
||||
ActiveRecord::Base.connection.add_timestamps :delete_me
|
||||
assert column_present?('delete_me', 'updated_at', 'datetime')
|
||||
assert column_present?('delete_me', 'created_at', 'datetime')
|
||||
ensure
|
||||
ActiveRecord::Base.connection.drop_table :delete_me rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_remove_timestamps
|
||||
with_real_execute do
|
||||
begin
|
||||
ActiveRecord::Base.connection.create_table :delete_me do |t|
|
||||
t.timestamps
|
||||
end
|
||||
ActiveRecord::Base.connection.remove_timestamps :delete_me
|
||||
assert !column_present?('delete_me', 'updated_at', 'datetime')
|
||||
assert !column_present?('delete_me', 'created_at', 'datetime')
|
||||
ensure
|
||||
ActiveRecord::Base.connection.drop_table :delete_me rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_real_execute
|
||||
#we need to actually modify some data, so we make execute point to the original method
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
||||
alias_method :execute_with_stub, :execute
|
||||
alias_method :execute, :execute_without_stub
|
||||
end
|
||||
yield
|
||||
ensure
|
||||
#before finishing, we restore the alias to the mock-up method
|
||||
ActiveRecord::ConnectionAdapters::MysqlAdapter.class_eval do
|
||||
alias_method :execute, :execute_with_stub
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def method_missing(method_symbol, *arguments)
|
||||
ActiveRecord::Base.connection.send(method_symbol, *arguments)
|
||||
end
|
||||
|
||||
def column_present?(table_name, column_name, type)
|
||||
results = ActiveRecord::Base.connection.select_all("SHOW FIELDS FROM #{table_name} LIKE '#{column_name}'")
|
||||
results.first && results.first['Type'] == type
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
require 'cases/helper'
|
||||
|
||||
class PostgresqlActiveSchemaTest < Test::Unit::TestCase
|
||||
def setup
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
|
||||
alias_method :real_execute, :execute
|
||||
def execute(sql, name = nil) sql end
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:alias_method, :execute, :real_execute)
|
||||
end
|
||||
|
||||
def test_create_database_with_encoding
|
||||
assert_equal %(CREATE DATABASE "matt" ENCODING = 'utf8'), create_database(:matt)
|
||||
assert_equal %(CREATE DATABASE "aimonetti" ENCODING = 'latin1'), create_database(:aimonetti, :encoding => :latin1)
|
||||
end
|
||||
|
||||
private
|
||||
def method_missing(method_symbol, *arguments)
|
||||
ActiveRecord::Base.connection.send(method_symbol, *arguments)
|
||||
end
|
||||
end
|
||||
144
vendor/rails/activerecord/test/cases/adapter_test.rb
vendored
144
vendor/rails/activerecord/test/cases/adapter_test.rb
vendored
|
|
@ -1,144 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class AdapterTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def test_tables
|
||||
tables = @connection.tables
|
||||
assert tables.include?("accounts")
|
||||
assert tables.include?("authors")
|
||||
assert tables.include?("tasks")
|
||||
assert tables.include?("topics")
|
||||
end
|
||||
|
||||
def test_table_exists?
|
||||
assert @connection.table_exists?("accounts")
|
||||
assert !@connection.table_exists?("nonexistingtable")
|
||||
end
|
||||
|
||||
def test_indexes
|
||||
idx_name = "accounts_idx"
|
||||
|
||||
if @connection.respond_to?(:indexes)
|
||||
indexes = @connection.indexes("accounts")
|
||||
assert indexes.empty?
|
||||
|
||||
@connection.add_index :accounts, :firm_id, :name => idx_name
|
||||
indexes = @connection.indexes("accounts")
|
||||
assert_equal "accounts", indexes.first.table
|
||||
# OpenBase does not have the concept of a named index
|
||||
# Indexes are merely properties of columns.
|
||||
assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
|
||||
assert !indexes.first.unique
|
||||
assert_equal ["firm_id"], indexes.first.columns
|
||||
else
|
||||
warn "#{@connection.class} does not respond to #indexes"
|
||||
end
|
||||
|
||||
ensure
|
||||
@connection.remove_index(:accounts, :name => idx_name) rescue nil
|
||||
end
|
||||
|
||||
def test_current_database
|
||||
if @connection.respond_to?(:current_database)
|
||||
assert_equal ENV['ARUNIT_DB_NAME'] || "activerecord_unittest", @connection.current_database
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
def test_charset
|
||||
assert_not_nil @connection.charset
|
||||
assert_not_equal 'character_set_database', @connection.charset
|
||||
assert_equal @connection.show_variable('character_set_database'), @connection.charset
|
||||
end
|
||||
|
||||
def test_collation
|
||||
assert_not_nil @connection.collation
|
||||
assert_not_equal 'collation_database', @connection.collation
|
||||
assert_equal @connection.show_variable('collation_database'), @connection.collation
|
||||
end
|
||||
|
||||
def test_show_nonexistent_variable_returns_nil
|
||||
assert_nil @connection.show_variable('foo_bar_baz')
|
||||
end
|
||||
|
||||
def test_not_specifying_database_name_for_cross_database_selects
|
||||
begin
|
||||
assert_nothing_raised do
|
||||
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
|
||||
ActiveRecord::Base.connection.execute "SELECT activerecord_unittest.pirates.*, activerecord_unittest2.courses.* FROM activerecord_unittest.pirates, activerecord_unittest2.courses"
|
||||
end
|
||||
ensure
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
def test_encoding
|
||||
assert_not_nil @connection.encoding
|
||||
end
|
||||
end
|
||||
|
||||
def test_table_alias
|
||||
def @connection.test_table_alias_length() 10; end
|
||||
class << @connection
|
||||
alias_method :old_table_alias_length, :table_alias_length
|
||||
alias_method :table_alias_length, :test_table_alias_length
|
||||
end
|
||||
|
||||
assert_equal 'posts', @connection.table_alias_for('posts')
|
||||
assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
|
||||
assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
|
||||
|
||||
class << @connection
|
||||
remove_method :table_alias_length
|
||||
alias_method :table_alias_length, :old_table_alias_length
|
||||
end
|
||||
end
|
||||
|
||||
# test resetting sequences in odd tables in postgreSQL
|
||||
if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
|
||||
require 'models/movie'
|
||||
require 'models/subscriber'
|
||||
|
||||
def test_reset_empty_table_with_custom_pk
|
||||
Movie.delete_all
|
||||
Movie.connection.reset_pk_sequence! 'movies'
|
||||
assert_equal 1, Movie.create(:name => 'fight club').id
|
||||
end
|
||||
|
||||
if ActiveRecord::Base.connection.adapter_name != "FrontBase"
|
||||
def test_reset_table_with_non_integer_pk
|
||||
Subscriber.delete_all
|
||||
Subscriber.connection.reset_pk_sequence! 'subscribers'
|
||||
sub = Subscriber.new(:name => 'robert drake')
|
||||
sub.id = 'bob drake'
|
||||
assert_nothing_raised { sub.save! }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_without_comas
|
||||
sql_inject = "1 select * from schema"
|
||||
assert_equal " LIMIT 1", @connection.add_limit_offset!("", :limit=>sql_inject)
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
||||
else
|
||||
assert_equal " LIMIT 1 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
||||
end
|
||||
end
|
||||
|
||||
def test_add_limit_offset_should_sanitize_sql_injection_for_limit_with_comas
|
||||
sql_inject = "1, 7 procedure help()"
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
||||
assert_equal " LIMIT 7, 1", @connection.add_limit_offset!("", :limit=> '1 ; DROP TABLE USERS', :offset=>7)
|
||||
else
|
||||
assert_equal " LIMIT 1,7", @connection.add_limit_offset!("", :limit=>sql_inject)
|
||||
assert_equal " LIMIT 1,7 OFFSET 7", @connection.add_limit_offset!("", :limit=>sql_inject, :offset=>7)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,167 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/customer'
|
||||
|
||||
class AggregationsTest < ActiveRecord::TestCase
|
||||
fixtures :customers
|
||||
|
||||
def test_find_single_value_object
|
||||
assert_equal 50, customers(:david).balance.amount
|
||||
assert_kind_of Money, customers(:david).balance
|
||||
assert_equal 300, customers(:david).balance.exchange_to("DKK").amount
|
||||
end
|
||||
|
||||
def test_find_multiple_value_object
|
||||
assert_equal customers(:david).address_street, customers(:david).address.street
|
||||
assert(
|
||||
customers(:david).address.close_to?(Address.new("Different Street", customers(:david).address_city, customers(:david).address_country))
|
||||
)
|
||||
end
|
||||
|
||||
def test_change_single_value_object
|
||||
customers(:david).balance = Money.new(100)
|
||||
customers(:david).save
|
||||
assert_equal 100, customers(:david).reload.balance.amount
|
||||
end
|
||||
|
||||
def test_immutable_value_objects
|
||||
customers(:david).balance = Money.new(100)
|
||||
assert_raise(ActiveSupport::FrozenObjectError) { customers(:david).balance.instance_eval { @amount = 20 } }
|
||||
end
|
||||
|
||||
def test_inferred_mapping
|
||||
assert_equal "35.544623640962634", customers(:david).gps_location.latitude
|
||||
assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
|
||||
|
||||
customers(:david).gps_location = GpsLocation.new("39x-110")
|
||||
|
||||
assert_equal "39", customers(:david).gps_location.latitude
|
||||
assert_equal "-110", customers(:david).gps_location.longitude
|
||||
|
||||
customers(:david).save
|
||||
|
||||
customers(:david).reload
|
||||
|
||||
assert_equal "39", customers(:david).gps_location.latitude
|
||||
assert_equal "-110", customers(:david).gps_location.longitude
|
||||
end
|
||||
|
||||
def test_reloaded_instance_refreshes_aggregations
|
||||
assert_equal "35.544623640962634", customers(:david).gps_location.latitude
|
||||
assert_equal "-105.9309951055148", customers(:david).gps_location.longitude
|
||||
|
||||
Customer.update_all("gps_location = '24x113'")
|
||||
customers(:david).reload
|
||||
assert_equal '24x113', customers(:david)['gps_location']
|
||||
|
||||
assert_equal GpsLocation.new('24x113'), customers(:david).gps_location
|
||||
end
|
||||
|
||||
def test_gps_equality
|
||||
assert GpsLocation.new('39x110') == GpsLocation.new('39x110')
|
||||
end
|
||||
|
||||
def test_gps_inequality
|
||||
assert GpsLocation.new('39x110') != GpsLocation.new('39x111')
|
||||
end
|
||||
|
||||
def test_allow_nil_gps_is_nil
|
||||
assert_equal nil, customers(:zaphod).gps_location
|
||||
end
|
||||
|
||||
def test_allow_nil_gps_set_to_nil
|
||||
customers(:david).gps_location = nil
|
||||
customers(:david).save
|
||||
customers(:david).reload
|
||||
assert_equal nil, customers(:david).gps_location
|
||||
end
|
||||
|
||||
def test_allow_nil_set_address_attributes_to_nil
|
||||
customers(:zaphod).address = nil
|
||||
assert_equal nil, customers(:zaphod).attributes[:address_street]
|
||||
assert_equal nil, customers(:zaphod).attributes[:address_city]
|
||||
assert_equal nil, customers(:zaphod).attributes[:address_country]
|
||||
end
|
||||
|
||||
def test_allow_nil_address_set_to_nil
|
||||
customers(:zaphod).address = nil
|
||||
customers(:zaphod).save
|
||||
customers(:zaphod).reload
|
||||
assert_equal nil, customers(:zaphod).address
|
||||
end
|
||||
|
||||
def test_nil_raises_error_when_allow_nil_is_false
|
||||
assert_raise(NoMethodError) { customers(:david).balance = nil }
|
||||
end
|
||||
|
||||
def test_allow_nil_address_loaded_when_only_some_attributes_are_nil
|
||||
customers(:zaphod).address_street = nil
|
||||
customers(:zaphod).save
|
||||
customers(:zaphod).reload
|
||||
assert_kind_of Address, customers(:zaphod).address
|
||||
assert customers(:zaphod).address.street.nil?
|
||||
end
|
||||
|
||||
def test_nil_assignment_results_in_nil
|
||||
customers(:david).gps_location = GpsLocation.new('39x111')
|
||||
assert_not_equal nil, customers(:david).gps_location
|
||||
customers(:david).gps_location = nil
|
||||
assert_equal nil, customers(:david).gps_location
|
||||
end
|
||||
|
||||
def test_custom_constructor
|
||||
assert_equal 'Barney GUMBLE', customers(:barney).fullname.to_s
|
||||
assert_kind_of Fullname, customers(:barney).fullname
|
||||
end
|
||||
|
||||
def test_custom_converter
|
||||
customers(:barney).fullname = 'Barnoit Gumbleau'
|
||||
assert_equal 'Barnoit GUMBLEAU', customers(:barney).fullname.to_s
|
||||
assert_kind_of Fullname, customers(:barney).fullname
|
||||
end
|
||||
end
|
||||
|
||||
class DeprecatedAggregationsTest < ActiveRecord::TestCase
|
||||
class Person < ActiveRecord::Base; end
|
||||
|
||||
def test_conversion_block_is_deprecated
|
||||
assert_deprecated 'conversion block has been deprecated' do
|
||||
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
|
||||
end
|
||||
end
|
||||
|
||||
def test_conversion_block_used_when_converter_option_is_nil
|
||||
assert_deprecated 'conversion block has been deprecated' do
|
||||
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount)) { |balance| balance.to_money }
|
||||
end
|
||||
assert_raise(NoMethodError) { Person.new.balance = 5 }
|
||||
end
|
||||
|
||||
def test_converter_option_overrides_conversion_block
|
||||
assert_deprecated 'conversion block has been deprecated' do
|
||||
Person.composed_of(:balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| Money.new(balance) }) { |balance| balance.to_money }
|
||||
end
|
||||
|
||||
person = Person.new
|
||||
assert_nothing_raised { person.balance = 5 }
|
||||
assert_equal 5, person.balance.amount
|
||||
assert_kind_of Money, person.balance
|
||||
end
|
||||
end
|
||||
|
||||
class OverridingAggregationsTest < ActiveRecord::TestCase
|
||||
class Name; end
|
||||
class DifferentName; end
|
||||
|
||||
class Person < ActiveRecord::Base
|
||||
composed_of :composed_of, :mapping => %w(person_first_name first_name)
|
||||
end
|
||||
|
||||
class DifferentPerson < Person
|
||||
composed_of :composed_of, :class_name => 'DifferentName', :mapping => %w(different_person_first_name first_name)
|
||||
end
|
||||
|
||||
def test_composed_of_aggregation_redefinition_reflections_should_differ_and_not_inherited
|
||||
assert_not_equal Person.reflect_on_aggregation(:composed_of),
|
||||
DifferentPerson.reflect_on_aggregation(:composed_of)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
|
||||
class ActiveRecordSchemaTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.drop_table :fruits rescue nil
|
||||
end
|
||||
|
||||
def test_schema_define
|
||||
ActiveRecord::Schema.define(:version => 7) do
|
||||
create_table :fruits do |t|
|
||||
t.column :color, :string
|
||||
t.column :fruit_size, :string # NOTE: "size" is reserved in Oracle
|
||||
t.column :texture, :string
|
||||
t.column :flavor, :string
|
||||
end
|
||||
end
|
||||
|
||||
assert_nothing_raised { @connection.select_all "SELECT * FROM fruits" }
|
||||
assert_nothing_raised { @connection.select_all "SELECT * FROM schema_migrations" }
|
||||
assert_equal 7, ActiveRecord::Migrator::current_version
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/computer'
|
||||
require 'models/customer'
|
||||
require 'models/order'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/comment'
|
||||
require 'models/sponsor'
|
||||
require 'models/member'
|
||||
require 'models/essay'
|
||||
|
||||
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :topics,
|
||||
:developers_projects, :computers, :authors, :posts, :tags, :taggings, :comments
|
||||
|
||||
def test_belongs_to
|
||||
Client.find(3).firm.name
|
||||
assert_equal companies(:first_firm).name, Client.find(3).firm.name
|
||||
assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_joins_on_correct_column
|
||||
sql = Client.send(:construct_finder_sql, :joins => :firm_with_primary_key)
|
||||
assert sql !~ /\.id/
|
||||
assert sql =~ /\.name/
|
||||
end
|
||||
|
||||
def test_proxy_assignment
|
||||
account = Account.find(1)
|
||||
assert_nothing_raised { account.firm = account.firm }
|
||||
end
|
||||
|
||||
def test_triple_equality
|
||||
assert Client.find(3).firm === Firm
|
||||
assert Firm === Client.find(3).firm
|
||||
end
|
||||
|
||||
def test_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = 1 }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
|
||||
end
|
||||
|
||||
def test_natural_assignment
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
citibank.firm = apple
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_natural_assignment_with_primary_key
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Client.create("name" => "Primary key client")
|
||||
citibank.firm_with_primary_key = apple
|
||||
assert_equal apple.name, citibank.firm_name
|
||||
end
|
||||
|
||||
def test_eager_loading_with_primary_key
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Client.create("name" => "Citibank", :firm_name => "Apple")
|
||||
citibank_result = Client.find(:first, :conditions => {:name => "Citibank"}, :include => :firm_with_primary_key)
|
||||
assert_not_nil citibank_result.instance_variable_get("@firm_with_primary_key")
|
||||
end
|
||||
|
||||
def test_no_unexpected_aliasing
|
||||
first_firm = companies(:first_firm)
|
||||
another_firm = companies(:another_firm)
|
||||
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
citibank.firm = first_firm
|
||||
original_proxy = citibank.firm
|
||||
citibank.firm = another_firm
|
||||
|
||||
assert_equal first_firm.object_id, original_proxy.target.object_id
|
||||
assert_equal another_firm.object_id, citibank.firm.target.object_id
|
||||
end
|
||||
|
||||
def test_creating_the_belonging_object
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple = citibank.create_firm("name" => "Apple")
|
||||
assert_equal apple, citibank.firm
|
||||
citibank.save
|
||||
citibank.reload
|
||||
assert_equal apple, citibank.firm
|
||||
end
|
||||
|
||||
def test_creating_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.create_firm_with_primary_key("name" => "Apple")
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
client.save
|
||||
client.reload
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple = citibank.build_firm("name" => "Apple")
|
||||
citibank.save
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.build_firm_with_primary_key("name" => "Apple")
|
||||
client.save
|
||||
assert_equal apple.name, client.firm_name
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil
|
||||
client = Client.find(3)
|
||||
client.firm = nil
|
||||
client.save
|
||||
assert_nil client.firm(true)
|
||||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
client.firm_with_primary_key = nil
|
||||
client.save
|
||||
assert_nil client.firm_with_primary_key(true)
|
||||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_with_different_class_name
|
||||
assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
|
||||
assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
|
||||
end
|
||||
|
||||
def test_with_condition
|
||||
assert_equal Company.find(1).name, Company.find(3).firm_with_condition.name
|
||||
assert_not_nil Company.find(3).firm_with_condition, "Microsoft should have a firm"
|
||||
end
|
||||
|
||||
def test_with_select
|
||||
assert_equal Company.find(2).firm_with_select.attributes.size, 1
|
||||
assert_equal Company.find(2, :include => :firm_with_select ).firm_with_select.attributes.size, 1
|
||||
end
|
||||
|
||||
def test_belongs_to_counter
|
||||
debate = Topic.create("title" => "debate")
|
||||
assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
|
||||
|
||||
trash = debate.replies.create("title" => "blah!", "content" => "world around!")
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
|
||||
|
||||
trash.destroy
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter
|
||||
debate = Topic.create("title" => "debate")
|
||||
assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
|
||||
|
||||
trash = debate.replies_with_primary_key.create("title" => "blah!", "content" => "world around!")
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
|
||||
|
||||
trash.destroy
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_assigning_nil
|
||||
p = Post.find(1)
|
||||
c = Comment.find(1)
|
||||
|
||||
assert_equal p.id, c.post_id
|
||||
assert_equal 2, Post.find(p.id).comments.size
|
||||
|
||||
c.post = nil
|
||||
|
||||
assert_equal 1, Post.find(p.id).comments.size
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter_with_assigning_nil
|
||||
debate = Topic.create("title" => "debate")
|
||||
reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate")
|
||||
|
||||
assert_equal debate.title, reply.parent_title
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
|
||||
reply.topic_with_primary_key = nil
|
||||
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_reassigning
|
||||
t1 = Topic.create("title" => "t1")
|
||||
t2 = Topic.create("title" => "t2")
|
||||
r1 = Reply.new("title" => "r1", "content" => "r1")
|
||||
r1.topic = t1
|
||||
|
||||
assert r1.save
|
||||
assert_equal 1, Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Topic.find(t2.id).replies.size
|
||||
|
||||
r1.topic = Topic.find(t2.id)
|
||||
|
||||
assert r1.save
|
||||
assert_equal 0, Topic.find(t1.id).replies.size
|
||||
assert_equal 1, Topic.find(t2.id).replies.size
|
||||
|
||||
r1.topic = nil
|
||||
|
||||
assert_equal 0, Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Topic.find(t2.id).replies.size
|
||||
|
||||
r1.topic = t1
|
||||
|
||||
assert_equal 1, Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Topic.find(t2.id).replies.size
|
||||
|
||||
r1.destroy
|
||||
|
||||
assert_equal 0, Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Topic.find(t2.id).replies.size
|
||||
end
|
||||
|
||||
def test_belongs_to_reassign_with_namespaced_models_and_counters
|
||||
t1 = Web::Topic.create("title" => "t1")
|
||||
t2 = Web::Topic.create("title" => "t2")
|
||||
r1 = Web::Reply.new("title" => "r1", "content" => "r1")
|
||||
r1.topic = t1
|
||||
|
||||
assert r1.save
|
||||
assert_equal 1, Web::Topic.find(t1.id).replies.size
|
||||
assert_equal 0, Web::Topic.find(t2.id).replies.size
|
||||
|
||||
r1.topic = Web::Topic.find(t2.id)
|
||||
|
||||
assert r1.save
|
||||
assert_equal 0, Web::Topic.find(t1.id).replies.size
|
||||
assert_equal 1, Web::Topic.find(t2.id).replies.size
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_after_save
|
||||
topic = Topic.create!(:title => "monday night")
|
||||
topic.replies.create!(:title => "re: monday night", :content => "football")
|
||||
assert_equal 1, Topic.find(topic.id)[:replies_count]
|
||||
|
||||
topic.save!
|
||||
assert_equal 1, Topic.find(topic.id)[:replies_count]
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_after_update_attributes
|
||||
topic = Topic.create!(:title => "37s")
|
||||
topic.replies.create!(:title => "re: 37s", :content => "rails")
|
||||
assert_equal 1, Topic.find(topic.id)[:replies_count]
|
||||
|
||||
topic.update_attributes(:title => "37signals")
|
||||
assert_equal 1, Topic.find(topic.id)[:replies_count]
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
firm = Firm.find(1)
|
||||
final_cut.firm = firm
|
||||
assert final_cut.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !firm.new_record?
|
||||
assert_equal firm, final_cut.firm
|
||||
assert_equal firm, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved_with_primary_key
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
firm = Firm.find(1)
|
||||
final_cut.firm_with_primary_key = firm
|
||||
assert final_cut.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !firm.new_record?
|
||||
assert_equal firm, final_cut.firm_with_primary_key
|
||||
assert_equal firm, final_cut.firm_with_primary_key(true)
|
||||
end
|
||||
|
||||
def test_new_record_with_foreign_key_but_no_object
|
||||
c = Client.new("firm_id" => 1)
|
||||
assert_equal Firm.find(:first), c.firm_with_basic_id
|
||||
end
|
||||
|
||||
def test_forgetting_the_load_when_foreign_key_enters_late
|
||||
c = Client.new
|
||||
assert_nil c.firm_with_basic_id
|
||||
|
||||
c.firm_id = 1
|
||||
assert_equal Firm.find(:first), c.firm_with_basic_id
|
||||
end
|
||||
|
||||
def test_field_name_same_as_foreign_key
|
||||
computer = Computer.find(1)
|
||||
assert_not_nil computer.developer, ":foreign key == attribute didn't lock up" # '
|
||||
end
|
||||
|
||||
def test_counter_cache
|
||||
topic = Topic.create :title => "Zoom-zoom-zoom"
|
||||
assert_equal 0, topic[:replies_count]
|
||||
|
||||
reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
|
||||
reply.topic = topic
|
||||
|
||||
assert_equal 1, topic.reload[:replies_count]
|
||||
assert_equal 1, topic.replies.size
|
||||
|
||||
topic[:replies_count] = 15
|
||||
assert_equal 15, topic.replies.size
|
||||
end
|
||||
|
||||
def test_custom_counter_cache
|
||||
reply = Reply.create(:title => "re: zoom", :content => "speedy quick!")
|
||||
assert_equal 0, reply[:replies_count]
|
||||
|
||||
silly = SillyReply.create(:title => "gaga", :content => "boo-boo")
|
||||
silly.reply = reply
|
||||
|
||||
assert_equal 1, reply.reload[:replies_count]
|
||||
assert_equal 1, reply.replies.size
|
||||
|
||||
reply[:replies_count] = 17
|
||||
assert_equal 17, reply.replies.size
|
||||
end
|
||||
|
||||
def test_association_assignment_sticks
|
||||
post = Post.find(:first)
|
||||
|
||||
author1, author2 = Author.find(:all, :limit => 2)
|
||||
assert_not_nil author1
|
||||
assert_not_nil author2
|
||||
|
||||
# make sure the association is loaded
|
||||
post.author
|
||||
|
||||
# set the association by id, directly
|
||||
post.author_id = author2.id
|
||||
|
||||
# save and reload
|
||||
post.save!
|
||||
post.reload
|
||||
|
||||
# the author id of the post should be the id we set
|
||||
assert_equal post.author_id, author2.id
|
||||
end
|
||||
|
||||
def test_cant_save_readonly_association
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_client).readonly_firm.save! }
|
||||
assert companies(:first_client).readonly_firm.readonly?
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_foreign_type_field_updating
|
||||
# should update when assigning a saved record
|
||||
sponsor = Sponsor.new
|
||||
member = Member.create
|
||||
sponsor.sponsorable = member
|
||||
assert_equal "Member", sponsor.sponsorable_type
|
||||
|
||||
# should update when assigning a new record
|
||||
sponsor = Sponsor.new
|
||||
member = Member.new
|
||||
sponsor.sponsorable = member
|
||||
assert_equal "Member", sponsor.sponsorable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating
|
||||
# should update when assigning a saved record
|
||||
essay = Essay.new
|
||||
writer = Author.create(:name => "David")
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
|
||||
# should update when assigning a new record
|
||||
essay = Essay.new
|
||||
writer = Author.new
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records
|
||||
sponsor = Sponsor.new
|
||||
saved_member = Member.create
|
||||
new_member = Member.new
|
||||
|
||||
sponsor.sponsorable = saved_member
|
||||
assert_equal saved_member.id, sponsor.sponsorable_id
|
||||
|
||||
sponsor.sponsorable = new_member
|
||||
assert_equal nil, sponsor.sponsorable_id
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records
|
||||
essay = Essay.new
|
||||
saved_writer = Author.create(:name => "David")
|
||||
new_writer = Author.new
|
||||
|
||||
essay.writer = saved_writer
|
||||
assert_equal saved_writer.name, essay.writer_id
|
||||
|
||||
essay.writer = new_writer
|
||||
assert_equal nil, essay.writer_id
|
||||
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 }
|
||||
end
|
||||
|
||||
def test_belongs_to_proxy_should_respond_to_private_methods_via_send
|
||||
companies(:first_firm).send(:private_method)
|
||||
companies(:second_client).firm.send(:private_method)
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_belongs_to
|
||||
@account = companies(:first_firm).account
|
||||
|
||||
assert_nothing_raised do
|
||||
Account.find(@account.id).save!
|
||||
Account.find(@account.id, :include => :firm).save!
|
||||
end
|
||||
|
||||
@account.firm.delete
|
||||
|
||||
assert_nothing_raised do
|
||||
Account.find(@account.id).save!
|
||||
Account.find(@account.id, :include => :firm).save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
require 'models/project'
|
||||
require 'models/developer'
|
||||
|
||||
class AssociationCallbacksTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :authors, :projects, :developers
|
||||
|
||||
def setup
|
||||
@david = authors(:david)
|
||||
@thinking = posts(:thinking)
|
||||
@authorless = posts(:authorless)
|
||||
assert @david.post_log.empty?
|
||||
end
|
||||
|
||||
def test_adding_macro_callbacks
|
||||
@david.posts_with_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_adding_with_proc_callbacks
|
||||
@david.posts_with_proc_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_proc_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "after_adding#{@thinking.id}", "before_adding#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_removing_with_macro_callbacks
|
||||
first_post, second_post = @david.posts_with_callbacks[0, 2]
|
||||
@david.posts_with_callbacks.delete(first_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
|
||||
@david.posts_with_callbacks.delete(second_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
|
||||
"after_removing#{second_post.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_removing_with_proc_callbacks
|
||||
first_post, second_post = @david.posts_with_callbacks[0, 2]
|
||||
@david.posts_with_proc_callbacks.delete(first_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}"], @david.post_log
|
||||
@david.posts_with_proc_callbacks.delete(second_post)
|
||||
assert_equal ["before_removing#{first_post.id}", "after_removing#{first_post.id}", "before_removing#{second_post.id}",
|
||||
"after_removing#{second_post.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_multiple_callbacks
|
||||
@david.posts_with_multiple_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
|
||||
"after_adding_proc#{@thinking.id}"], @david.post_log
|
||||
@david.posts_with_multiple_callbacks << @thinking
|
||||
assert_equal ["before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}", "after_adding#{@thinking.id}",
|
||||
"after_adding_proc#{@thinking.id}", "before_adding#{@thinking.id}", "before_adding_proc#{@thinking.id}",
|
||||
"after_adding#{@thinking.id}", "after_adding_proc#{@thinking.id}"], @david.post_log
|
||||
end
|
||||
|
||||
def test_has_many_callbacks_with_create
|
||||
morten = Author.create :name => "Morten"
|
||||
post = morten.posts_with_proc_callbacks.create! :title => "Hello", :body => "How are you doing?"
|
||||
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
|
||||
end
|
||||
|
||||
def test_has_many_callbacks_with_create!
|
||||
morten = Author.create! :name => "Morten"
|
||||
post = morten.posts_with_proc_callbacks.create :title => "Hello", :body => "How are you doing?"
|
||||
assert_equal ["before_adding<new>", "after_adding#{post.id}"], morten.post_log
|
||||
end
|
||||
|
||||
def test_has_many_callbacks_for_save_on_parent
|
||||
jack = Author.new :name => "Jack"
|
||||
post = jack.posts_with_callbacks.build :title => "Call me back!", :body => "Before you wake up and after you sleep"
|
||||
|
||||
callback_log = ["before_adding<new>", "after_adding#{jack.posts_with_callbacks.first.id}"]
|
||||
assert_equal callback_log, jack.post_log
|
||||
assert jack.save
|
||||
assert_equal 1, jack.posts_with_callbacks.count
|
||||
assert_equal callback_log, jack.post_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_add_callback
|
||||
david = developers(:david)
|
||||
ar = projects(:active_record)
|
||||
assert ar.developers_log.empty?
|
||||
ar.developers_with_callbacks << david
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}"], ar.developers_log
|
||||
ar.developers_with_callbacks << david
|
||||
assert_equal ["before_adding#{david.id}", "after_adding#{david.id}", "before_adding#{david.id}",
|
||||
"after_adding#{david.id}"], ar.developers_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_after_add_called_after_save
|
||||
ar = projects(:active_record)
|
||||
assert ar.developers_log.empty?
|
||||
alice = Developer.new(:name => 'alice')
|
||||
ar.developers_with_callbacks << alice
|
||||
assert_equal"after_adding#{alice.id}", ar.developers_log.last
|
||||
|
||||
bob = ar.developers_with_callbacks.create(:name => 'bob')
|
||||
assert_equal "after_adding#{bob.id}", ar.developers_log.last
|
||||
|
||||
ar.developers_with_callbacks.build(:name => 'charlie')
|
||||
assert_equal "after_adding<new>", ar.developers_log.last
|
||||
end
|
||||
|
||||
|
||||
def test_has_and_belongs_to_many_remove_callback
|
||||
david = developers(:david)
|
||||
jamis = developers(:jamis)
|
||||
activerecord = projects(:active_record)
|
||||
assert activerecord.developers_log.empty?
|
||||
activerecord.developers_with_callbacks.delete(david)
|
||||
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}"], activerecord.developers_log
|
||||
|
||||
activerecord.developers_with_callbacks.delete(jamis)
|
||||
assert_equal ["before_removing#{david.id}", "after_removing#{david.id}", "before_removing#{jamis.id}",
|
||||
"after_removing#{jamis.id}"], activerecord.developers_log
|
||||
end
|
||||
|
||||
def test_has_and_belongs_to_many_remove_callback_on_clear
|
||||
activerecord = projects(:active_record)
|
||||
assert activerecord.developers_log.empty?
|
||||
if activerecord.developers_with_callbacks.size == 0
|
||||
activerecord.developers << developers(:david)
|
||||
activerecord.developers << developers(:jamis)
|
||||
activerecord.reload
|
||||
assert activerecord.developers_with_callbacks.size == 2
|
||||
end
|
||||
log_array = activerecord.developers_with_callbacks.collect {|d| ["before_removing#{d.id}","after_removing#{d.id}"]}.flatten.sort
|
||||
assert activerecord.developers_with_callbacks.clear
|
||||
assert_equal log_array, activerecord.developers_log.sort
|
||||
end
|
||||
|
||||
def test_has_many_and_belongs_to_many_callbacks_for_save_on_parent
|
||||
project = Project.new :name => "Callbacks"
|
||||
project.developers_with_callbacks.build :name => "Jack", :salary => 95000
|
||||
|
||||
callback_log = ["before_adding<new>", "after_adding<new>"]
|
||||
assert_equal callback_log, project.developers_log
|
||||
assert project.save
|
||||
assert_equal 1, project.developers_with_callbacks.size
|
||||
assert_equal callback_log, project.developers_log
|
||||
end
|
||||
|
||||
def test_dont_add_if_before_callback_raises_exception
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
begin
|
||||
@david.unchangable_posts << @authorless
|
||||
rescue Exception => e
|
||||
end
|
||||
assert @david.post_log.empty?
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
@david.reload
|
||||
assert !@david.unchangable_posts.include?(@authorless)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
|
||||
class CascadedEagerLoadingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :mixins, :companies, :posts, :topics, :accounts, :comments, :categorizations
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels
|
||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_and_one_level
|
||||
authors = Author.find(:all, :include=>[{:posts=>:comments}, :categorizations], :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
assert_equal 1, authors[0].categorizations.size
|
||||
assert_equal 2, authors[1].categorizations.size
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_with_two_has_many_associations
|
||||
authors = Author.find(:all, :include=>{:posts=>[:comments, :categorizations]}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal 1, authors[1].posts.size
|
||||
assert_equal 9, authors[0].posts.collect{|post| post.comments.size }.inject(0){|sum,i| sum+i}
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_and_self_table_reference
|
||||
authors = Author.find(:all, :include=>{:posts=>[:comments, :author]}, :order=>"authors.id")
|
||||
assert_equal 2, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
assert_equal authors(:david).name, authors[0].name
|
||||
assert_equal [authors(:david).name], authors[0].posts.collect{|post| post.author.name}.uniq
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_two_levels_with_condition
|
||||
authors = Author.find(:all, :include=>{:posts=>:comments}, :conditions=>"authors.id=1", :order=>"authors.id")
|
||||
assert_equal 1, authors.size
|
||||
assert_equal 5, authors[0].posts.size
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_cascaded_three_levels_by_ping_pong
|
||||
firms = Firm.find(:all, :include=>{:account=>{:firm=>:account}}, :order=>"companies.id")
|
||||
assert_equal 2, firms.size
|
||||
assert_equal firms.first.account, firms.first.account.firm.account
|
||||
assert_equal companies(:first_firm).account, assert_no_queries { firms.first.account.firm.account }
|
||||
assert_equal companies(:first_firm).account.firm.account, assert_no_queries { firms.first.account.firm.account }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_has_many_sti
|
||||
topics = Topic.find(:all, :include => :replies, :order => 'topics.id')
|
||||
first, second, = topics(:first).replies.size, topics(:second).replies.size
|
||||
assert_no_queries do
|
||||
assert_equal first, topics[0].replies.size
|
||||
assert_equal second, topics[1].replies.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_has_many_sti_and_subclasses
|
||||
silly = SillyReply.new(:title => "gaga", :content => "boo-boo", :parent_id => 1)
|
||||
silly.parent_id = 1
|
||||
assert silly.save
|
||||
|
||||
topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
|
||||
assert_no_queries do
|
||||
assert_equal 2, topics[0].replies.size
|
||||
assert_equal 0, topics[1].replies.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_sti
|
||||
replies = Reply.find(:all, :include => :topic, :order => 'topics.id')
|
||||
assert replies.include?(topics(:second))
|
||||
assert !replies.include?(topics(:first))
|
||||
assert_equal topics(:first), assert_no_queries { replies.first.topic }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_multiple_stis_and_order
|
||||
author = Author.find(:first, :include => { :posts => [ :special_comments , :very_special_comment ] }, :order => 'authors.name, comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
|
||||
assert_equal authors(:david), author
|
||||
assert_no_queries do
|
||||
author.posts.first.special_comments
|
||||
author.posts.first.very_special_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_of_stis_with_multiple_references
|
||||
authors = Author.find(:all, :include => { :posts => { :special_comments => { :post => [ :special_comments, :very_special_comment ] } } }, :order => 'comments.body, very_special_comments_posts.body', :conditions => 'posts.id = 4')
|
||||
assert_equal [authors(:david)], authors
|
||||
assert_no_queries do
|
||||
authors.first.posts.first.special_comments.first.post.special_comments
|
||||
authors.first.posts.first.special_comments.first.post.very_special_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_where_first_level_returns_nil
|
||||
authors = Author.find(:all, :include => {:post_about_thinking => :comments}, :order => 'authors.id DESC')
|
||||
assert_equal [authors(:mary), authors(:david)], authors
|
||||
assert_no_queries do
|
||||
authors[1].post_about_thinking.comments.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'models/vertex'
|
||||
require 'models/edge'
|
||||
class CascadedEagerLoadingTest < ActiveRecord::TestCase
|
||||
fixtures :edges, :vertices
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_four_levels_has_many_through
|
||||
source = Vertex.find(:first, :include=>{:sinks=>{:sinks=>{:sinks=>:sinks}}}, :order => 'vertices.id')
|
||||
assert_equal vertices(:vertex_4), assert_no_queries { source.sinks.first.sinks.first.sinks.first }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_recursive_cascading_four_levels_has_and_belongs_to_many
|
||||
sink = Vertex.find(:first, :include=>{:sources=>{:sources=>{:sources=>:sources}}}, :order => 'vertices.id DESC')
|
||||
assert_equal vertices(:vertex_1), assert_no_queries { sink.sources.first.sources.first.sources.first.sources.first }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/post'
|
||||
require 'models/tagging'
|
||||
|
||||
module Namespaced
|
||||
class Post < ActiveRecord::Base
|
||||
set_table_name 'posts'
|
||||
has_one :tagging, :as => :taggable, :class_name => 'Tagging'
|
||||
end
|
||||
end
|
||||
|
||||
class EagerLoadIncludeFullStiClassNamesTest < ActiveRecord::TestCase
|
||||
|
||||
def setup
|
||||
generate_test_objects
|
||||
end
|
||||
|
||||
def generate_test_objects
|
||||
post = Namespaced::Post.create( :title => 'Great stuff', :body => 'This is not', :author_id => 1 )
|
||||
tagging = Tagging.create( :taggable => post )
|
||||
end
|
||||
|
||||
def test_class_names
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
|
||||
ActiveRecord::Base.store_full_sti_class = false
|
||||
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
|
||||
assert_nil post.tagging
|
||||
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
|
||||
assert_equal 'Tagging', post.tagging.class.name
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
end
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/comment'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
require 'active_support/core_ext/array/random_access'
|
||||
|
||||
module Remembered
|
||||
def self.included(base)
|
||||
base.extend ClassMethods
|
||||
base.class_eval do
|
||||
after_create :remember
|
||||
protected
|
||||
def remember; self.class.remembered << self; end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def remembered; @@remembered ||= []; end
|
||||
def sample; @@remembered.sample; end
|
||||
end
|
||||
end
|
||||
|
||||
class ShapeExpression < ActiveRecord::Base
|
||||
belongs_to :shape, :polymorphic => true
|
||||
belongs_to :paint, :polymorphic => true
|
||||
end
|
||||
|
||||
class Circle < ActiveRecord::Base
|
||||
has_many :shape_expressions, :as => :shape
|
||||
include Remembered
|
||||
end
|
||||
class Square < ActiveRecord::Base
|
||||
has_many :shape_expressions, :as => :shape
|
||||
include Remembered
|
||||
end
|
||||
class Triangle < ActiveRecord::Base
|
||||
has_many :shape_expressions, :as => :shape
|
||||
include Remembered
|
||||
end
|
||||
class PaintColor < ActiveRecord::Base
|
||||
has_many :shape_expressions, :as => :paint
|
||||
belongs_to :non_poly, :foreign_key => "non_poly_one_id", :class_name => "NonPolyOne"
|
||||
include Remembered
|
||||
end
|
||||
class PaintTexture < ActiveRecord::Base
|
||||
has_many :shape_expressions, :as => :paint
|
||||
belongs_to :non_poly, :foreign_key => "non_poly_two_id", :class_name => "NonPolyTwo"
|
||||
include Remembered
|
||||
end
|
||||
class NonPolyOne < ActiveRecord::Base
|
||||
has_many :paint_colors
|
||||
include Remembered
|
||||
end
|
||||
class NonPolyTwo < ActiveRecord::Base
|
||||
has_many :paint_textures
|
||||
include Remembered
|
||||
end
|
||||
|
||||
|
||||
|
||||
class EagerLoadPolyAssocsTest < ActiveRecord::TestCase
|
||||
NUM_SIMPLE_OBJS = 50
|
||||
NUM_SHAPE_EXPRESSIONS = 100
|
||||
|
||||
def setup
|
||||
generate_test_object_graphs
|
||||
end
|
||||
|
||||
def teardown
|
||||
[Circle, Square, Triangle, PaintColor, PaintTexture,
|
||||
ShapeExpression, NonPolyOne, NonPolyTwo].each do |c|
|
||||
c.delete_all
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
def generate_test_object_graphs
|
||||
1.upto(NUM_SIMPLE_OBJS) do
|
||||
[Circle, Square, Triangle, NonPolyOne, NonPolyTwo].map(&:create!)
|
||||
end
|
||||
1.upto(NUM_SIMPLE_OBJS) do
|
||||
PaintColor.create!(:non_poly_one_id => NonPolyOne.sample.id)
|
||||
PaintTexture.create!(:non_poly_two_id => NonPolyTwo.sample.id)
|
||||
end
|
||||
1.upto(NUM_SHAPE_EXPRESSIONS) do
|
||||
shape_type = [Circle, Square, Triangle].sample
|
||||
paint_type = [PaintColor, PaintTexture].sample
|
||||
ShapeExpression.create!(:shape_type => shape_type.to_s, :shape_id => shape_type.sample.id,
|
||||
:paint_type => paint_type.to_s, :paint_id => paint_type.sample.id)
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_query
|
||||
res = 0
|
||||
res = ShapeExpression.find :all, :include => [ :shape, { :paint => :non_poly } ]
|
||||
assert_equal NUM_SHAPE_EXPRESSIONS, res.size
|
||||
assert_queries(0) do
|
||||
res.each do |se|
|
||||
assert_not_nil se.paint.non_poly, "this is the association that was loading incorrectly before the change"
|
||||
assert_not_nil se.shape, "just making sure other associations still work"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class EagerLoadNestedIncludeWithMissingDataTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@davey_mcdave = Author.create(:name => 'Davey McDave')
|
||||
@first_post = @davey_mcdave.posts.create(:title => 'Davey Speaks', :body => 'Expressive wordage')
|
||||
@first_comment = @first_post.comments.create(:body => 'Inflamatory doublespeak')
|
||||
@first_categorization = @davey_mcdave.categorizations.create(:category => Category.first, :post => @first_post)
|
||||
end
|
||||
|
||||
def teardown
|
||||
@davey_mcdave.destroy
|
||||
@first_post.destroy
|
||||
@first_comment.destroy
|
||||
@first_categorization.destroy
|
||||
end
|
||||
|
||||
def test_missing_data_in_a_nested_include_should_not_cause_errors_when_constructing_objects
|
||||
assert_nothing_raised do
|
||||
# @davey_mcdave doesn't have any author_favorites
|
||||
includes = {:posts => :comments, :categorizations => :category, :author_favorites => :favorite_author }
|
||||
Author.all :include => includes, :conditions => {:authors => {:name => @davey_mcdave.name}}, :order => 'categories.name'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/tee'
|
||||
require 'models/tie'
|
||||
require 'models/polymorphic_design'
|
||||
require 'models/polymorphic_price'
|
||||
|
||||
class EagerLoadNestedPolymorphicIncludeTest < ActiveRecord::TestCase
|
||||
fixtures :tees, :ties, :polymorphic_designs, :polymorphic_prices
|
||||
|
||||
def test_eager_load_polymorphic_has_one_nested_under_polymorphic_belongs_to
|
||||
designs = PolymorphicDesign.scoped(:include => {:designable => :polymorphic_price})
|
||||
|
||||
associated_price_ids = designs.map{|design| design.designable.polymorphic_price.id}
|
||||
expected_price_ids = [1, 2, 3, 4]
|
||||
|
||||
assert expected_price_ids.all?{|expected_id| associated_price_ids.include?(expected_id)},
|
||||
"Expected associated prices to be #{expected_price_ids.inspect} but they were #{associated_price_ids.sort.inspect}"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class Virus < ActiveRecord::Base
|
||||
belongs_to :octopus
|
||||
end
|
||||
class Octopus < ActiveRecord::Base
|
||||
has_one :virus
|
||||
end
|
||||
class Pass < ActiveRecord::Base
|
||||
belongs_to :bus
|
||||
end
|
||||
class Bus < ActiveRecord::Base
|
||||
has_many :passes
|
||||
end
|
||||
class Mess < ActiveRecord::Base
|
||||
has_and_belongs_to_many :crises
|
||||
end
|
||||
class Crisis < ActiveRecord::Base
|
||||
has_and_belongs_to_many :messes
|
||||
has_many :analyses, :dependent => :destroy
|
||||
has_many :successes, :through => :analyses
|
||||
has_many :dresses, :dependent => :destroy
|
||||
has_many :compresses, :through => :dresses
|
||||
end
|
||||
class Analysis < ActiveRecord::Base
|
||||
belongs_to :crisis
|
||||
belongs_to :success
|
||||
end
|
||||
class Success < ActiveRecord::Base
|
||||
has_many :analyses, :dependent => :destroy
|
||||
has_many :crises, :through => :analyses
|
||||
end
|
||||
class Dress < ActiveRecord::Base
|
||||
belongs_to :crisis
|
||||
has_many :compresses
|
||||
end
|
||||
class Compress < ActiveRecord::Base
|
||||
belongs_to :dress
|
||||
end
|
||||
|
||||
|
||||
class EagerSingularizationTest < ActiveRecord::TestCase
|
||||
|
||||
def setup
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
ActiveRecord::Base.connection.create_table :viri do |t|
|
||||
t.column :octopus_id, :integer
|
||||
t.column :species, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :octopi do |t|
|
||||
t.column :species, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :passes do |t|
|
||||
t.column :bus_id, :integer
|
||||
t.column :rides, :integer
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :buses do |t|
|
||||
t.column :name, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :crises_messes, :id => false do |t|
|
||||
t.column :crisis_id, :integer
|
||||
t.column :mess_id, :integer
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :messes do |t|
|
||||
t.column :name, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :crises do |t|
|
||||
t.column :name, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :successes do |t|
|
||||
t.column :name, :string
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :analyses do |t|
|
||||
t.column :crisis_id, :integer
|
||||
t.column :success_id, :integer
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :dresses do |t|
|
||||
t.column :crisis_id, :integer
|
||||
end
|
||||
ActiveRecord::Base.connection.create_table :compresses do |t|
|
||||
t.column :dress_id, :integer
|
||||
end
|
||||
@have_tables = true
|
||||
else
|
||||
@have_tables = false
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.connection.drop_table :viri
|
||||
ActiveRecord::Base.connection.drop_table :octopi
|
||||
ActiveRecord::Base.connection.drop_table :passes
|
||||
ActiveRecord::Base.connection.drop_table :buses
|
||||
ActiveRecord::Base.connection.drop_table :crises_messes
|
||||
ActiveRecord::Base.connection.drop_table :messes
|
||||
ActiveRecord::Base.connection.drop_table :crises
|
||||
ActiveRecord::Base.connection.drop_table :successes
|
||||
ActiveRecord::Base.connection.drop_table :analyses
|
||||
ActiveRecord::Base.connection.drop_table :dresses
|
||||
ActiveRecord::Base.connection.drop_table :compresses
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_belongs_to
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Virus.find(:all, :include => :octopus)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_has_one
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Octopus.find(:all, :include => :virus)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_has_many
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Bus.find(:all, :include => :passes)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_has_and_belongs_to_many
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Crisis.find(:all, :include => :messes)
|
||||
Mess.find(:all, :include => :crises)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_has_many_through_belongs_to
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Crisis.find(:all, :include => :successes)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_no_extra_singularization_has_many_through_has_many
|
||||
return unless @have_tables
|
||||
assert_nothing_raised do
|
||||
Crisis.find(:all, :include => :compresses)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,847 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/tagging'
|
||||
require 'models/tag'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
require 'models/company'
|
||||
require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/reference'
|
||||
require 'models/job'
|
||||
require 'models/subscriber'
|
||||
require 'models/subscription'
|
||||
require 'models/book'
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
|
||||
class EagerAssociationTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :comments, :authors, :author_addresses, :categories, :categories_posts,
|
||||
:companies, :accounts, :tags, :taggings, :people, :readers,
|
||||
:owners, :pets, :author_favorites, :jobs, :references, :subscribers, :subscriptions, :books,
|
||||
:developers, :projects, :developers_projects
|
||||
|
||||
def test_loading_with_one_association
|
||||
posts = Post.find(:all, :include => :comments)
|
||||
post = posts.find { |p| p.id == 1 }
|
||||
assert_equal 2, post.comments.size
|
||||
assert post.comments.include?(comments(:greetings))
|
||||
|
||||
post = Post.find(:first, :include => :comments, :conditions => "posts.title = 'Welcome to the weblog'")
|
||||
assert_equal 2, post.comments.size
|
||||
assert post.comments.include?(comments(:greetings))
|
||||
|
||||
posts = Post.find(:all, :include => :last_comment)
|
||||
post = posts.find { |p| p.id == 1 }
|
||||
assert_equal Post.find(1).last_comment, post.last_comment
|
||||
end
|
||||
|
||||
def test_loading_with_one_association_with_non_preload
|
||||
posts = Post.find(:all, :include => :last_comment, :order => 'comments.id DESC')
|
||||
post = posts.find { |p| p.id == 1 }
|
||||
assert_equal Post.find(1).last_comment, post.last_comment
|
||||
end
|
||||
|
||||
def test_loading_conditions_with_or
|
||||
posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
|
||||
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
|
||||
"expected to find only david's posts"
|
||||
end
|
||||
|
||||
def test_with_ordering
|
||||
list = Post.find(:all, :include => :comments, :order => "posts.id DESC")
|
||||
[:eager_other, :sti_habtm, :sti_post_and_comments, :sti_comments,
|
||||
:authorless, :thinking, :welcome
|
||||
].each_with_index do |post, index|
|
||||
assert_equal posts(post), list[index]
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_two_tables_in_from_without_getting_double_quoted
|
||||
posts = Post.find(:all,
|
||||
:select => "posts.*",
|
||||
:from => "authors, posts",
|
||||
:include => :comments,
|
||||
:conditions => "posts.author_id = authors.id",
|
||||
:order => "posts.id"
|
||||
)
|
||||
|
||||
assert_equal 2, posts.first.comments.size
|
||||
end
|
||||
|
||||
def test_loading_with_multiple_associations
|
||||
posts = Post.find(:all, :include => [ :comments, :author, :categories ], :order => "posts.id")
|
||||
assert_equal 2, posts.first.comments.size
|
||||
assert_equal 2, posts.first.categories.size
|
||||
assert posts.first.comments.include?(comments(:greetings))
|
||||
end
|
||||
|
||||
def test_duplicate_middle_objects
|
||||
comments = Comment.find :all, :conditions => 'post_id = 1', :include => [:post => :author]
|
||||
assert_no_queries do
|
||||
comments.each {|comment| comment.post.author.name}
|
||||
end
|
||||
end
|
||||
|
||||
def test_including_duplicate_objects_from_belongs_to
|
||||
popular_post = Post.create!(:title => 'foo', :body => "I like cars!")
|
||||
comment = popular_post.comments.create!(:body => "lol")
|
||||
popular_post.readers.create!(:person => people(:michael))
|
||||
popular_post.readers.create!(:person => people(:david))
|
||||
|
||||
readers = Reader.find(:all, :conditions => ["post_id = ?", popular_post.id],
|
||||
:include => {:post => :comments})
|
||||
readers.each do |reader|
|
||||
assert_equal [comment], reader.post.comments
|
||||
end
|
||||
end
|
||||
|
||||
def test_including_duplicate_objects_from_has_many
|
||||
car_post = Post.create!(:title => 'foo', :body => "I like cars!")
|
||||
car_post.categories << categories(:general)
|
||||
car_post.categories << categories(:technology)
|
||||
|
||||
comment = car_post.comments.create!(:body => "hmm")
|
||||
categories = Category.find(:all, :conditions => ["posts.id=?", car_post.id],
|
||||
:include => {:posts => :comments})
|
||||
categories.each do |category|
|
||||
assert_equal [comment], category.posts[0].comments
|
||||
end
|
||||
end
|
||||
|
||||
def test_finding_with_includes_on_has_many_association_with_same_include_includes_only_once
|
||||
author_id = authors(:david).id
|
||||
author = assert_queries(3) { Author.find(author_id, :include => {:posts_with_comments => :comments}) } # find the author, then find the posts, then find the comments
|
||||
author.posts_with_comments.each do |post_with_comments|
|
||||
assert_equal post_with_comments.comments.length, post_with_comments.comments.count
|
||||
assert_equal nil, post_with_comments.comments.uniq!
|
||||
end
|
||||
end
|
||||
|
||||
def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_only_once
|
||||
author = authors(:david)
|
||||
post = author.post_about_thinking_with_last_comment
|
||||
last_comment = post.last_comment
|
||||
author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
|
||||
assert_no_queries do
|
||||
assert_equal post, author.post_about_thinking_with_last_comment
|
||||
assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
|
||||
end
|
||||
end
|
||||
|
||||
def test_finding_with_includes_on_belongs_to_association_with_same_include_includes_only_once
|
||||
post = posts(:welcome)
|
||||
author = post.author
|
||||
author_address = author.author_address
|
||||
post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
|
||||
assert_no_queries do
|
||||
assert_equal author, post.author_with_address
|
||||
assert_equal author_address, post.author_with_address.author_address
|
||||
end
|
||||
end
|
||||
|
||||
def test_finding_with_includes_on_null_belongs_to_association_with_same_include_includes_only_once
|
||||
post = posts(:welcome)
|
||||
post.update_attributes!(:author => nil)
|
||||
post = assert_queries(1) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author which is null so no query for the author or address
|
||||
assert_no_queries do
|
||||
assert_equal nil, post.author_with_address
|
||||
end
|
||||
end
|
||||
|
||||
def test_loading_from_an_association
|
||||
posts = authors(:david).posts.find(:all, :include => :comments, :order => "posts.id")
|
||||
assert_equal 2, posts.first.comments.size
|
||||
end
|
||||
|
||||
def test_loading_from_an_association_that_has_a_hash_of_conditions
|
||||
assert_nothing_raised do
|
||||
Author.find(:all, :include => :hello_posts_with_hash_conditions)
|
||||
end
|
||||
assert !Author.find(authors(:david).id, :include => :hello_posts_with_hash_conditions).hello_posts.empty?
|
||||
end
|
||||
|
||||
def test_loading_with_no_associations
|
||||
assert_nil Post.find(posts(:authorless).id, :include => :author).author
|
||||
end
|
||||
|
||||
def test_nested_loading_with_no_associations
|
||||
assert_nothing_raised do
|
||||
Post.find(posts(:authorless).id, :include => {:author => :author_addresss})
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_foreign_keys
|
||||
pets = Pet.find(:all, :include => :owner)
|
||||
assert_equal 3, pets.length
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to
|
||||
comments = Comment.find(:all, :include => :post)
|
||||
assert_equal 10, comments.length
|
||||
titles = comments.map { |c| c.post.title }
|
||||
assert titles.include?(posts(:welcome).title)
|
||||
assert titles.include?(posts(:sti_post_and_comments).title)
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit
|
||||
comments = Comment.find(:all, :include => :post, :limit => 5, :order => 'comments.id')
|
||||
assert_equal 5, comments.length
|
||||
assert_equal [1,2,3,5,6], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_conditions
|
||||
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [5,6,7], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset
|
||||
comments = Comment.find(:all, :include => :post, :limit => 3, :offset => 2, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [3,5,6], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions
|
||||
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [6,7,8], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array
|
||||
comments = Comment.find(:all, :include => :post, :conditions => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id')
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [6,7,8], comments.collect { |c| c.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
|
||||
assert_nothing_raised do
|
||||
Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_conditions_hash
|
||||
comments = []
|
||||
assert_nothing_raised do
|
||||
comments = Comment.find(:all, :include => :post, :conditions => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id')
|
||||
end
|
||||
assert_equal 3, comments.length
|
||||
assert_equal [5,6,7], comments.collect { |c| c.id }
|
||||
assert_no_queries do
|
||||
comments.first.post
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
|
||||
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
|
||||
assert_nothing_raised do
|
||||
Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_order_string_with_unquoted_table_name
|
||||
assert_nothing_raised do
|
||||
Comment.find(:all, :include => :post, :order => 'posts.id')
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
|
||||
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
|
||||
assert_nothing_raised do
|
||||
Comment.find(:all, :include => :post, :order => quoted_posts_id)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations
|
||||
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :order => 'posts.id')
|
||||
assert_equal 1, posts.length
|
||||
assert_equal [1], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations
|
||||
posts = Post.find(:all, :include => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id')
|
||||
assert_equal 1, posts.length
|
||||
assert_equal [2], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_belongs_to_inferred_foreign_key_from_association_name
|
||||
author_favorite = AuthorFavorite.find(:first, :include => :favorite_author)
|
||||
assert_equal authors(:mary), assert_no_queries { author_favorite.favorite_author }
|
||||
end
|
||||
|
||||
def test_eager_load_belongs_to_quotes_table_and_column_names
|
||||
job = Job.find jobs(:unicyclist).id, :include => :ideal_reference
|
||||
references(:michael_unicyclist)
|
||||
assert_no_queries{ assert_equal references(:michael_unicyclist), job.ideal_reference}
|
||||
end
|
||||
|
||||
def test_eager_load_has_one_quotes_table_and_column_names
|
||||
michael = Person.find(people(:michael), :include => :favourite_reference)
|
||||
references(:michael_unicyclist)
|
||||
assert_no_queries{ assert_equal references(:michael_unicyclist), michael.favourite_reference}
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_quotes_table_and_column_names
|
||||
michael = Person.find(people(:michael), :include => :references)
|
||||
references(:michael_magician,:michael_unicyclist)
|
||||
assert_no_queries{ assert_equal references(:michael_magician,:michael_unicyclist), michael.references.sort_by(&:id) }
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_quotes_table_and_column_names
|
||||
michael = Person.find(people(:michael), :include => :jobs)
|
||||
jobs(:magician, :unicyclist)
|
||||
assert_no_queries{ assert_equal jobs(:unicyclist, :magician), michael.jobs.sort_by(&:id) }
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_with_string_keys
|
||||
subscriptions = subscriptions(:webster_awdr, :webster_rfr)
|
||||
subscriber =Subscriber.find(subscribers(:second).id, :include => :subscriptions)
|
||||
assert_equal subscriptions, subscriber.subscriptions.sort_by(&:id)
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_with_string_keys
|
||||
books = books(:awdr, :rfr)
|
||||
subscriber = Subscriber.find(subscribers(:second).id, :include => :books)
|
||||
assert_equal books, subscriber.books.sort_by(&:id)
|
||||
end
|
||||
|
||||
def test_eager_load_belongs_to_with_string_keys
|
||||
subscriber = subscribers(:second)
|
||||
subscription = Subscription.find(subscriptions(:webster_awdr).id, :include => :subscriber)
|
||||
assert_equal subscriber, subscription.subscriber
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_explicit_join
|
||||
posts = Post.find(:all, :include => :comments, :joins => "INNER JOIN authors ON posts.author_id = authors.id AND authors.name = 'Mary'", :limit => 1, :order => 'author_id')
|
||||
assert_equal 1, posts.length
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through
|
||||
posts_with_comments = people(:michael).posts.find(:all, :include => :comments, :order => 'posts.id')
|
||||
posts_with_author = people(:michael).posts.find(:all, :include => :author, :order => 'posts.id')
|
||||
posts_with_comments_and_author = people(:michael).posts.find(:all, :include => [ :comments, :author ], :order => 'posts.id')
|
||||
assert_equal 2, posts_with_comments.inject(0) { |sum, post| sum += post.comments.size }
|
||||
assert_equal authors(:david), assert_no_queries { posts_with_author.first.author }
|
||||
assert_equal authors(:david), assert_no_queries { posts_with_comments_and_author.first.author }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_a_belongs_to_association
|
||||
author = authors(:mary)
|
||||
post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
|
||||
author.author_favorites.create(:favorite_author_id => 1)
|
||||
author.author_favorites.create(:favorite_author_id => 2)
|
||||
posts_with_author_favorites = author.posts.find(:all, :include => :author_favorites)
|
||||
assert_no_queries { posts_with_author_favorites.first.author_favorites.first.author_id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_an_sti_join_model
|
||||
author = Author.find(:first, :include => :special_post_comments, :order => 'authors.id')
|
||||
assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both
|
||||
author = Author.find(:first, :include => :special_nonexistant_post_comments, :order => 'authors.id')
|
||||
assert_equal [], author.special_nonexistant_post_comments
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_join_model_with_conditions
|
||||
assert_equal Author.find(:first, :include => :hello_post_comments,
|
||||
:order => 'authors.id').hello_post_comments.sort_by(&:id),
|
||||
Author.find(:first, :order => 'authors.id').hello_post_comments.sort_by(&:id)
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_join_model_with_conditions_on_top_level
|
||||
assert_equal comments(:more_greetings), Author.find(authors(:david).id, :include => :comments_with_order_and_conditions).comments_with_order_and_conditions.first
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_through_with_conditions_join_model_with_include
|
||||
post_tags = Post.find(posts(:welcome).id).misc_tags
|
||||
eager_post_tags = Post.find(1, :include => :misc_tags).misc_tags
|
||||
assert_equal post_tags, eager_post_tags
|
||||
end
|
||||
|
||||
|
||||
def test_eager_with_has_many_through_join_model_with_include
|
||||
author_comments = Author.find(authors(:david).id, :include => :comments_with_include).comments_with_include.to_a
|
||||
assert_no_queries do
|
||||
author_comments.first.post.title
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit
|
||||
posts = Post.find(:all, :order => 'posts.id asc', :include => [ :author, :comments ], :limit => 2)
|
||||
assert_equal 2, posts.size
|
||||
assert_equal 3, posts.inject(0) { |sum, post| sum += post.comments.size }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions
|
||||
if current_adapter?(:OpenBaseAdapter)
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "FETCHBLOB(posts.body) = 'hello'", :order => "posts.id")
|
||||
else
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.body = 'hello'", :order => "posts.id")
|
||||
end
|
||||
assert_equal 2, posts.size
|
||||
assert_equal [4,5], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_array
|
||||
if current_adapter?(:OpenBaseAdapter)
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "FETCHBLOB(posts.body) = ?", 'hello' ], :order => "posts.id")
|
||||
else
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "posts.body = ?", 'hello' ], :order => "posts.id")
|
||||
end
|
||||
assert_equal 2, posts.size
|
||||
assert_equal [4,5], posts.collect { |p| p.id }
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_high_offset
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 0, posts.size
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
|
||||
assert_queries(1) do
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
|
||||
:conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ])
|
||||
assert_equal 0, posts.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_hash_conditions
|
||||
assert_queries(1) do
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
|
||||
:conditions => { 'authors.name' => 'David', 'comments.body' => 'go crazy' })
|
||||
assert_equal 0, posts.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_count_eager_with_has_many_and_limit_and_high_offset
|
||||
posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
|
||||
assert_equal 0, posts
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_with_no_results
|
||||
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => "posts.title = 'magic forest'")
|
||||
assert_equal 0, posts.size
|
||||
end
|
||||
|
||||
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
|
||||
author = authors(:david)
|
||||
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
|
||||
assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
|
||||
end
|
||||
|
||||
def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
|
||||
person = people(:michael)
|
||||
person_posts_without_comments = person.posts.select { |post| post.comments.blank? }
|
||||
assert_equal person_posts_without_comments.size, person.posts_with_no_comments.count
|
||||
end
|
||||
|
||||
def test_eager_with_has_and_belongs_to_many_and_limit
|
||||
posts = Post.find(:all, :include => :categories, :order => "posts.id", :limit => 3)
|
||||
assert_equal 3, posts.size
|
||||
assert_equal 2, posts[0].categories.size
|
||||
assert_equal 1, posts[1].categories.size
|
||||
assert_equal 0, posts[2].categories.size
|
||||
assert posts[0].categories.include?(categories(:technology))
|
||||
assert posts[1].categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers
|
||||
posts = authors(:david).posts.find(:all,
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_scoped_conditions_on_the_eagers
|
||||
posts = nil
|
||||
Post.with_scope(:find => {
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
|
||||
}) do
|
||||
posts = authors(:david).posts.find(:all, :limit => 2)
|
||||
assert_equal 2, posts.size
|
||||
end
|
||||
|
||||
Post.with_scope(:find => {
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
|
||||
}) do
|
||||
count = Post.count(:limit => 2)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_many_and_limit_and_scoped_and_explicit_conditions_on_the_eagers
|
||||
Post.with_scope(:find => { :conditions => "1=1" }) do
|
||||
posts = authors(:david).posts.find(:all,
|
||||
:include => :comments,
|
||||
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal 2, posts.size
|
||||
|
||||
count = Post.count(
|
||||
:include => [ :comments, :author ],
|
||||
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
|
||||
:limit => 2
|
||||
)
|
||||
assert_equal count, posts.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
|
||||
posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
|
||||
posts_with_scoped_order = Post.with_scope(:find => {:order => 'posts.id DESC'}) do
|
||||
Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
|
||||
end
|
||||
assert_equal posts_with_explicit_order, posts_with_scoped_order
|
||||
end
|
||||
|
||||
def test_eager_association_loading_with_habtm
|
||||
posts = Post.find(:all, :include => :categories, :order => "posts.id")
|
||||
assert_equal 2, posts[0].categories.size
|
||||
assert_equal 1, posts[1].categories.size
|
||||
assert_equal 0, posts[2].categories.size
|
||||
assert posts[0].categories.include?(categories(:technology))
|
||||
assert posts[1].categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_eager_with_inheritance
|
||||
posts = SpecialPost.find(:all, :include => [ :comments ])
|
||||
end
|
||||
|
||||
def test_eager_has_one_with_association_inheritance
|
||||
post = Post.find(4, :include => [ :very_special_comment ])
|
||||
assert_equal "VerySpecialComment", post.very_special_comment.class.to_s
|
||||
end
|
||||
|
||||
def test_eager_has_many_with_association_inheritance
|
||||
post = Post.find(4, :include => [ :special_comments ])
|
||||
post.special_comments.each do |special_comment|
|
||||
assert_equal "SpecialComment", special_comment.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_habtm_with_association_inheritance
|
||||
post = Post.find(6, :include => [ :special_categories ])
|
||||
assert_equal 1, post.special_categories.size
|
||||
post.special_categories.each do |special_category|
|
||||
assert_equal "SpecialCategory", special_category.class.to_s
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_has_one_dependent_does_not_destroy_dependent
|
||||
assert_not_nil companies(:first_firm).account
|
||||
f = Firm.find(:first, :include => :account,
|
||||
:conditions => ["companies.name = ?", "37signals"])
|
||||
assert_not_nil f.account
|
||||
assert_equal companies(:first_firm, :reload).account, f.account
|
||||
end
|
||||
|
||||
def test_eager_with_multi_table_conditional_properly_counts_the_records_when_using_size
|
||||
author = authors(:david)
|
||||
posts_with_no_comments = author.posts.select { |post| post.comments.blank? }
|
||||
assert_equal posts_with_no_comments.size, author.posts_with_no_comments.size
|
||||
assert_equal posts_with_no_comments, author.posts_with_no_comments
|
||||
end
|
||||
|
||||
def test_eager_with_invalid_association_reference
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=> :monkeys )
|
||||
}
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ :monkeys ])
|
||||
}
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys") {
|
||||
post = Post.find(6, :include=>[ 'monkeys' ])
|
||||
}
|
||||
assert_raise(ActiveRecord::ConfigurationError, "Association was not found; perhaps you misspelled it? You specified :include => :monkeys, :elephants") {
|
||||
post = Post.find(6, :include=>[ :monkeys, :elephants ])
|
||||
}
|
||||
end
|
||||
|
||||
def find_all_ordered(className, include=nil)
|
||||
className.find(:all, :order=>"#{className.table_name}.#{className.primary_key}", :include=>include)
|
||||
end
|
||||
|
||||
def test_limited_eager_with_order
|
||||
assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
|
||||
assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
|
||||
end
|
||||
|
||||
def test_limited_eager_with_multiple_order_columns
|
||||
assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1)
|
||||
assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1)
|
||||
end
|
||||
|
||||
def test_preload_with_interpolation
|
||||
assert_equal [comments(:greetings)], Post.find(posts(:welcome).id, :include => :comments_with_interpolated_conditions).comments_with_interpolated_conditions
|
||||
end
|
||||
|
||||
def test_polymorphic_type_condition
|
||||
post = Post.find(posts(:thinking).id, :include => :taggings)
|
||||
assert post.taggings.include?(taggings(:thinking_general))
|
||||
post = SpecialPost.find(posts(:thinking).id, :include => :taggings)
|
||||
assert post.taggings.include?(taggings(:thinking_general))
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_has_many_and_habtm
|
||||
# Eager includes of has many and habtm associations aren't necessarily sorted in the same way
|
||||
def assert_equal_after_sort(item1, item2, item3 = nil)
|
||||
assert_equal(item1.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id})
|
||||
assert_equal(item3.sort{|a,b| a.id <=> b.id}, item2.sort{|a,b| a.id <=> b.id}) if item3
|
||||
end
|
||||
# Test regular association, association with conditions, association with
|
||||
# STI, and association with conditions assured not to be true
|
||||
post_types = [:posts, :other_posts, :special_posts]
|
||||
# test both has_many and has_and_belongs_to_many
|
||||
[Author, Category].each do |className|
|
||||
d1 = find_all_ordered(className)
|
||||
# test including all post types at once
|
||||
d2 = find_all_ordered(className, post_types)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
assert_equal_after_sort(d1[i].posts, d2[i].posts)
|
||||
post_types[1..-1].each do |post_type|
|
||||
# test including post_types together
|
||||
d3 = find_all_ordered(className, [:posts, post_type])
|
||||
assert_equal(d1[i], d3[i])
|
||||
assert_equal_after_sort(d1[i].posts, d3[i].posts)
|
||||
assert_equal_after_sort(d1[i].send(post_type), d2[i].send(post_type), d3[i].send(post_type))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_has_one
|
||||
d1 = find_all_ordered(Firm)
|
||||
d2 = find_all_ordered(Firm, :account)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
assert_equal(d1[i].account, d2[i].account)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_with_multiple_associations_with_same_table_belongs_to
|
||||
firm_types = [:firm, :firm_with_basic_id, :firm_with_other_name, :firm_with_condition]
|
||||
d1 = find_all_ordered(Client)
|
||||
d2 = find_all_ordered(Client, firm_types)
|
||||
d1.each_index do |i|
|
||||
assert_equal(d1[i], d2[i])
|
||||
firm_types.each { |type| assert_equal(d1[i].send(type), d2[i].send(type)) }
|
||||
end
|
||||
end
|
||||
def test_eager_with_valid_association_as_string_not_symbol
|
||||
assert_nothing_raised { Post.find(:all, :include => 'comments') }
|
||||
end
|
||||
|
||||
def test_eager_with_floating_point_numbers
|
||||
assert_queries(2) do
|
||||
# Before changes, the floating point numbers will be interpreted as table names and will cause this to run in one query
|
||||
Comment.find :all, :conditions => "123.456 = 123.456", :include => :post
|
||||
end
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_belongs_to
|
||||
author = posts(:welcome).author_with_posts
|
||||
assert_no_queries {assert_equal 5, author.posts.size}
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_one
|
||||
comment = posts(:sti_comments).very_special_comment_with_post
|
||||
assert_no_queries {assert_equal posts(:sti_comments), comment.post}
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_many
|
||||
posts = authors(:david).posts_with_comments
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_no_queries do
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.comments.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_habtm
|
||||
posts = authors(:david).posts_with_categories
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_no_queries do
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.categories.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_preconfigured_includes_with_has_many_and_habtm
|
||||
posts = authors(:david).posts_with_comments_and_categories
|
||||
one = posts.detect { |p| p.id == 1 }
|
||||
assert_no_queries do
|
||||
assert_equal 5, posts.size
|
||||
assert_equal 2, one.comments.size
|
||||
assert_equal 2, one.categories.size
|
||||
end
|
||||
end
|
||||
|
||||
def test_count_with_include
|
||||
if current_adapter?(:SybaseAdapter)
|
||||
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
|
||||
elsif current_adapter?(:OpenBaseAdapter)
|
||||
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
|
||||
else
|
||||
assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
|
||||
end
|
||||
end
|
||||
|
||||
def test_load_with_sti_sharing_association
|
||||
assert_queries(2) do #should not do 1 query per subclass
|
||||
Comment.find :all, :include => :post
|
||||
end
|
||||
end
|
||||
|
||||
def test_conditions_on_join_table_with_include_and_limit
|
||||
assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
|
||||
end
|
||||
|
||||
def test_order_on_join_table_with_include_and_limit
|
||||
assert_equal 5, Developer.find(:all, :include => 'projects', :order => 'developers_projects.joined_on DESC', :limit => 5).size
|
||||
end
|
||||
|
||||
def test_eager_loading_with_order_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :joins => :comments, :include => :author, :order => 'comments.id DESC')
|
||||
end
|
||||
assert_equal posts(:eager_other), posts[0]
|
||||
assert_equal authors(:mary), assert_no_queries { posts[0].author}
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :include => :author, :joins => {:taggings => :tag}, :conditions => "tags.name = 'General'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal posts(:welcome, :thinking), posts
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :include => :author, :joins => {:taggings => {:tag => :taggings}}, :conditions => "taggings_tags.super_tag_id=2", :order => 'posts.id')
|
||||
end
|
||||
assert_equal posts(:welcome, :thinking), posts
|
||||
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_string_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => "INNER JOIN comments on comments.post_id = posts.id", :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
|
||||
end
|
||||
assert_equal [posts(:welcome)], posts
|
||||
assert_equal authors(:david), assert_no_queries { posts[0].author}
|
||||
|
||||
end
|
||||
|
||||
def test_eager_loading_with_select_on_joined_table_preloads
|
||||
posts = assert_queries(2) do
|
||||
Post.find(:all, :select => 'posts.*, authors.name as author_name', :include => :comments, :joins => :author, :order => 'posts.id')
|
||||
end
|
||||
assert_equal 'David', posts[0].author_name
|
||||
assert_equal posts(:welcome).comments, assert_no_queries { posts[0].comments}
|
||||
end
|
||||
|
||||
def test_eager_loading_with_conditions_on_join_model_preloads
|
||||
authors = assert_queries(2) do
|
||||
Author.find(:all, :include => :author_address, :joins => :comments, :conditions => "posts.title like 'Welcome%'")
|
||||
end
|
||||
assert_equal authors(:david), authors[0]
|
||||
assert_equal author_addresses(:david_address), authors[0].author_address
|
||||
end
|
||||
|
||||
def test_preload_belongs_to_uses_exclusive_scope
|
||||
people = Person.males.find(:all, :include => :primary_contact)
|
||||
assert_not_equal people.length, 0
|
||||
people.each do |person|
|
||||
assert_no_queries {assert_not_nil person.primary_contact}
|
||||
assert_equal Person.find(person.id).primary_contact, person.primary_contact
|
||||
end
|
||||
end
|
||||
|
||||
def test_preload_has_many_uses_exclusive_scope
|
||||
people = Person.males.find :all, :include => :agents
|
||||
people.each do |person|
|
||||
assert_equal Person.find(person.id).agents, person.agents
|
||||
end
|
||||
end
|
||||
|
||||
def test_preload_has_many_using_primary_key
|
||||
expected = Firm.find(:first).clients_using_primary_key.to_a
|
||||
firm = Firm.find :first, :include => :clients_using_primary_key
|
||||
assert_no_queries do
|
||||
assert_equal expected, firm.clients_using_primary_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_has_many_using_primary_key
|
||||
expected = Firm.find(1).clients_using_primary_key.sort_by &:name
|
||||
firm = Firm.find 1, :include => :clients_using_primary_key, :order => 'clients_using_primary_keys_companies.name'
|
||||
assert_no_queries do
|
||||
assert_equal expected, firm.clients_using_primary_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_preload_has_one_using_primary_key
|
||||
expected = Firm.find(:first).account_using_primary_key
|
||||
firm = Firm.find :first, :include => :account_using_primary_key
|
||||
assert_no_queries do
|
||||
assert_equal expected, firm.account_using_primary_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_has_one_using_primary_key
|
||||
expected = Firm.find(1).account_using_primary_key
|
||||
firm = Firm.find(:all, :include => :account_using_primary_key, :order => 'accounts.id').detect {|f| f.id == 1}
|
||||
assert_no_queries do
|
||||
assert_equal expected, firm.account_using_primary_key
|
||||
end
|
||||
end
|
||||
|
||||
def test_preloading_empty_polymorphic_parent
|
||||
t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general))
|
||||
|
||||
assert_queries(2) { @tagging = Tagging.find(t.id, :include => :taggable) }
|
||||
assert_no_queries { assert ! @tagging.taggable }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/project'
|
||||
require 'models/developer'
|
||||
require 'models/company_in_module'
|
||||
|
||||
class AssociationsExtensionsTest < ActiveRecord::TestCase
|
||||
fixtures :projects, :developers, :developers_projects, :comments, :posts
|
||||
|
||||
def test_extension_on_has_many
|
||||
assert_equal comments(:more_greetings), posts(:welcome).comments.find_most_recent
|
||||
end
|
||||
|
||||
def test_extension_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects.find_most_recent
|
||||
end
|
||||
|
||||
def test_named_extension_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name.find_most_recent
|
||||
end
|
||||
|
||||
def test_named_two_extensions_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_twice.find_most_recent
|
||||
assert_equal projects(:active_record), developers(:david).projects_extended_by_name_twice.find_least_recent
|
||||
end
|
||||
|
||||
def test_named_extension_and_block_on_habtm
|
||||
assert_equal projects(:action_controller), developers(:david).projects_extended_by_name_and_block.find_most_recent
|
||||
assert_equal projects(:active_record), developers(:david).projects_extended_by_name_and_block.find_least_recent
|
||||
end
|
||||
|
||||
def test_marshalling_extensions
|
||||
david = developers(:david)
|
||||
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||
|
||||
david = Marshal.load(Marshal.dump(david))
|
||||
assert_equal projects(:action_controller), david.projects.find_most_recent
|
||||
end
|
||||
|
||||
def test_marshalling_named_extensions
|
||||
david = developers(:david)
|
||||
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||
|
||||
david = Marshal.load(Marshal.dump(david))
|
||||
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
|
||||
end
|
||||
|
||||
|
||||
def test_extension_name
|
||||
extension = Proc.new {}
|
||||
name = :association_name
|
||||
|
||||
assert_equal 'DeveloperAssociationNameAssociationExtension', Developer.send(:create_extension_modules, name, extension, []).first.name
|
||||
assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension',
|
||||
MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
|
||||
assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
|
||||
assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', MyApplication::Business::Developer.send(:create_extension_modules, name, extension, []).first.name
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
require 'cases/helper'
|
||||
|
||||
class MyReader < ActiveRecord::Base
|
||||
has_and_belongs_to_many :my_books
|
||||
end
|
||||
|
||||
class MyBook < ActiveRecord::Base
|
||||
has_and_belongs_to_many :my_readers
|
||||
end
|
||||
|
||||
class HabtmJoinTableTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
ActiveRecord::Base.connection.create_table :my_books, :force => true do |t|
|
||||
t.string :name
|
||||
end
|
||||
assert ActiveRecord::Base.connection.table_exists?(:my_books)
|
||||
|
||||
ActiveRecord::Base.connection.create_table :my_readers, :force => true do |t|
|
||||
t.string :name
|
||||
end
|
||||
assert ActiveRecord::Base.connection.table_exists?(:my_readers)
|
||||
|
||||
ActiveRecord::Base.connection.create_table :my_books_my_readers, :force => true do |t|
|
||||
t.integer :my_book_id
|
||||
t.integer :my_reader_id
|
||||
end
|
||||
assert ActiveRecord::Base.connection.table_exists?(:my_books_my_readers)
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.connection.drop_table :my_books
|
||||
ActiveRecord::Base.connection.drop_table :my_readers
|
||||
ActiveRecord::Base.connection.drop_table :my_books_my_readers
|
||||
end
|
||||
|
||||
uses_transaction :test_should_raise_exception_when_join_table_has_a_primary_key
|
||||
def test_should_raise_exception_when_join_table_has_a_primary_key
|
||||
if ActiveRecord::Base.connection.supports_primary_key?
|
||||
assert_raise ActiveRecord::ConfigurationError do
|
||||
jaime = MyReader.create(:name=>"Jaime")
|
||||
jaime.my_books << MyBook.create(:name=>'Great Expectations')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
uses_transaction :test_should_cache_result_of_primary_key_check
|
||||
def test_should_cache_result_of_primary_key_check
|
||||
if ActiveRecord::Base.connection.supports_primary_key?
|
||||
ActiveRecord::Base.connection.stubs(:primary_key).with('my_books_my_readers').returns(false).once
|
||||
weaz = MyReader.create(:name=>'Weaz')
|
||||
|
||||
weaz.my_books << MyBook.create(:name=>'Great Expectations')
|
||||
weaz.my_books << MyBook.create(:name=>'Greater Expectations')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,822 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/computer'
|
||||
require 'models/customer'
|
||||
require 'models/order'
|
||||
require 'models/categorization'
|
||||
require 'models/category'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/comment'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/parrot'
|
||||
require 'models/pirate'
|
||||
require 'models/treasure'
|
||||
require 'models/price_estimate'
|
||||
require 'models/club'
|
||||
require 'models/member'
|
||||
require 'models/membership'
|
||||
require 'models/sponsor'
|
||||
|
||||
class ProjectWithAfterCreateHook < ActiveRecord::Base
|
||||
set_table_name 'projects'
|
||||
has_and_belongs_to_many :developers,
|
||||
:class_name => "DeveloperForProjectWithAfterCreateHook",
|
||||
:join_table => "developers_projects",
|
||||
:foreign_key => "project_id",
|
||||
:association_foreign_key => "developer_id"
|
||||
|
||||
after_create :add_david
|
||||
|
||||
def add_david
|
||||
david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
|
||||
david.projects << self
|
||||
end
|
||||
end
|
||||
|
||||
class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
has_and_belongs_to_many :projects,
|
||||
:class_name => "ProjectWithAfterCreateHook",
|
||||
:join_table => "developers_projects",
|
||||
:association_foreign_key => "project_id",
|
||||
:foreign_key => "developer_id"
|
||||
end
|
||||
|
||||
class ProjectWithSymbolsForKeys < ActiveRecord::Base
|
||||
set_table_name 'projects'
|
||||
has_and_belongs_to_many :developers,
|
||||
:class_name => "DeveloperWithSymbolsForKeys",
|
||||
:join_table => :developers_projects,
|
||||
:foreign_key => :project_id,
|
||||
:association_foreign_key => "developer_id"
|
||||
end
|
||||
|
||||
class DeveloperWithSymbolsForKeys < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
has_and_belongs_to_many :projects,
|
||||
:class_name => "ProjectWithSymbolsForKeys",
|
||||
:join_table => :developers_projects,
|
||||
:association_foreign_key => :project_id,
|
||||
:foreign_key => "developer_id"
|
||||
end
|
||||
|
||||
class DeveloperWithCounterSQL < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
has_and_belongs_to_many :projects,
|
||||
:class_name => "DeveloperWithCounterSQL",
|
||||
:join_table => "developers_projects",
|
||||
:association_foreign_key => "project_id",
|
||||
:foreign_key => "developer_id",
|
||||
:counter_sql => 'SELECT COUNT(*) AS count_all FROM projects INNER JOIN developers_projects ON projects.id = developers_projects.project_id WHERE developers_projects.developer_id =#{id}'
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
|
||||
:parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
|
||||
|
||||
def test_has_and_belongs_to_many
|
||||
david = Developer.find(1)
|
||||
|
||||
assert !david.projects.empty?
|
||||
assert_equal 2, david.projects.size
|
||||
|
||||
active_record = Project.find(1)
|
||||
assert !active_record.developers.empty?
|
||||
assert_equal 3, active_record.developers.size
|
||||
assert active_record.developers.include?(david)
|
||||
end
|
||||
|
||||
def test_triple_equality
|
||||
assert !(Array === Developer.find(1).projects)
|
||||
assert Developer.find(1).projects === Array
|
||||
end
|
||||
|
||||
def test_adding_single
|
||||
jamis = Developer.find(2)
|
||||
jamis.projects.reload # causing the collection to load
|
||||
action_controller = Project.find(2)
|
||||
assert_equal 1, jamis.projects.size
|
||||
assert_equal 1, action_controller.developers.size
|
||||
|
||||
jamis.projects << action_controller
|
||||
|
||||
assert_equal 2, jamis.projects.size
|
||||
assert_equal 2, jamis.projects(true).size
|
||||
assert_equal 2, action_controller.developers(true).size
|
||||
end
|
||||
|
||||
def test_adding_type_mismatch
|
||||
jamis = Developer.find(2)
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << nil }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { jamis.projects << 1 }
|
||||
end
|
||||
|
||||
def test_adding_from_the_project
|
||||
jamis = Developer.find(2)
|
||||
action_controller = Project.find(2)
|
||||
action_controller.developers.reload
|
||||
assert_equal 1, jamis.projects.size
|
||||
assert_equal 1, action_controller.developers.size
|
||||
|
||||
action_controller.developers << jamis
|
||||
|
||||
assert_equal 2, jamis.projects(true).size
|
||||
assert_equal 2, action_controller.developers.size
|
||||
assert_equal 2, action_controller.developers(true).size
|
||||
end
|
||||
|
||||
def test_adding_from_the_project_fixed_timestamp
|
||||
jamis = Developer.find(2)
|
||||
action_controller = Project.find(2)
|
||||
action_controller.developers.reload
|
||||
assert_equal 1, jamis.projects.size
|
||||
assert_equal 1, action_controller.developers.size
|
||||
updated_at = jamis.updated_at
|
||||
|
||||
action_controller.developers << jamis
|
||||
|
||||
assert_equal updated_at, jamis.updated_at
|
||||
assert_equal 2, jamis.projects(true).size
|
||||
assert_equal 2, action_controller.developers.size
|
||||
assert_equal 2, action_controller.developers(true).size
|
||||
end
|
||||
|
||||
def test_adding_multiple
|
||||
aredridel = Developer.new("name" => "Aredridel")
|
||||
aredridel.save
|
||||
aredridel.projects.reload
|
||||
aredridel.projects.push(Project.find(1), Project.find(2))
|
||||
assert_equal 2, aredridel.projects.size
|
||||
assert_equal 2, aredridel.projects(true).size
|
||||
end
|
||||
|
||||
def test_adding_a_collection
|
||||
aredridel = Developer.new("name" => "Aredridel")
|
||||
aredridel.save
|
||||
aredridel.projects.reload
|
||||
aredridel.projects.concat([Project.find(1), Project.find(2)])
|
||||
assert_equal 2, aredridel.projects.size
|
||||
assert_equal 2, aredridel.projects(true).size
|
||||
end
|
||||
|
||||
def test_adding_uses_default_values_on_join_table
|
||||
ac = projects(:action_controller)
|
||||
assert !developers(:jamis).projects.include?(ac)
|
||||
developers(:jamis).projects << ac
|
||||
|
||||
assert developers(:jamis, :reload).projects.include?(ac)
|
||||
project = developers(:jamis).projects.detect { |p| p == ac }
|
||||
assert_equal 1, project.access_level.to_i
|
||||
end
|
||||
|
||||
def test_habtm_attribute_access_and_respond_to
|
||||
project = developers(:jamis).projects[0]
|
||||
assert project.has_attribute?("name")
|
||||
assert project.has_attribute?("joined_on")
|
||||
assert project.has_attribute?("access_level")
|
||||
assert project.respond_to?("name")
|
||||
assert project.respond_to?("name=")
|
||||
assert project.respond_to?("name?")
|
||||
assert project.respond_to?("joined_on")
|
||||
# given that the 'join attribute' won't be persisted, I don't
|
||||
# think we should define the mutators
|
||||
#assert project.respond_to?("joined_on=")
|
||||
assert project.respond_to?("joined_on?")
|
||||
assert project.respond_to?("access_level")
|
||||
#assert project.respond_to?("access_level=")
|
||||
assert project.respond_to?("access_level?")
|
||||
end
|
||||
|
||||
def test_habtm_adding_before_save
|
||||
no_of_devels = Developer.count
|
||||
no_of_projects = Project.count
|
||||
aredridel = Developer.new("name" => "Aredridel")
|
||||
aredridel.projects.concat([Project.find(1), p = Project.new("name" => "Projekt")])
|
||||
assert aredridel.new_record?
|
||||
assert p.new_record?
|
||||
assert aredridel.save
|
||||
assert !aredridel.new_record?
|
||||
assert_equal no_of_devels+1, Developer.count
|
||||
assert_equal no_of_projects+1, Project.count
|
||||
assert_equal 2, aredridel.projects.size
|
||||
assert_equal 2, aredridel.projects(true).size
|
||||
end
|
||||
|
||||
def test_habtm_saving_multiple_relationships
|
||||
new_project = Project.new("name" => "Grimetime")
|
||||
amount_of_developers = 4
|
||||
developers = (0...amount_of_developers).collect {|i| Developer.create(:name => "JME #{i}") }.reverse
|
||||
|
||||
new_project.developer_ids = [developers[0].id, developers[1].id]
|
||||
new_project.developers_with_callback_ids = [developers[2].id, developers[3].id]
|
||||
assert new_project.save
|
||||
|
||||
new_project.reload
|
||||
assert_equal amount_of_developers, new_project.developers.size
|
||||
assert_equal developers, new_project.developers
|
||||
end
|
||||
|
||||
def test_habtm_unique_order_preserved
|
||||
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
|
||||
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
|
||||
end
|
||||
|
||||
def test_build
|
||||
devel = Developer.find(1)
|
||||
proj = assert_no_queries { devel.projects.build("name" => "Projekt") }
|
||||
assert !devel.projects.loaded?
|
||||
|
||||
assert_equal devel.projects.last, proj
|
||||
assert devel.projects.loaded?
|
||||
|
||||
assert proj.new_record?
|
||||
devel.save
|
||||
assert !proj.new_record?
|
||||
assert_equal devel.projects.last, proj
|
||||
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
|
||||
end
|
||||
|
||||
def test_build_by_new_record
|
||||
devel = Developer.new(:name => "Marcel", :salary => 75000)
|
||||
proj1 = devel.projects.build(:name => "Make bed")
|
||||
proj2 = devel.projects.build(:name => "Lie in it")
|
||||
assert_equal devel.projects.last, proj2
|
||||
assert proj2.new_record?
|
||||
devel.save
|
||||
assert !devel.new_record?
|
||||
assert !proj2.new_record?
|
||||
assert_equal devel.projects.last, proj2
|
||||
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
|
||||
end
|
||||
|
||||
def test_create
|
||||
devel = Developer.find(1)
|
||||
proj = devel.projects.create("name" => "Projekt")
|
||||
assert !devel.projects.loaded?
|
||||
|
||||
assert_equal devel.projects.last, proj
|
||||
assert !devel.projects.loaded?
|
||||
|
||||
assert !proj.new_record?
|
||||
assert_equal Developer.find(1).projects.sort_by(&:id).last, proj # prove join table is updated
|
||||
end
|
||||
|
||||
def test_create_by_new_record
|
||||
devel = Developer.new(:name => "Marcel", :salary => 75000)
|
||||
proj1 = devel.projects.build(:name => "Make bed")
|
||||
proj2 = devel.projects.build(:name => "Lie in it")
|
||||
assert_equal devel.projects.last, proj2
|
||||
assert proj2.new_record?
|
||||
devel.save
|
||||
assert !devel.new_record?
|
||||
assert !proj2.new_record?
|
||||
assert_equal devel.projects.last, proj2
|
||||
assert_equal Developer.find_by_name("Marcel").projects.last, proj2 # prove join table is updated
|
||||
end
|
||||
|
||||
def test_creation_respects_hash_condition
|
||||
post = categories(:general).post_with_conditions.build(:body => '')
|
||||
|
||||
assert post.save
|
||||
assert_equal 'Yet Another Testing Title', post.title
|
||||
|
||||
another_post = categories(:general).post_with_conditions.create(:body => '')
|
||||
|
||||
assert !another_post.new_record?
|
||||
assert_equal 'Yet Another Testing Title', another_post.title
|
||||
end
|
||||
|
||||
def test_uniq_after_the_fact
|
||||
dev = developers(:jamis)
|
||||
dev.projects << projects(:active_record)
|
||||
dev.projects << projects(:active_record)
|
||||
|
||||
assert_equal 3, dev.projects.size
|
||||
assert_equal 1, dev.projects.uniq.size
|
||||
end
|
||||
|
||||
def test_uniq_before_the_fact
|
||||
projects(:active_record).developers << developers(:jamis)
|
||||
projects(:active_record).developers << developers(:david)
|
||||
assert_equal 3, projects(:active_record, :reload).developers.size
|
||||
end
|
||||
|
||||
def test_uniq_option_prevents_duplicate_push
|
||||
project = projects(:active_record)
|
||||
project.developers << developers(:jamis)
|
||||
project.developers << developers(:david)
|
||||
assert_equal 3, project.developers.size
|
||||
|
||||
project.developers << developers(:david)
|
||||
project.developers << developers(:jamis)
|
||||
assert_equal 3, project.developers.size
|
||||
end
|
||||
|
||||
def test_deleting
|
||||
david = Developer.find(1)
|
||||
active_record = Project.find(1)
|
||||
david.projects.reload
|
||||
assert_equal 2, david.projects.size
|
||||
assert_equal 3, active_record.developers.size
|
||||
|
||||
david.projects.delete(active_record)
|
||||
|
||||
assert_equal 1, david.projects.size
|
||||
assert_equal 1, david.projects(true).size
|
||||
assert_equal 2, active_record.developers(true).size
|
||||
end
|
||||
|
||||
def test_deleting_array
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
david.projects.delete(Project.find(:all))
|
||||
assert_equal 0, david.projects.size
|
||||
assert_equal 0, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_deleting_with_sql
|
||||
david = Developer.find(1)
|
||||
active_record = Project.find(1)
|
||||
active_record.developers.reload
|
||||
assert_equal 3, active_record.developers_by_sql.size
|
||||
|
||||
active_record.developers_by_sql.delete(david)
|
||||
assert_equal 2, active_record.developers_by_sql(true).size
|
||||
end
|
||||
|
||||
def test_deleting_array_with_sql
|
||||
active_record = Project.find(1)
|
||||
active_record.developers.reload
|
||||
assert_equal 3, active_record.developers_by_sql.size
|
||||
|
||||
active_record.developers_by_sql.delete(Developer.find(:all))
|
||||
assert_equal 0, active_record.developers_by_sql(true).size
|
||||
end
|
||||
|
||||
def test_deleting_all
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
david.projects.clear
|
||||
assert_equal 0, david.projects.size
|
||||
assert_equal 0, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_removing_associations_on_destroy
|
||||
david = DeveloperWithBeforeDestroyRaise.find(1)
|
||||
assert !david.projects.empty?
|
||||
assert_nothing_raised { david.destroy }
|
||||
assert david.projects.empty?
|
||||
assert DeveloperWithBeforeDestroyRaise.connection.select_all("SELECT * FROM developers_projects WHERE developer_id = 1").empty?
|
||||
end
|
||||
|
||||
def test_additional_columns_from_join_table
|
||||
assert_date_from_db Date.new(2004, 10, 10), Developer.find(1).projects.first.joined_on.to_date
|
||||
end
|
||||
|
||||
def test_destroying
|
||||
david = Developer.find(1)
|
||||
active_record = Project.find(1)
|
||||
david.projects.reload
|
||||
assert_equal 2, david.projects.size
|
||||
assert_equal 3, active_record.developers.size
|
||||
|
||||
assert_difference "Project.count", -1 do
|
||||
david.projects.destroy(active_record)
|
||||
end
|
||||
|
||||
assert_equal 1, david.reload.projects.size
|
||||
assert_equal 1, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_destroying_array
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
|
||||
assert_difference "Project.count", -Project.count do
|
||||
david.projects.destroy(Project.find(:all))
|
||||
end
|
||||
|
||||
assert_equal 0, david.reload.projects.size
|
||||
assert_equal 0, david.projects(true).size
|
||||
end
|
||||
|
||||
def test_destroy_all
|
||||
david = Developer.find(1)
|
||||
david.projects.reload
|
||||
assert !david.projects.empty?
|
||||
david.projects.destroy_all
|
||||
assert david.projects.empty?
|
||||
assert david.projects(true).empty?
|
||||
end
|
||||
|
||||
def test_deprecated_push_with_attributes_was_removed
|
||||
jamis = developers(:jamis)
|
||||
assert_raise(NoMethodError) do
|
||||
jamis.projects.push_with_attributes(projects(:action_controller), :joined_on => Date.today)
|
||||
end
|
||||
end
|
||||
|
||||
def test_associations_with_conditions
|
||||
assert_equal 3, projects(:active_record).developers.size
|
||||
assert_equal 1, projects(:active_record).developers_named_david.size
|
||||
assert_equal 1, projects(:active_record).developers_named_david_with_hash_conditions.size
|
||||
|
||||
assert_equal developers(:david), projects(:active_record).developers_named_david.find(developers(:david).id)
|
||||
assert_equal developers(:david), projects(:active_record).developers_named_david_with_hash_conditions.find(developers(:david).id)
|
||||
assert_equal developers(:david), projects(:active_record).salaried_developers.find(developers(:david).id)
|
||||
|
||||
projects(:active_record).developers_named_david.clear
|
||||
assert_equal 2, projects(:active_record, :reload).developers.size
|
||||
end
|
||||
|
||||
def test_find_in_association
|
||||
# Using sql
|
||||
assert_equal developers(:david), projects(:active_record).developers.find(developers(:david).id), "SQL find"
|
||||
|
||||
# Using ruby
|
||||
active_record = projects(:active_record)
|
||||
active_record.developers.reload
|
||||
assert_equal developers(:david), active_record.developers.find(developers(:david).id), "Ruby find"
|
||||
end
|
||||
|
||||
def test_include_uses_array_include_after_loaded
|
||||
project = projects(:active_record)
|
||||
project.developers.class # force load target
|
||||
|
||||
developer = project.developers.first
|
||||
|
||||
assert_no_queries do
|
||||
assert project.developers.loaded?
|
||||
assert project.developers.include?(developer)
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_checks_if_record_exists_if_target_not_loaded
|
||||
project = projects(:active_record)
|
||||
developer = project.developers.first
|
||||
|
||||
project.reload
|
||||
assert ! project.developers.loaded?
|
||||
assert_queries(1) do
|
||||
assert project.developers.include?(developer)
|
||||
end
|
||||
assert ! project.developers.loaded?
|
||||
end
|
||||
|
||||
def test_include_returns_false_for_non_matching_record_to_verify_scoping
|
||||
project = projects(:active_record)
|
||||
developer = Developer.create :name => "Bryan", :salary => 50_000
|
||||
|
||||
assert ! project.developers.loaded?
|
||||
assert ! project.developers.include?(developer)
|
||||
end
|
||||
|
||||
def test_find_in_association_with_custom_finder_sql
|
||||
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id), "SQL find"
|
||||
|
||||
active_record = projects(:active_record)
|
||||
active_record.developers_with_finder_sql.reload
|
||||
assert_equal developers(:david), active_record.developers_with_finder_sql.find(developers(:david).id), "Ruby find"
|
||||
end
|
||||
|
||||
def test_find_in_association_with_custom_finder_sql_and_multiple_interpolations
|
||||
# interpolate once:
|
||||
assert_equal [developers(:david), developers(:jamis), developers(:poor_jamis)], projects(:active_record).developers_with_finder_sql, "first interpolation"
|
||||
# interpolate again, for a different project id
|
||||
assert_equal [developers(:david)], projects(:action_controller).developers_with_finder_sql, "second interpolation"
|
||||
end
|
||||
|
||||
def test_find_in_association_with_custom_finder_sql_and_string_id
|
||||
assert_equal developers(:david), projects(:active_record).developers_with_finder_sql.find(developers(:david).id.to_s), "SQL find"
|
||||
end
|
||||
|
||||
def test_find_with_merged_options
|
||||
assert_equal 1, projects(:active_record).limited_developers.size
|
||||
assert_equal 1, projects(:active_record).limited_developers.find(:all).size
|
||||
assert_equal 3, projects(:active_record).limited_developers.find(:all, :limit => nil).size
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_order
|
||||
# Developers are ordered 'name DESC, id DESC'
|
||||
low_id_jamis = developers(:jamis)
|
||||
middle_id_jamis = developers(:poor_jamis)
|
||||
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
|
||||
|
||||
assert_equal high_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'")
|
||||
assert_equal high_id_jamis, projects(:active_record).developers.find_by_name('Jamis')
|
||||
end
|
||||
|
||||
def test_dynamic_find_order_should_override_association_order
|
||||
# Developers are ordered 'name DESC, id DESC'
|
||||
low_id_jamis = developers(:jamis)
|
||||
middle_id_jamis = developers(:poor_jamis)
|
||||
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
|
||||
|
||||
assert_equal low_id_jamis, projects(:active_record).developers.find(:first, :conditions => "name = 'Jamis'", :order => 'id')
|
||||
assert_equal low_id_jamis, projects(:active_record).developers.find_by_name('Jamis', :order => 'id')
|
||||
end
|
||||
|
||||
def test_dynamic_find_all_should_respect_association_order
|
||||
# Developers are ordered 'name DESC, id DESC'
|
||||
low_id_jamis = developers(:jamis)
|
||||
middle_id_jamis = developers(:poor_jamis)
|
||||
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
|
||||
|
||||
assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'")
|
||||
assert_equal [high_id_jamis, middle_id_jamis, low_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis')
|
||||
end
|
||||
|
||||
def test_dynamic_find_all_order_should_override_association_order
|
||||
# Developers are ordered 'name DESC, id DESC'
|
||||
low_id_jamis = developers(:jamis)
|
||||
middle_id_jamis = developers(:poor_jamis)
|
||||
high_id_jamis = projects(:active_record).developers.create(:name => 'Jamis')
|
||||
|
||||
assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find(:all, :conditions => "name = 'Jamis'", :order => 'id')
|
||||
assert_equal [low_id_jamis, middle_id_jamis, high_id_jamis], projects(:active_record).developers.find_all_by_name('Jamis', :order => 'id')
|
||||
end
|
||||
|
||||
def test_dynamic_find_all_should_respect_association_limit
|
||||
assert_equal 1, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'").length
|
||||
assert_equal 1, projects(:active_record).limited_developers.find_all_by_name('Jamis').length
|
||||
end
|
||||
|
||||
def test_dynamic_find_all_order_should_override_association_limit
|
||||
assert_equal 2, projects(:active_record).limited_developers.find(:all, :conditions => "name = 'Jamis'", :limit => 9_000).length
|
||||
assert_equal 2, projects(:active_record).limited_developers.find_all_by_name('Jamis', :limit => 9_000).length
|
||||
end
|
||||
|
||||
def test_dynamic_find_all_should_respect_readonly_access
|
||||
projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?}
|
||||
projects(:active_record).readonly_developers.each { |d| d.readonly? }
|
||||
end
|
||||
|
||||
def test_new_with_values_in_collection
|
||||
jamis = DeveloperForProjectWithAfterCreateHook.find_by_name('Jamis')
|
||||
david = DeveloperForProjectWithAfterCreateHook.find_by_name('David')
|
||||
project = ProjectWithAfterCreateHook.new(:name => "Cooking with Bertie")
|
||||
project.developers << jamis
|
||||
project.save!
|
||||
project.reload
|
||||
|
||||
assert project.developers.include?(jamis)
|
||||
assert project.developers.include?(david)
|
||||
end
|
||||
|
||||
def test_find_in_association_with_options
|
||||
developers = projects(:active_record).developers.find(:all)
|
||||
assert_equal 3, developers.size
|
||||
|
||||
assert_equal developers(:poor_jamis), projects(:active_record).developers.find(:first, :conditions => "salary < 10000")
|
||||
assert_equal developers(:jamis), projects(:active_record).developers.find(:first, :order => "salary DESC")
|
||||
end
|
||||
|
||||
def test_replace_with_less
|
||||
david = developers(:david)
|
||||
david.projects = [projects(:action_controller)]
|
||||
assert david.save
|
||||
assert_equal 1, david.projects.length
|
||||
end
|
||||
|
||||
def test_replace_with_new
|
||||
david = developers(:david)
|
||||
david.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
|
||||
david.save
|
||||
assert_equal 2, david.projects.length
|
||||
assert !david.projects.include?(projects(:active_record))
|
||||
end
|
||||
|
||||
def test_replace_on_new_object
|
||||
new_developer = Developer.new("name" => "Matz")
|
||||
new_developer.projects = [projects(:action_controller), Project.new("name" => "ActionWebSearch")]
|
||||
new_developer.save
|
||||
assert_equal 2, new_developer.projects.length
|
||||
end
|
||||
|
||||
def test_consider_type
|
||||
developer = Developer.find(:first)
|
||||
special_project = SpecialProject.create("name" => "Special Project")
|
||||
|
||||
other_project = developer.projects.first
|
||||
developer.special_projects << special_project
|
||||
developer.reload
|
||||
|
||||
assert developer.projects.include?(special_project)
|
||||
assert developer.special_projects.include?(special_project)
|
||||
assert !developer.special_projects.include?(other_project)
|
||||
end
|
||||
|
||||
def test_update_attributes_after_push_without_duplicate_join_table_rows
|
||||
developer = Developer.new("name" => "Kano")
|
||||
project = SpecialProject.create("name" => "Special Project")
|
||||
assert developer.save
|
||||
developer.projects << project
|
||||
developer.update_attribute("name", "Bruza")
|
||||
assert_equal 1, Developer.connection.select_value(<<-end_sql).to_i
|
||||
SELECT count(*) FROM developers_projects
|
||||
WHERE project_id = #{project.id}
|
||||
AND developer_id = #{developer.id}
|
||||
end_sql
|
||||
end
|
||||
|
||||
def test_updating_attributes_on_non_rich_associations
|
||||
welcome = categories(:technology).posts.first
|
||||
welcome.title = "Something else"
|
||||
assert welcome.save!
|
||||
end
|
||||
|
||||
def test_habtm_respects_select
|
||||
categories(:technology).select_testing_posts(true).each do |o|
|
||||
assert_respond_to o, :correctness_marker
|
||||
end
|
||||
assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
|
||||
end
|
||||
|
||||
def test_updating_attributes_on_rich_associations
|
||||
david = projects(:action_controller).developers.first
|
||||
david.name = "DHH"
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { david.save! }
|
||||
end
|
||||
|
||||
def test_updating_attributes_on_rich_associations_with_limited_find_from_reflection
|
||||
david = projects(:action_controller).selected_developers.first
|
||||
david.name = "DHH"
|
||||
assert_nothing_raised { david.save! }
|
||||
end
|
||||
|
||||
|
||||
def test_updating_attributes_on_rich_associations_with_limited_find
|
||||
david = projects(:action_controller).developers.find(:all, :select => "developers.*").first
|
||||
david.name = "DHH"
|
||||
assert david.save!
|
||||
end
|
||||
|
||||
def test_join_table_alias
|
||||
assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
|
||||
end
|
||||
|
||||
def test_join_with_group
|
||||
group = Developer.columns.inject([]) do |g, c|
|
||||
g << "developers.#{c.name}"
|
||||
g << "developers_projects_2.#{c.name}"
|
||||
end
|
||||
Project.columns.each { |c| group << "projects.#{c.name}" }
|
||||
|
||||
assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
|
||||
end
|
||||
|
||||
def test_find_grouped
|
||||
all_posts_from_category1 = Post.find(:all, :conditions => "category_id = 1", :joins => :categories)
|
||||
grouped_posts_of_category1 = Post.find(:all, :conditions => "category_id = 1", :group => "author_id", :select => 'count(posts.id) as posts_count', :joins => :categories)
|
||||
assert_equal 4, all_posts_from_category1.size
|
||||
assert_equal 1, grouped_posts_of_category1.size
|
||||
end
|
||||
|
||||
def test_find_scoped_grouped
|
||||
assert_equal 4, categories(:general).posts_gruoped_by_title.size
|
||||
assert_equal 1, categories(:technology).posts_gruoped_by_title.size
|
||||
end
|
||||
|
||||
def test_find_scoped_grouped_having
|
||||
assert_equal 2, projects(:active_record).well_payed_salary_groups.size
|
||||
assert projects(:active_record).well_payed_salary_groups.all? { |g| g.salary > 10000 }
|
||||
end
|
||||
|
||||
def test_get_ids
|
||||
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developers(:david).project_ids.sort
|
||||
assert_equal [projects(:active_record).id], developers(:jamis).project_ids
|
||||
end
|
||||
|
||||
def test_get_ids_for_loaded_associations
|
||||
developer = developers(:david)
|
||||
developer.projects(true)
|
||||
assert_queries(0) do
|
||||
developer.project_ids
|
||||
developer.project_ids
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_ids_for_unloaded_associations_does_not_load_them
|
||||
developer = developers(:david)
|
||||
assert !developer.projects.loaded?
|
||||
assert_equal projects(:active_record, :action_controller).map(&:id).sort, developer.project_ids.sort
|
||||
assert !developer.projects.loaded?
|
||||
end
|
||||
|
||||
def test_assign_ids
|
||||
developer = Developer.new("name" => "Joe")
|
||||
developer.project_ids = projects(:active_record, :action_controller).map(&:id)
|
||||
developer.save
|
||||
developer.reload
|
||||
assert_equal 2, developer.projects.length
|
||||
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
|
||||
end
|
||||
|
||||
def test_assign_ids_ignoring_blanks
|
||||
developer = Developer.new("name" => "Joe")
|
||||
developer.project_ids = [projects(:active_record).id, nil, projects(:action_controller).id, '']
|
||||
developer.save
|
||||
developer.reload
|
||||
assert_equal 2, developer.projects.length
|
||||
assert_equal [projects(:active_record), projects(:action_controller)].map(&:id).sort, developer.project_ids.sort
|
||||
end
|
||||
|
||||
def test_select_limited_ids_list
|
||||
# Set timestamps
|
||||
Developer.transaction do
|
||||
Developer.find(:all, :order => 'id').each_with_index do |record, i|
|
||||
record.update_attributes(:created_at => 5.years.ago + (i * 5.minutes))
|
||||
end
|
||||
end
|
||||
|
||||
join_base = ActiveRecord::Associations::ClassMethods::JoinDependency::JoinBase.new(Project)
|
||||
join_dep = ActiveRecord::Associations::ClassMethods::JoinDependency.new(join_base, :developers, nil)
|
||||
projects = Project.send(:select_limited_ids_list, {:order => 'developers.created_at'}, join_dep)
|
||||
assert !projects.include?("'"), projects
|
||||
assert_equal %w(1 2), projects.scan(/\d/).sort
|
||||
end
|
||||
|
||||
def test_scoped_find_on_through_association_doesnt_return_read_only_records
|
||||
tag = Post.find(1).tags.find_by_name("General")
|
||||
|
||||
assert_nothing_raised do
|
||||
tag.save!
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_polymorphic_has_manys_works
|
||||
assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
|
||||
end
|
||||
|
||||
def test_symbols_as_keys
|
||||
developer = DeveloperWithSymbolsForKeys.new(:name => 'David')
|
||||
project = ProjectWithSymbolsForKeys.new(:name => 'Rails Testing')
|
||||
project.developers << developer
|
||||
project.save!
|
||||
|
||||
assert_equal 1, project.developers.size
|
||||
assert_equal 1, developer.projects.size
|
||||
assert_equal developer, project.developers.find(:first)
|
||||
assert_equal project, developer.projects.find(:first)
|
||||
end
|
||||
|
||||
def test_self_referential_habtm_without_foreign_key_set_should_raise_exception
|
||||
assert_raise(ActiveRecord::HasAndBelongsToManyAssociationForeignKeyNeeded) {
|
||||
Member.class_eval do
|
||||
has_and_belongs_to_many :friends, :class_name => "Member", :join_table => "member_friends"
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_include
|
||||
# SQL error in sort clause if :include is not included
|
||||
# due to Unknown column 'authors.id'
|
||||
assert Category.find(1).posts_with_authors_sorted_by_author_id.find_by_title('Welcome to the weblog')
|
||||
end
|
||||
|
||||
def test_counting_on_habtm_association_and_not_array
|
||||
david = Developer.find(1)
|
||||
# Extra parameter just to make sure we aren't falling back to
|
||||
# Array#count in Ruby >=1.8.7, which would raise an ArgumentError
|
||||
assert_nothing_raised { david.projects.count(:all, :conditions => '1=1') }
|
||||
end
|
||||
|
||||
def test_count
|
||||
david = Developer.find(1)
|
||||
assert_equal 2, david.projects.count
|
||||
end
|
||||
|
||||
def test_count_with_counter_sql
|
||||
developer = DeveloperWithCounterSQL.create(:name => 'tekin')
|
||||
developer.project_ids = [projects(:active_record).id]
|
||||
developer.save
|
||||
developer.reload
|
||||
assert_equal 1, developer.projects.count
|
||||
end
|
||||
|
||||
def test_association_proxy_transaction_method_starts_transaction_in_association_class
|
||||
Post.expects(:transaction)
|
||||
Category.find(:first).posts.transaction do
|
||||
# nothing
|
||||
end
|
||||
end
|
||||
|
||||
def test_caching_of_columns
|
||||
david = Developer.find(1)
|
||||
# clear cache possibly created by other tests
|
||||
david.projects.reset_column_information
|
||||
assert_queries(0) { david.projects.columns; david.projects.columns }
|
||||
# and again to verify that reset_column_information clears the cache correctly
|
||||
david.projects.reset_column_information
|
||||
assert_queries(0) { david.projects.columns; david.projects.columns }
|
||||
end
|
||||
|
||||
end
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,346 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/person'
|
||||
require 'models/reference'
|
||||
require 'models/job'
|
||||
require 'models/reader'
|
||||
require 'models/comment'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/author'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/toy'
|
||||
require 'models/contract'
|
||||
require 'models/company'
|
||||
require 'models/developer'
|
||||
|
||||
class HasManyThroughAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :readers, :people, :comments, :authors, :owners, :pets, :toys,
|
||||
:companies
|
||||
|
||||
def test_associate_existing
|
||||
assert_queries(2) { posts(:thinking);people(:david) }
|
||||
|
||||
posts(:thinking).people
|
||||
|
||||
assert_queries(1) do
|
||||
posts(:thinking).people << people(:david)
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
assert posts(:thinking).people.include?(people(:david))
|
||||
end
|
||||
|
||||
assert posts(:thinking).reload.people(true).include?(people(:david))
|
||||
end
|
||||
|
||||
def test_associating_new
|
||||
assert_queries(1) { posts(:thinking) }
|
||||
new_person = nil # so block binding catches it
|
||||
|
||||
assert_queries(0) do
|
||||
new_person = Person.new :first_name => 'bob'
|
||||
end
|
||||
|
||||
# Associating new records always saves them
|
||||
# Thus, 1 query for the new person record, 1 query for the new join table record
|
||||
assert_queries(2) do
|
||||
posts(:thinking).people << new_person
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
assert posts(:thinking).people.include?(new_person)
|
||||
end
|
||||
|
||||
assert posts(:thinking).reload.people(true).include?(new_person)
|
||||
end
|
||||
|
||||
def test_associate_new_by_building
|
||||
assert_queries(1) { posts(:thinking) }
|
||||
|
||||
assert_queries(0) do
|
||||
posts(:thinking).people.build(:first_name=>"Bob")
|
||||
posts(:thinking).people.new(:first_name=>"Ted")
|
||||
end
|
||||
|
||||
# Should only need to load the association once
|
||||
assert_queries(1) do
|
||||
assert posts(:thinking).people.collect(&:first_name).include?("Bob")
|
||||
assert posts(:thinking).people.collect(&:first_name).include?("Ted")
|
||||
end
|
||||
|
||||
# 2 queries for each new record (1 to save the record itself, 1 for the join model)
|
||||
# * 2 new records = 4
|
||||
# + 1 query to save the actual post = 5
|
||||
assert_queries(5) do
|
||||
posts(:thinking).body += '-changed'
|
||||
posts(:thinking).save
|
||||
end
|
||||
|
||||
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
|
||||
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
|
||||
end
|
||||
|
||||
def test_delete_association
|
||||
assert_queries(2){posts(:welcome);people(:michael); }
|
||||
|
||||
assert_queries(1) do
|
||||
posts(:welcome).people.delete(people(:michael))
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
assert posts(:welcome).people.empty?
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people(true).empty?
|
||||
end
|
||||
|
||||
def test_destroy_association
|
||||
assert_difference "Person.count", -1 do
|
||||
posts(:welcome).people.destroy(people(:michael))
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people.empty?
|
||||
assert posts(:welcome).people(true).empty?
|
||||
end
|
||||
|
||||
def test_destroy_all
|
||||
assert_difference "Person.count", -1 do
|
||||
posts(:welcome).people.destroy_all
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people.empty?
|
||||
assert posts(:welcome).people(true).empty?
|
||||
end
|
||||
|
||||
def test_replace_association
|
||||
assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
|
||||
|
||||
# 1 query to delete the existing reader (michael)
|
||||
# 1 query to associate the new reader (david)
|
||||
assert_queries(2) do
|
||||
posts(:welcome).people = [people(:david)]
|
||||
end
|
||||
|
||||
assert_queries(0){
|
||||
assert posts(:welcome).people.include?(people(:david))
|
||||
assert !posts(:welcome).people.include?(people(:michael))
|
||||
}
|
||||
|
||||
assert posts(:welcome).reload.people(true).include?(people(:david))
|
||||
assert !posts(:welcome).reload.people(true).include?(people(:michael))
|
||||
end
|
||||
|
||||
def test_replace_order_is_preserved
|
||||
posts(:welcome).people.clear
|
||||
posts(:welcome).people = [people(:david), people(:michael)]
|
||||
assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
|
||||
|
||||
# Test the inverse order in case the first success was a coincidence
|
||||
posts(:welcome).people.clear
|
||||
posts(:welcome).people = [people(:michael), people(:david)]
|
||||
assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
|
||||
end
|
||||
|
||||
def test_replace_by_id_order_is_preserved
|
||||
posts(:welcome).people.clear
|
||||
posts(:welcome).person_ids = [people(:david).id, people(:michael).id]
|
||||
assert_equal [people(:david).id, people(:michael).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
|
||||
|
||||
# Test the inverse order in case the first success was a coincidence
|
||||
posts(:welcome).people.clear
|
||||
posts(:welcome).person_ids = [people(:michael).id, people(:david).id]
|
||||
assert_equal [people(:michael).id, people(:david).id], posts(:welcome).readers.all(:order => 'id').map(&:person_id)
|
||||
end
|
||||
|
||||
def test_associate_with_create
|
||||
assert_queries(1) { posts(:thinking) }
|
||||
|
||||
# 1 query for the new record, 1 for the join table record
|
||||
# No need to update the actual collection yet!
|
||||
assert_queries(2) do
|
||||
posts(:thinking).people.create(:first_name=>"Jeb")
|
||||
end
|
||||
|
||||
# *Now* we actually need the collection so it's loaded
|
||||
assert_queries(1) do
|
||||
assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
|
||||
end
|
||||
|
||||
assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
|
||||
end
|
||||
|
||||
def test_associate_with_create_and_no_options
|
||||
peeps = posts(:thinking).people.count
|
||||
posts(:thinking).people.create(:first_name => 'foo')
|
||||
assert_equal peeps + 1, posts(:thinking).people.count
|
||||
end
|
||||
|
||||
def test_associate_with_create_exclamation_and_no_options
|
||||
peeps = posts(:thinking).people.count
|
||||
posts(:thinking).people.create!(:first_name => 'foo')
|
||||
assert_equal peeps + 1, posts(:thinking).people.count
|
||||
end
|
||||
|
||||
def test_associate_with_create_and_invalid_options
|
||||
peeps = companies(:first_firm).developers.count
|
||||
assert_nothing_raised { companies(:first_firm).developers.create(:name => '0') }
|
||||
assert_equal peeps, companies(:first_firm).developers.count
|
||||
end
|
||||
|
||||
def test_associate_with_create_and_valid_options
|
||||
peeps = companies(:first_firm).developers.count
|
||||
assert_nothing_raised { companies(:first_firm).developers.create(:name => 'developer') }
|
||||
assert_equal peeps + 1, companies(:first_firm).developers.count
|
||||
end
|
||||
|
||||
def test_associate_with_create_bang_and_invalid_options
|
||||
peeps = companies(:first_firm).developers.count
|
||||
assert_raises(ActiveRecord::RecordInvalid) { companies(:first_firm).developers.create!(:name => '0') }
|
||||
assert_equal peeps, companies(:first_firm).developers.count
|
||||
end
|
||||
|
||||
def test_associate_with_create_bang_and_valid_options
|
||||
peeps = companies(:first_firm).developers.count
|
||||
assert_nothing_raised { companies(:first_firm).developers.create!(:name => 'developer') }
|
||||
assert_equal peeps + 1, companies(:first_firm).developers.count
|
||||
end
|
||||
|
||||
def test_clear_associations
|
||||
assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
|
||||
|
||||
assert_queries(1) do
|
||||
posts(:welcome).people.clear
|
||||
end
|
||||
|
||||
assert_queries(0) do
|
||||
assert posts(:welcome).people.empty?
|
||||
end
|
||||
|
||||
assert posts(:welcome).reload.people(true).empty?
|
||||
end
|
||||
|
||||
def test_association_callback_ordering
|
||||
Post.reset_log
|
||||
log = Post.log
|
||||
post = posts(:thinking)
|
||||
|
||||
post.people_with_callbacks << people(:michael)
|
||||
assert_equal [
|
||||
[:added, :before, "Michael"],
|
||||
[:added, :after, "Michael"]
|
||||
], log.last(2)
|
||||
|
||||
post.people_with_callbacks.push(people(:david), Person.create!(:first_name => "Bob"), Person.new(:first_name => "Lary"))
|
||||
assert_equal [
|
||||
[:added, :before, "David"],
|
||||
[:added, :after, "David"],
|
||||
[:added, :before, "Bob"],
|
||||
[:added, :after, "Bob"],
|
||||
[:added, :before, "Lary"],
|
||||
[:added, :after, "Lary"]
|
||||
],log.last(6)
|
||||
|
||||
post.people_with_callbacks.build(:first_name => "Ted")
|
||||
assert_equal [
|
||||
[:added, :before, "Ted"],
|
||||
[:added, :after, "Ted"]
|
||||
], log.last(2)
|
||||
|
||||
post.people_with_callbacks.create(:first_name => "Sam")
|
||||
assert_equal [
|
||||
[:added, :before, "Sam"],
|
||||
[:added, :after, "Sam"]
|
||||
], log.last(2)
|
||||
|
||||
post.people_with_callbacks = [people(:michael),people(:david), Person.new(:first_name => "Julian"), Person.create!(:first_name => "Roger")]
|
||||
assert_equal (%w(Ted Bob Sam Lary) * 2).sort, log[-12..-5].collect(&:last).sort
|
||||
assert_equal [
|
||||
[:added, :before, "Julian"],
|
||||
[:added, :after, "Julian"],
|
||||
[:added, :before, "Roger"],
|
||||
[:added, :after, "Roger"]
|
||||
], log.last(4)
|
||||
|
||||
post.people_with_callbacks.clear
|
||||
assert_equal (%w(Michael David Julian Roger) * 2).sort, log.last(8).collect(&:last).sort
|
||||
end
|
||||
|
||||
def test_dynamic_find_should_respect_association_include
|
||||
# SQL error in sort clause if :include is not included
|
||||
# due to Unknown column 'comments.id'
|
||||
assert Person.find(1).posts_with_comments_sorted_by_comment_id.find_by_title('Welcome to the weblog')
|
||||
end
|
||||
|
||||
def test_count_with_include_should_alias_join_table
|
||||
assert_equal 2, people(:michael).posts.count(:include => :readers)
|
||||
end
|
||||
|
||||
def test_inner_join_with_quoted_table_name
|
||||
assert_equal 2, people(:michael).jobs.size
|
||||
end
|
||||
|
||||
def test_get_ids
|
||||
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
|
||||
end
|
||||
|
||||
def test_get_ids_for_loaded_associations
|
||||
person = people(:michael)
|
||||
person.posts(true)
|
||||
assert_queries(0) do
|
||||
person.post_ids
|
||||
person.post_ids
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_ids_for_unloaded_associations_does_not_load_them
|
||||
person = people(:michael)
|
||||
assert !person.posts.loaded?
|
||||
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, person.post_ids.sort
|
||||
assert !person.posts.loaded?
|
||||
end
|
||||
|
||||
def test_association_proxy_transaction_method_starts_transaction_in_association_class
|
||||
Tag.expects(:transaction)
|
||||
Post.find(:first).tags.transaction do
|
||||
# nothing
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_association_through_a_belongs_to_association_where_the_association_doesnt_exist
|
||||
author = authors(:mary)
|
||||
post = Post.create!(:title => "TITLE", :body => "BODY")
|
||||
assert_equal [], post.author_favorites
|
||||
end
|
||||
|
||||
def test_has_many_association_through_a_belongs_to_association
|
||||
author = authors(:mary)
|
||||
post = Post.create!(:author => author, :title => "TITLE", :body => "BODY")
|
||||
author.author_favorites.create(:favorite_author_id => 1)
|
||||
author.author_favorites.create(:favorite_author_id => 2)
|
||||
author.author_favorites.create(:favorite_author_id => 3)
|
||||
assert_equal post.author.author_favorites, post.author_favorites
|
||||
end
|
||||
|
||||
def test_has_many_association_through_a_has_many_association_with_nonstandard_primary_keys
|
||||
assert_equal 1, owners(:blackbeard).toys.count
|
||||
end
|
||||
|
||||
def test_find_on_has_many_association_collection_with_include_and_conditions
|
||||
post_with_no_comments = people(:michael).posts_with_no_comments.first
|
||||
assert_equal post_with_no_comments, posts(:authorless)
|
||||
end
|
||||
|
||||
def test_has_many_through_has_one_reflection
|
||||
assert_equal [comments(:eager_sti_on_associations_vs_comment)], authors(:david).very_special_comments
|
||||
end
|
||||
|
||||
def test_modifying_has_many_through_has_one_reflection_should_raise
|
||||
[
|
||||
lambda { authors(:david).very_special_comments = [VerySpecialComment.create!(:body => "Gorp!", :post_id => 1011), VerySpecialComment.create!(:body => "Eep!", :post_id => 1012)] },
|
||||
lambda { authors(:david).very_special_comments << VerySpecialComment.create!(:body => "Hoohah!", :post_id => 1013) },
|
||||
lambda { authors(:david).very_special_comments.delete(authors(:david).very_special_comments.first) },
|
||||
].each {|block| assert_raise(ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection, &block) }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,330 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/company'
|
||||
|
||||
class HasOneAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :developers_projects
|
||||
|
||||
def setup
|
||||
Account.destroyed_account_ids.clear
|
||||
end
|
||||
|
||||
def test_has_one
|
||||
assert_equal companies(:first_firm).account, Account.find(1)
|
||||
assert_equal Account.find(1).credit_limit, companies(:first_firm).account.credit_limit
|
||||
end
|
||||
|
||||
def test_has_one_cache_nils
|
||||
firm = companies(:another_firm)
|
||||
assert_queries(1) { assert_nil firm.account }
|
||||
assert_queries(0) { assert_nil firm.account }
|
||||
|
||||
firms = Firm.find(:all, :include => :account)
|
||||
assert_queries(0) { firms.each(&:account) }
|
||||
end
|
||||
|
||||
def test_with_select
|
||||
assert_equal Firm.find(1).account_with_select.attributes.size, 2
|
||||
assert_equal Firm.find(1, :include => :account_with_select).account_with_select.attributes.size, 2
|
||||
end
|
||||
|
||||
def test_finding_using_primary_key
|
||||
firm = companies(:first_firm)
|
||||
assert_equal Account.find_by_firm_id(firm.id), firm.account
|
||||
firm.firm_id = companies(:rails_core).id
|
||||
assert_equal accounts(:rails_core_account), firm.account_using_primary_key
|
||||
end
|
||||
|
||||
def test_update_with_foreign_and_primary_keys
|
||||
firm = companies(:first_firm)
|
||||
account = firm.account_using_foreign_and_primary_keys
|
||||
assert_equal Account.find_by_firm_name(firm.name), account
|
||||
firm.save
|
||||
firm.reload
|
||||
assert_equal account, firm.account_using_foreign_and_primary_keys
|
||||
end
|
||||
|
||||
def test_can_marshal_has_one_association_with_nil_target
|
||||
firm = Firm.new
|
||||
assert_nothing_raised do
|
||||
assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
|
||||
end
|
||||
|
||||
firm.account
|
||||
assert_nothing_raised do
|
||||
assert_equal firm.attributes, Marshal.load(Marshal.dump(firm)).attributes
|
||||
end
|
||||
end
|
||||
|
||||
def test_proxy_assignment
|
||||
company = companies(:first_firm)
|
||||
assert_nothing_raised { company.account = company.account }
|
||||
end
|
||||
|
||||
def test_triple_equality
|
||||
assert Account === companies(:first_firm).account
|
||||
assert companies(:first_firm).account === Account
|
||||
end
|
||||
|
||||
def test_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = 1 }
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { companies(:first_firm).account = Project.find(1) }
|
||||
end
|
||||
|
||||
def test_natural_assignment
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple.account = citibank
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil
|
||||
old_account_id = companies(:first_firm).account.id
|
||||
companies(:first_firm).account = nil
|
||||
companies(:first_firm).save
|
||||
assert_nil companies(:first_firm).account
|
||||
# account is dependent, therefore is destroyed when reference to owner is lost
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Account.find(old_account_id) }
|
||||
end
|
||||
|
||||
def test_nullification_on_association_change
|
||||
firm = companies(:rails_core)
|
||||
old_account_id = firm.account.id
|
||||
firm.account = Account.new
|
||||
# account is dependent with nullify, therefore its firm_id should be nil
|
||||
assert_nil Account.find(old_account_id).firm_id
|
||||
end
|
||||
|
||||
def test_association_changecalls_delete
|
||||
companies(:first_firm).deletable_account = Account.new
|
||||
assert_equal [], Account.destroyed_account_ids[companies(:first_firm).id]
|
||||
end
|
||||
|
||||
def test_association_change_calls_destroy
|
||||
companies(:first_firm).account = Account.new
|
||||
assert_equal [companies(:first_firm).id], Account.destroyed_account_ids[companies(:first_firm).id]
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_already_associated_record
|
||||
company = companies(:first_firm)
|
||||
account = accounts(:signals37)
|
||||
assert_equal company.account, account
|
||||
company.account = account
|
||||
company.reload
|
||||
account.reload
|
||||
assert_equal company.account, account
|
||||
end
|
||||
|
||||
def test_assignment_without_replacement
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple.account = citibank
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
|
||||
hsbc = apple.build_account({ :credit_limit => 20}, false)
|
||||
assert_equal apple.id, hsbc.firm_id
|
||||
hsbc.save
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
|
||||
nykredit = apple.create_account({ :credit_limit => 30}, false)
|
||||
assert_equal apple.id, nykredit.firm_id
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
assert_equal apple.id, hsbc.firm_id
|
||||
end
|
||||
|
||||
def test_assignment_without_replacement_on_create
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple.account = citibank
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
|
||||
hsbc = apple.create_account({:credit_limit => 10}, false)
|
||||
assert_equal apple.id, hsbc.firm_id
|
||||
hsbc.save
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_dependence
|
||||
num_accounts = Account.count
|
||||
|
||||
firm = Firm.find(1)
|
||||
assert !firm.account.nil?
|
||||
account_id = firm.account.id
|
||||
assert_equal [], Account.destroyed_account_ids[firm.id]
|
||||
|
||||
firm.destroy
|
||||
assert_equal num_accounts - 1, Account.count
|
||||
assert_equal [account_id], Account.destroyed_account_ids[firm.id]
|
||||
end
|
||||
|
||||
def test_exclusive_dependence
|
||||
num_accounts = Account.count
|
||||
|
||||
firm = ExclusivelyDependentFirm.find(9)
|
||||
assert !firm.account.nil?
|
||||
account_id = firm.account.id
|
||||
assert_equal [], Account.destroyed_account_ids[firm.id]
|
||||
|
||||
firm.destroy
|
||||
assert_equal num_accounts - 1, Account.count
|
||||
assert_equal [], Account.destroyed_account_ids[firm.id]
|
||||
end
|
||||
|
||||
def test_dependence_with_nil_associate
|
||||
firm = DependentFirm.new(:name => 'nullify')
|
||||
firm.save!
|
||||
assert_nothing_raised { firm.destroy }
|
||||
end
|
||||
|
||||
def test_succesful_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
account = firm.build_account("credit_limit" => 1000)
|
||||
assert account.save
|
||||
assert_equal account, firm.account
|
||||
end
|
||||
|
||||
def test_failing_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
account = firm.build_account
|
||||
assert !account.save
|
||||
assert_equal "can't be empty", account.errors.on("credit_limit")
|
||||
end
|
||||
|
||||
def test_build_association_twice_without_saving_affects_nothing
|
||||
count_of_account = Account.count
|
||||
firm = Firm.find(:first)
|
||||
account1 = firm.build_account("credit_limit" => 1000)
|
||||
account2 = firm.build_account("credit_limit" => 2000)
|
||||
|
||||
assert_equal count_of_account, Account.count
|
||||
end
|
||||
|
||||
def test_create_association
|
||||
firm = Firm.create(:name => "GlobalMegaCorp")
|
||||
account = firm.create_account(:credit_limit => 1000)
|
||||
assert_equal account, firm.reload.account
|
||||
end
|
||||
|
||||
def test_build
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
firm.account = account = Account.new("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
assert account.save
|
||||
assert_equal account, firm.account
|
||||
end
|
||||
|
||||
def test_failing_build_association
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
|
||||
firm.account = account = Account.new
|
||||
assert_equal account, firm.account
|
||||
assert !account.save
|
||||
assert_equal account, firm.account
|
||||
assert_equal "can't be empty", account.errors.on("credit_limit")
|
||||
end
|
||||
|
||||
def test_create
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.save
|
||||
firm.account = account = Account.create("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
end
|
||||
|
||||
def test_create_before_save
|
||||
firm = Firm.new("name" => "GlobalMegaCorp")
|
||||
firm.account = account = Account.create("credit_limit" => 1000)
|
||||
assert_equal account, firm.account
|
||||
end
|
||||
|
||||
def test_dependence_with_missing_association
|
||||
Account.destroy_all
|
||||
firm = Firm.find(1)
|
||||
assert firm.account.nil?
|
||||
firm.destroy
|
||||
end
|
||||
|
||||
def test_dependence_with_missing_association_and_nullify
|
||||
Account.destroy_all
|
||||
firm = DependentFirm.find(:first)
|
||||
assert firm.account.nil?
|
||||
firm.destroy
|
||||
end
|
||||
|
||||
def test_finding_with_interpolated_condition
|
||||
firm = Firm.find(:first)
|
||||
superior = firm.clients.create(:name => 'SuperiorCo')
|
||||
superior.rating = 10
|
||||
superior.save
|
||||
assert_equal 10, firm.clients_with_interpolated_conditions.first.rating
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved
|
||||
firm = Firm.find(1)
|
||||
firm.account = a = Account.new("credit_limit" => 1000)
|
||||
assert !a.new_record?
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account
|
||||
assert_equal a, firm.account(true)
|
||||
end
|
||||
|
||||
def test_save_still_works_after_accessing_nil_has_one
|
||||
jp = Company.new :name => 'Jaded Pixel'
|
||||
jp.dummy_account.nil?
|
||||
|
||||
assert_nothing_raised do
|
||||
jp.save!
|
||||
end
|
||||
end
|
||||
|
||||
def test_cant_save_readonly_association
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { companies(:first_firm).readonly_account.save! }
|
||||
assert companies(:first_firm).readonly_account.readonly?
|
||||
end
|
||||
|
||||
def test_has_one_proxy_should_not_respond_to_private_methods
|
||||
assert_raise(NoMethodError) { accounts(:signals37).private_method }
|
||||
assert_raise(NoMethodError) { companies(:first_firm).account.private_method }
|
||||
end
|
||||
|
||||
def test_has_one_proxy_should_respond_to_private_methods_via_send
|
||||
accounts(:signals37).send(:private_method)
|
||||
companies(:first_firm).account.send(:private_method)
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_has_one
|
||||
@firm = companies(:first_firm)
|
||||
assert_not_nil @firm.account
|
||||
|
||||
assert_nothing_raised do
|
||||
Firm.find(@firm.id).save!
|
||||
Firm.find(@firm.id, :include => :account).save!
|
||||
end
|
||||
|
||||
@firm.account.destroy
|
||||
|
||||
assert_nothing_raised do
|
||||
Firm.find(@firm.id).save!
|
||||
Firm.find(@firm.id, :include => :account).save!
|
||||
end
|
||||
end
|
||||
|
||||
def test_build_respects_hash_condition
|
||||
account = companies(:first_firm).build_account_limit_500_with_hash_conditions
|
||||
assert account.save
|
||||
assert_equal 500, account.credit_limit
|
||||
end
|
||||
|
||||
def test_create_respects_hash_condition
|
||||
account = companies(:first_firm).create_account_limit_500_with_hash_conditions
|
||||
assert !account.new_record?
|
||||
assert_equal 500, account.credit_limit
|
||||
end
|
||||
end
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/club'
|
||||
require 'models/member_type'
|
||||
require 'models/member'
|
||||
require 'models/membership'
|
||||
require 'models/sponsor'
|
||||
require 'models/organization'
|
||||
require 'models/member_detail'
|
||||
|
||||
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations
|
||||
|
||||
def setup
|
||||
@member = members(:groucho)
|
||||
end
|
||||
|
||||
def test_has_one_through_with_has_one
|
||||
assert_equal clubs(:boring_club), @member.club
|
||||
end
|
||||
|
||||
def test_has_one_through_with_has_many
|
||||
assert_equal clubs(:moustache_club), @member.favourite_club
|
||||
end
|
||||
|
||||
def test_creating_association_creates_through_record
|
||||
new_member = Member.create(:name => "Chris")
|
||||
new_member.club = Club.create(:name => "LRUG")
|
||||
assert_not_nil new_member.current_membership
|
||||
assert_not_nil new_member.club
|
||||
end
|
||||
|
||||
def test_creating_association_builds_through_record_for_new
|
||||
new_member = Member.new(:name => "Jane")
|
||||
new_member.club = clubs(:moustache_club)
|
||||
assert new_member.current_membership
|
||||
assert_equal clubs(:moustache_club), new_member.current_membership.club
|
||||
assert_equal clubs(:moustache_club), new_member.club
|
||||
assert new_member.save
|
||||
assert_equal clubs(:moustache_club), new_member.club
|
||||
end
|
||||
|
||||
def test_replace_target_record
|
||||
new_club = Club.create(:name => "Marx Bros")
|
||||
@member.club = new_club
|
||||
@member.reload
|
||||
assert_equal new_club, @member.club
|
||||
end
|
||||
|
||||
def test_replacing_target_record_deletes_old_association
|
||||
assert_no_difference "Membership.count" do
|
||||
new_club = Club.create(:name => "Bananarama")
|
||||
@member.club = new_club
|
||||
@member.reload
|
||||
end
|
||||
end
|
||||
|
||||
def test_set_record_to_nil_should_delete_association
|
||||
@member.club = nil
|
||||
@member.reload
|
||||
assert_equal nil, @member.current_membership
|
||||
assert_nil @member.club
|
||||
end
|
||||
|
||||
def test_has_one_through_polymorphic
|
||||
assert_equal clubs(:moustache_club), @member.sponsor_club
|
||||
end
|
||||
|
||||
def has_one_through_to_has_many
|
||||
assert_equal 2, @member.fellow_members.size
|
||||
end
|
||||
|
||||
def test_has_one_through_eager_loading
|
||||
members = assert_queries(3) do #base table, through table, clubs table
|
||||
Member.find(:all, :include => :club, :conditions => ["name = ?", "Groucho Marx"])
|
||||
end
|
||||
assert_equal 1, members.size
|
||||
assert_not_nil assert_no_queries {members[0].club}
|
||||
end
|
||||
|
||||
def test_has_one_through_eager_loading_through_polymorphic
|
||||
members = assert_queries(3) do #base table, through table, clubs table
|
||||
Member.find(:all, :include => :sponsor_club, :conditions => ["name = ?", "Groucho Marx"])
|
||||
end
|
||||
assert_equal 1, members.size
|
||||
assert_not_nil assert_no_queries {members[0].sponsor_club}
|
||||
end
|
||||
|
||||
def test_has_one_through_polymorphic_with_source_type
|
||||
assert_equal members(:groucho), clubs(:moustache_club).sponsored_member
|
||||
end
|
||||
|
||||
def test_eager_has_one_through_polymorphic_with_source_type
|
||||
clubs = Club.find(:all, :include => :sponsored_member, :conditions => ["name = ?","Moustache and Eyebrow Fancier Club"])
|
||||
# Only the eyebrow fanciers club has a sponsored_member
|
||||
assert_not_nil assert_no_queries {clubs[0].sponsored_member}
|
||||
end
|
||||
|
||||
def test_has_one_through_nonpreload_eagerloading
|
||||
members = assert_queries(1) do
|
||||
Member.find(:all, :include => :club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
|
||||
end
|
||||
assert_equal 1, members.size
|
||||
assert_not_nil assert_no_queries {members[0].club}
|
||||
end
|
||||
|
||||
def test_has_one_through_nonpreload_eager_loading_through_polymorphic
|
||||
members = assert_queries(1) do
|
||||
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name') #force fallback
|
||||
end
|
||||
assert_equal 1, members.size
|
||||
assert_not_nil assert_no_queries {members[0].sponsor_club}
|
||||
end
|
||||
|
||||
def test_has_one_through_nonpreload_eager_loading_through_polymorphic_with_more_than_one_through_record
|
||||
Sponsor.new(:sponsor_club => clubs(:crazy_club), :sponsorable => members(:groucho)).save!
|
||||
members = assert_queries(1) do
|
||||
Member.find(:all, :include => :sponsor_club, :conditions => ["members.name = ?", "Groucho Marx"], :order => 'clubs.name DESC') #force fallback
|
||||
end
|
||||
assert_equal 1, members.size
|
||||
assert_not_nil assert_no_queries { members[0].sponsor_club }
|
||||
assert_equal clubs(:crazy_club), members[0].sponsor_club
|
||||
end
|
||||
|
||||
def test_uninitialized_has_one_through_should_return_nil_for_unsaved_record
|
||||
assert_nil Member.new.club
|
||||
end
|
||||
|
||||
def test_assigning_association_correctly_assigns_target
|
||||
new_member = Member.create(:name => "Chris")
|
||||
new_member.club = new_club = Club.create(:name => "LRUG")
|
||||
assert_equal new_club, new_member.club.target
|
||||
end
|
||||
|
||||
def test_has_one_through_proxy_should_not_respond_to_private_methods
|
||||
assert_raise(NoMethodError) { clubs(:moustache_club).private_method }
|
||||
assert_raise(NoMethodError) { @member.club.private_method }
|
||||
end
|
||||
|
||||
def test_has_one_through_proxy_should_respond_to_private_methods_via_send
|
||||
clubs(:moustache_club).send(:private_method)
|
||||
@member.club.send(:private_method)
|
||||
end
|
||||
|
||||
def test_assigning_to_has_one_through_preserves_decorated_join_record
|
||||
@organization = organizations(:nsa)
|
||||
assert_difference 'MemberDetail.count', 1 do
|
||||
@member_detail = MemberDetail.new(:extra_data => 'Extra')
|
||||
@member.member_detail = @member_detail
|
||||
@member.organization = @organization
|
||||
end
|
||||
assert_equal @organization, @member.organization
|
||||
assert @organization.members.include?(@member)
|
||||
assert_equal 'Extra', @member.member_detail.extra_data
|
||||
end
|
||||
|
||||
def test_reassigning_has_one_through
|
||||
@organization = organizations(:nsa)
|
||||
@new_organization = organizations(:discordians)
|
||||
|
||||
assert_difference 'MemberDetail.count', 1 do
|
||||
@member_detail = MemberDetail.new(:extra_data => 'Extra')
|
||||
@member.member_detail = @member_detail
|
||||
@member.organization = @organization
|
||||
end
|
||||
assert_equal @organization, @member.organization
|
||||
assert_equal 'Extra', @member.member_detail.extra_data
|
||||
assert @organization.members.include?(@member)
|
||||
assert !@new_organization.members.include?(@member)
|
||||
|
||||
assert_no_difference 'MemberDetail.count' do
|
||||
@member.organization = @new_organization
|
||||
end
|
||||
assert_equal @new_organization, @member.organization
|
||||
assert_equal 'Extra', @member.member_detail.extra_data
|
||||
assert !@organization.members.include?(@member)
|
||||
assert @new_organization.members.include?(@member)
|
||||
end
|
||||
|
||||
def test_preloading_has_one_through_on_belongs_to
|
||||
assert_not_nil @member.member_type
|
||||
@organization = organizations(:nsa)
|
||||
@member_detail = MemberDetail.new
|
||||
@member.member_detail = @member_detail
|
||||
@member.organization = @organization
|
||||
@member_details = assert_queries(3) do
|
||||
MemberDetail.find(:all, :include => :member_type)
|
||||
end
|
||||
@new_detail = @member_details[0]
|
||||
assert @new_detail.loaded_member_type?
|
||||
assert_not_nil assert_no_queries { @new_detail.member_type }
|
||||
end
|
||||
|
||||
def test_save_of_record_with_loaded_has_one_through
|
||||
@club = @member.club
|
||||
assert_not_nil @club.sponsored_member
|
||||
|
||||
assert_nothing_raised do
|
||||
Club.find(@club.id).save!
|
||||
Club.find(@club.id, :include => :sponsored_member).save!
|
||||
end
|
||||
|
||||
@club.sponsor.destroy
|
||||
|
||||
assert_nothing_raised do
|
||||
Club.find(@club.id).save!
|
||||
Club.find(@club.id, :include => :sponsored_member).save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
|
||||
class InnerJoinAssociationTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :posts, :comments, :categories, :categories_posts, :categorizations
|
||||
|
||||
def test_construct_finder_sql_creates_inner_joins
|
||||
sql = Author.send(:construct_finder_sql, :joins => :posts)
|
||||
assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_cascades_inner_joins
|
||||
sql = Author.send(:construct_finder_sql, :joins => {:posts => :comments})
|
||||
assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
|
||||
assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = posts.id/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_inner_joins_through_associations
|
||||
sql = Author.send(:construct_finder_sql, :joins => :categorized_posts)
|
||||
assert_match /INNER JOIN .?categorizations.?.*INNER JOIN .?posts.?/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_applies_association_conditions
|
||||
sql = Author.send(:construct_finder_sql, :joins => :categories_like_general, :conditions => "TERMINATING_MARKER")
|
||||
assert_match /INNER JOIN .?categories.? ON.*AND.*.?General.?.*TERMINATING_MARKER/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
|
||||
result = Author.find(:all, :joins => [:thinking_posts, :welcome_posts])
|
||||
assert_equal authors(:david), result.first
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_unpacks_nested_joins
|
||||
sql = Author.send(:construct_finder_sql, :joins => {:posts => [[:comments]]})
|
||||
assert_no_match /inner join.*inner join.*inner join/i, sql, "only two join clauses should be present"
|
||||
assert_match /INNER JOIN .?posts.? ON .?posts.?.author_id = authors.id/, sql
|
||||
assert_match /INNER JOIN .?comments.? ON .?comments.?.post_id = .?posts.?.id/, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_ignores_empty_joins_hash
|
||||
sql = Author.send(:construct_finder_sql, :joins => {})
|
||||
assert_no_match /JOIN/i, sql
|
||||
end
|
||||
|
||||
def test_construct_finder_sql_ignores_empty_joins_array
|
||||
sql = Author.send(:construct_finder_sql, :joins => [])
|
||||
assert_no_match /JOIN/i, sql
|
||||
end
|
||||
|
||||
def test_find_with_implicit_inner_joins_honors_readonly_without_select
|
||||
authors = Author.find(:all, :joins => :posts)
|
||||
assert !authors.empty?, "expected authors to be non-empty"
|
||||
assert authors.all? {|a| a.readonly? }, "expected all authors to be readonly"
|
||||
end
|
||||
|
||||
def test_find_with_implicit_inner_joins_honors_readonly_with_select
|
||||
authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
|
||||
assert !authors.empty?, "expected authors to be non-empty"
|
||||
assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
|
||||
end
|
||||
|
||||
def test_find_with_implicit_inner_joins_honors_readonly_false
|
||||
authors = Author.find(:all, :joins => :posts, :readonly => false)
|
||||
assert !authors.empty?, "expected authors to be non-empty"
|
||||
assert authors.all? {|a| !a.readonly? }, "expected no authors to be readonly"
|
||||
end
|
||||
|
||||
def test_find_with_implicit_inner_joins_does_not_set_associations
|
||||
authors = Author.find(:all, :select => 'authors.*', :joins => :posts)
|
||||
assert !authors.empty?, "expected authors to be non-empty"
|
||||
assert authors.all? {|a| !a.send(:instance_variable_names).include?("@posts")}, "expected no authors to have the @posts association loaded"
|
||||
end
|
||||
|
||||
def test_count_honors_implicit_inner_joins
|
||||
real_count = Author.find(:all).sum{|a| a.posts.count }
|
||||
assert_equal real_count, Author.count(:joins => :posts), "plain inner join count should match the number of referenced posts records"
|
||||
end
|
||||
|
||||
def test_calculate_honors_implicit_inner_joins
|
||||
real_count = Author.find(:all).sum{|a| a.posts.count }
|
||||
assert_equal real_count, Author.calculate(:count, 'authors.id', :joins => :posts), "plain inner join count should match the number of referenced posts records"
|
||||
end
|
||||
|
||||
def test_calculate_honors_implicit_inner_joins_and_distinct_and_conditions
|
||||
real_count = Author.find(:all).select {|a| a.posts.any? {|p| p.title =~ /^Welcome/} }.length
|
||||
authors_with_welcoming_post_titles = Author.calculate(:count, 'authors.id', :joins => :posts, :distinct => true, :conditions => "posts.title like 'Welcome%'")
|
||||
assert_equal real_count, authors_with_welcoming_post_titles, "inner join and conditions should have only returned authors posting titles starting with 'Welcome'"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,566 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/man'
|
||||
require 'models/face'
|
||||
require 'models/interest'
|
||||
require 'models/zine'
|
||||
require 'models/club'
|
||||
require 'models/sponsor'
|
||||
|
||||
class InverseAssociationTests < ActiveRecord::TestCase
|
||||
def test_should_allow_for_inverse_of_options_in_associations
|
||||
assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_many') do
|
||||
Class.new(ActiveRecord::Base).has_many(:wheels, :inverse_of => :car)
|
||||
end
|
||||
|
||||
assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on has_one') do
|
||||
Class.new(ActiveRecord::Base).has_one(:engine, :inverse_of => :car)
|
||||
end
|
||||
|
||||
assert_nothing_raised(ArgumentError, 'ActiveRecord should allow the inverse_of options on belongs_to') do
|
||||
Class.new(ActiveRecord::Base).belongs_to(:car, :inverse_of => :driver)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_be_able_to_ask_a_reflection_if_it_has_an_inverse
|
||||
has_one_with_inverse_ref = Man.reflect_on_association(:face)
|
||||
assert has_one_with_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert has_one_with_inverse_ref.has_inverse?
|
||||
|
||||
has_many_with_inverse_ref = Man.reflect_on_association(:interests)
|
||||
assert has_many_with_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert has_many_with_inverse_ref.has_inverse?
|
||||
|
||||
belongs_to_with_inverse_ref = Face.reflect_on_association(:man)
|
||||
assert belongs_to_with_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert belongs_to_with_inverse_ref.has_inverse?
|
||||
|
||||
has_one_without_inverse_ref = Club.reflect_on_association(:sponsor)
|
||||
assert has_one_without_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert !has_one_without_inverse_ref.has_inverse?
|
||||
|
||||
has_many_without_inverse_ref = Club.reflect_on_association(:memberships)
|
||||
assert has_many_without_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert !has_many_without_inverse_ref.has_inverse?
|
||||
|
||||
belongs_to_without_inverse_ref = Sponsor.reflect_on_association(:sponsor_club)
|
||||
assert belongs_to_without_inverse_ref.respond_to?(:has_inverse?)
|
||||
assert !belongs_to_without_inverse_ref.has_inverse?
|
||||
end
|
||||
|
||||
def test_should_be_able_to_ask_a_reflection_what_it_is_the_inverse_of
|
||||
has_one_ref = Man.reflect_on_association(:face)
|
||||
assert has_one_ref.respond_to?(:inverse_of)
|
||||
|
||||
has_many_ref = Man.reflect_on_association(:interests)
|
||||
assert has_many_ref.respond_to?(:inverse_of)
|
||||
|
||||
belongs_to_ref = Face.reflect_on_association(:man)
|
||||
assert belongs_to_ref.respond_to?(:inverse_of)
|
||||
end
|
||||
|
||||
def test_inverse_of_method_should_supply_the_actual_reflection_instance_it_is_the_inverse_of
|
||||
has_one_ref = Man.reflect_on_association(:face)
|
||||
assert_equal Face.reflect_on_association(:man), has_one_ref.inverse_of
|
||||
|
||||
has_many_ref = Man.reflect_on_association(:interests)
|
||||
assert_equal Interest.reflect_on_association(:man), has_many_ref.inverse_of
|
||||
|
||||
belongs_to_ref = Face.reflect_on_association(:man)
|
||||
assert_equal Man.reflect_on_association(:face), belongs_to_ref.inverse_of
|
||||
end
|
||||
|
||||
def test_associations_with_no_inverse_of_should_return_nil
|
||||
has_one_ref = Club.reflect_on_association(:sponsor)
|
||||
assert_nil has_one_ref.inverse_of
|
||||
|
||||
has_many_ref = Club.reflect_on_association(:memberships)
|
||||
assert_nil has_many_ref.inverse_of
|
||||
|
||||
belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
|
||||
assert_nil belongs_to_ref.inverse_of
|
||||
end
|
||||
end
|
||||
|
||||
class InverseHasOneTests < ActiveRecord::TestCase
|
||||
fixtures :men, :faces
|
||||
|
||||
def test_parent_instance_should_be_shared_with_child_on_find
|
||||
m = men(:gordon)
|
||||
f = m.face
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_eager_loaded_child_on_find
|
||||
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :face)
|
||||
f = m.face
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
|
||||
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :face, :order => 'faces.id')
|
||||
f = m.face
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_built_child
|
||||
m = men(:gordon)
|
||||
f = m.build_face(:description => 'haunted')
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_child
|
||||
m = men(:gordon)
|
||||
f = m.create_face(:description => 'haunted')
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method
|
||||
m = Man.find(:first)
|
||||
f = m.face.create!(:description => 'haunted')
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_built_child_when_we_dont_replace_existing
|
||||
m = Man.find(:first)
|
||||
f = m.build_face({:description => 'haunted'}, false)
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_child_when_we_dont_replace_existing
|
||||
m = Man.find(:first)
|
||||
f = m.create_face({:description => 'haunted'}, false)
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_child_via_bang_method_when_we_dont_replace_existing
|
||||
m = Man.find(:first)
|
||||
f = m.face.create!({:description => 'haunted'}, false)
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_replaced_via_accessor_child
|
||||
m = Man.find(:first)
|
||||
f = Face.new(:description => 'haunted')
|
||||
m.face = f
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_replaced_via_method_child
|
||||
m = Man.find(:first)
|
||||
f = Face.new(:description => 'haunted')
|
||||
m.face.replace(f)
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_replaced_via_method_child_when_we_dont_replace_existing
|
||||
m = Man.find(:first)
|
||||
f = Face.new(:description => 'haunted')
|
||||
m.face.replace(f, false)
|
||||
assert_not_nil f.man
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to parent instance"
|
||||
f.man.name = 'Mungo'
|
||||
assert_equal m.name, f.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
|
||||
end
|
||||
|
||||
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
|
||||
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).dirty_face }
|
||||
end
|
||||
end
|
||||
|
||||
class InverseHasManyTests < ActiveRecord::TestCase
|
||||
fixtures :men, :interests
|
||||
|
||||
def test_parent_instance_should_be_shared_with_every_child_on_find
|
||||
m = men(:gordon)
|
||||
is = m.interests
|
||||
is.each do |i|
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
end
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_eager_loaded_children
|
||||
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :interests)
|
||||
is = m.interests
|
||||
is.each do |i|
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
end
|
||||
|
||||
m = Man.find(:first, :conditions => {:name => 'Gordon'}, :include => :interests, :order => 'interests.id')
|
||||
is = m.interests
|
||||
is.each do |i|
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to child-owned instance"
|
||||
end
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_built_child
|
||||
m = men(:gordon)
|
||||
i = m.interests.build(:topic => 'Industrial Revolution Re-enactment')
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_block_style_built_child
|
||||
m = Man.find(:first)
|
||||
i = m.interests.build {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
|
||||
assert_not_nil i.topic, "Child attributes supplied to build via blocks should be populated"
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to just-built-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_child
|
||||
m = men(:gordon)
|
||||
i = m.interests.create(:topic => 'Industrial Revolution Re-enactment')
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
|
||||
m = Man.find(:first)
|
||||
i = m.interests.create!(:topic => 'Industrial Revolution Re-enactment')
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_newly_block_style_created_child
|
||||
m = Man.find(:first)
|
||||
i = m.interests.create {|ii| ii.topic = 'Industrial Revolution Re-enactment'}
|
||||
assert_not_nil i.topic, "Child attributes supplied to create via blocks should be populated"
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_poked_in_child
|
||||
m = men(:gordon)
|
||||
i = Interest.create(:topic => 'Industrial Revolution Re-enactment')
|
||||
m.interests << i
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to newly-created-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
|
||||
m = Man.find(:first)
|
||||
i = Interest.new(:topic => 'Industrial Revolution Re-enactment')
|
||||
m.interests = [i]
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
|
||||
end
|
||||
|
||||
def test_parent_instance_should_be_shared_with_replaced_via_method_children
|
||||
m = Man.find(:first)
|
||||
i = Interest.new(:topic => 'Industrial Revolution Re-enactment')
|
||||
m.interests.replace([i])
|
||||
assert_not_nil i.man
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same before changes to parent instance"
|
||||
m.name = 'Bongo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to parent instance"
|
||||
i.man.name = 'Mungo'
|
||||
assert_equal m.name, i.man.name, "Name of man should be the same after changes to replaced-child-owned instance"
|
||||
end
|
||||
|
||||
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
|
||||
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Man.find(:first).secret_interests }
|
||||
end
|
||||
end
|
||||
|
||||
class InverseBelongsToTests < ActiveRecord::TestCase
|
||||
fixtures :men, :faces, :interests
|
||||
|
||||
def test_child_instance_should_be_shared_with_parent_on_find
|
||||
f = faces(:trusting)
|
||||
m = f.man
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
|
||||
f = Face.find(:first, :include => :man, :conditions => {:description => 'trusting'})
|
||||
m = f.man
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
|
||||
f = Face.find(:first, :include => :man, :order => 'men.id', :conditions => {:description => 'trusting'})
|
||||
m = f.man
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_newly_built_parent
|
||||
f = faces(:trusting)
|
||||
m = f.build_man(:name => 'Charles')
|
||||
assert_not_nil m.face
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to just-built-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_newly_created_parent
|
||||
f = faces(:trusting)
|
||||
m = f.create_man(:name => 'Charles')
|
||||
assert_not_nil m.face
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to newly-created-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
|
||||
i = interests(:trainspotting)
|
||||
m = i.man
|
||||
assert_not_nil m.interests
|
||||
iz = m.interests.detect {|iz| iz.id == i.id}
|
||||
assert_not_nil iz
|
||||
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
|
||||
i.topic = 'Eating cheese with a spoon'
|
||||
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
|
||||
iz.topic = 'Cow tipping'
|
||||
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
|
||||
f = Face.find(:first)
|
||||
m = Man.new(:name => 'Charles')
|
||||
f.man = m
|
||||
assert_not_nil m.face
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_replaced_via_method_parent
|
||||
f = faces(:trusting)
|
||||
assert_not_nil f.man
|
||||
m = Man.new(:name => 'Charles')
|
||||
f.man.replace(m)
|
||||
assert_not_nil m.face
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to child instance"
|
||||
m.face.description = 'pleasing'
|
||||
assert_equal f.description, m.face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_trying_to_use_inverses_that_dont_exist_should_raise_an_error
|
||||
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_man }
|
||||
end
|
||||
end
|
||||
|
||||
class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
|
||||
fixtures :men, :faces, :interests
|
||||
|
||||
def test_child_instance_should_be_shared_with_parent_on_find
|
||||
f = Face.find(:first, :conditions => {:description => 'confused'})
|
||||
m = f.polymorphic_man
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
|
||||
m.polymorphic_face.description = 'pleasing'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_eager_loaded_child_instance_should_be_shared_with_parent_on_find
|
||||
f = Face.find(:first, :conditions => {:description => 'confused'}, :include => :man)
|
||||
m = f.polymorphic_man
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
|
||||
m.polymorphic_face.description = 'pleasing'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
|
||||
f = Face.find(:first, :conditions => {:description => 'confused'}, :include => :man, :order => 'men.id')
|
||||
m = f.polymorphic_man
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same before changes to child instance"
|
||||
f.description = 'gormless'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to child instance"
|
||||
m.polymorphic_face.description = 'pleasing'
|
||||
assert_equal f.description, m.polymorphic_face.description, "Description of face should be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_replaced_via_accessor_parent
|
||||
face = faces(:confused)
|
||||
old_man = face.polymorphic_man
|
||||
new_man = Man.new
|
||||
|
||||
assert_not_nil face.polymorphic_man
|
||||
face.polymorphic_man = new_man
|
||||
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
|
||||
face.description = 'Bongo'
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
|
||||
new_man.polymorphic_face.description = 'Mungo'
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_child_instance_should_be_shared_with_replaced_via_method_parent
|
||||
face = faces(:confused)
|
||||
old_man = face.polymorphic_man
|
||||
new_man = Man.new
|
||||
|
||||
assert_not_nil face.polymorphic_man
|
||||
face.polymorphic_man.replace(new_man)
|
||||
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same before changes to parent instance"
|
||||
face.description = 'Bongo'
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to parent instance"
|
||||
new_man.polymorphic_face.description = 'Mungo'
|
||||
assert_equal face.description, new_man.polymorphic_face.description, "Description of face should be the same after changes to replaced-parent-owned instance"
|
||||
end
|
||||
|
||||
def test_should_not_try_to_set_inverse_instances_when_the_inverse_is_a_has_many
|
||||
i = interests(:llama_wrangling)
|
||||
m = i.polymorphic_man
|
||||
assert_not_nil m.polymorphic_interests
|
||||
iz = m.polymorphic_interests.detect {|iz| iz.id == i.id}
|
||||
assert_not_nil iz
|
||||
assert_equal i.topic, iz.topic, "Interest topics should be the same before changes to child"
|
||||
i.topic = 'Eating cheese with a spoon'
|
||||
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to child"
|
||||
iz.topic = 'Cow tipping'
|
||||
assert_not_equal i.topic, iz.topic, "Interest topics should not be the same after changes to parent-owned instance"
|
||||
end
|
||||
|
||||
def test_trying_to_access_inverses_that_dont_exist_shouldnt_raise_an_error
|
||||
# Ideally this would, if only for symmetry's sake with other association types
|
||||
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_polymorphic_man }
|
||||
end
|
||||
|
||||
def test_trying_to_set_polymorphic_inverses_that_dont_exist_at_all_should_raise_an_error
|
||||
# fails because no class has the correct inverse_of for horrible_polymorphic_man
|
||||
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).horrible_polymorphic_man = Man.first }
|
||||
end
|
||||
|
||||
def test_trying_to_set_polymorphic_inverses_that_dont_exist_on_the_instance_being_set_should_raise_an_error
|
||||
# passes because Man does have the correct inverse_of
|
||||
assert_nothing_raised(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).polymorphic_man = Man.first }
|
||||
# fails because Interest does have the correct inverse_of
|
||||
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.find(:first).polymorphic_man = Interest.first }
|
||||
end
|
||||
end
|
||||
|
||||
# NOTE - these tests might not be meaningful, ripped as they were from the parental_control plugin
|
||||
# which would guess the inverse rather than look for an explicit configuration option.
|
||||
class InverseMultipleHasManyInversesForSameModel < ActiveRecord::TestCase
|
||||
fixtures :men, :interests, :zines
|
||||
|
||||
def test_that_we_can_load_associations_that_have_the_same_reciprocal_name_from_different_models
|
||||
assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
|
||||
i = Interest.find(:first)
|
||||
z = i.zine
|
||||
m = i.man
|
||||
end
|
||||
end
|
||||
|
||||
def test_that_we_can_create_associations_that_have_the_same_reciprocal_name_from_different_models
|
||||
assert_nothing_raised(ActiveRecord::AssociationTypeMismatch) do
|
||||
i = Interest.find(:first)
|
||||
i.build_zine(:title => 'Get Some in Winter! 2008')
|
||||
i.build_man(:name => 'Gordon')
|
||||
i.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,712 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/post'
|
||||
require 'models/item'
|
||||
require 'models/comment'
|
||||
require 'models/author'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
require 'models/vertex'
|
||||
require 'models/edge'
|
||||
require 'models/book'
|
||||
require 'models/citation'
|
||||
|
||||
class AssociationsJoinModelTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
fixtures :posts, :authors, :categories, :categorizations, :comments, :tags, :taggings, :author_favorites, :vertices, :items, :books
|
||||
|
||||
def test_has_many
|
||||
assert authors(:david).categories.include?(categories(:general))
|
||||
end
|
||||
|
||||
def test_has_many_inherited
|
||||
assert authors(:mary).categories.include?(categories(:sti_test))
|
||||
end
|
||||
|
||||
def test_inherited_has_many
|
||||
assert categories(:sti_test).authors.include?(authors(:mary))
|
||||
end
|
||||
|
||||
def test_has_many_uniq_through_join_model
|
||||
assert_equal 2, authors(:mary).categorized_posts.size
|
||||
assert_equal 1, authors(:mary).unique_categorized_posts.size
|
||||
end
|
||||
|
||||
def test_has_many_uniq_through_count
|
||||
author = authors(:mary)
|
||||
assert !authors(:mary).unique_categorized_posts.loaded?
|
||||
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
|
||||
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count(:title) }
|
||||
assert_queries(1) { assert_equal 0, author.unique_categorized_posts.count(:title, :conditions => "title is NULL") }
|
||||
assert !authors(:mary).unique_categorized_posts.loaded?
|
||||
end
|
||||
|
||||
def test_has_many_uniq_through_find
|
||||
assert_equal 1, authors(:mary).unique_categorized_posts.find(:all).size
|
||||
end
|
||||
|
||||
def test_has_many_uniq_through_dynamic_find
|
||||
assert_equal 1, authors(:mary).unique_categorized_posts.find_all_by_title("So I was thinking").size
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many
|
||||
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one
|
||||
assert_equal taggings(:welcome_general), posts(:welcome).tagging
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to
|
||||
assert_equal posts(:welcome), posts(:welcome).taggings.first.taggable
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.first
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_count_polymorphic_has_many
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
assert_equal 1, posts(:welcome).tags.count
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_find
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.find(:first)
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection
|
||||
assert_equal tags(:general), tag = posts(:welcome).funky_tags.first
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_include_on_source_reflection_with_find
|
||||
assert_equal tags(:general), tag = posts(:welcome).funky_tags.find(:first)
|
||||
assert_no_queries do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_disabled_include
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
|
||||
assert_queries 1 do
|
||||
tag.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
|
||||
assert_equal tags(:general), tag = posts(:welcome).tags.add_joins_and_select.first
|
||||
tag.author_id
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_custom_foreign_key
|
||||
assert_equal tags(:misc), taggings(:welcome_general).super_tag
|
||||
assert_equal tags(:misc), posts(:welcome).super_tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_create_model_with_inheritance_and_custom_base_class
|
||||
post = SubStiPost.create :title => 'SubStiPost', :body => 'SubStiPost body'
|
||||
assert_instance_of SubStiPost, post
|
||||
|
||||
tagging = tags(:misc).taggings.create(:taggable => post)
|
||||
assert_equal "SubStiPost", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_inheritance
|
||||
assert_equal tags(:general), posts(:thinking).tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_going_through_join_model_with_inheritance_with_custom_class_name
|
||||
assert_equal tags(:general), posts(:thinking).funky_tags.first
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many_create_model_with_inheritance
|
||||
post = posts(:thinking)
|
||||
assert_instance_of SpecialPost, post
|
||||
|
||||
tagging = tags(:misc).taggings.create(:taggable => post)
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one_create_model_with_inheritance
|
||||
tagging = tags(:misc).create_tagging(:taggable => posts(:thinking))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_set_polymorphic_has_many
|
||||
tagging = tags(:misc).taggings.create
|
||||
posts(:thinking).taggings << tagging
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_set_polymorphic_has_one
|
||||
tagging = tags(:misc).taggings.create
|
||||
posts(:thinking).tagging = tagging
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
end
|
||||
|
||||
def test_create_polymorphic_has_many_with_scope
|
||||
old_count = posts(:welcome).taggings.count
|
||||
tagging = posts(:welcome).taggings.create(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_create_bang_polymorphic_with_has_many_scope
|
||||
old_count = posts(:welcome).taggings.count
|
||||
tagging = posts(:welcome).taggings.create!(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_create_polymorphic_has_one_with_scope
|
||||
old_count = Tagging.count
|
||||
tagging = posts(:welcome).tagging.create(:tag => tags(:misc))
|
||||
assert_equal "Post", tagging.taggable_type
|
||||
assert_equal old_count+1, Tagging.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_delete_all
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDeleteAll'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :delete_all)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_destroy
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyDestroy'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :destroy)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_many_with_nullify
|
||||
assert_equal 1, posts(:welcome).taggings.count
|
||||
posts(:welcome).taggings.first.update_attribute :taggable_type, 'PostWithHasManyNullify'
|
||||
post = find_post_with_dependency(1, :has_many, :taggings, :nullify)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count, Tagging.count
|
||||
assert_equal 0, posts(:welcome).taggings.count
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_one_with_destroy
|
||||
assert posts(:welcome).tagging
|
||||
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneDestroy'
|
||||
post = find_post_with_dependency(1, :has_one, :tagging, :destroy)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count-1, Tagging.count
|
||||
assert_nil posts(:welcome).tagging(true)
|
||||
end
|
||||
|
||||
def test_delete_polymorphic_has_one_with_nullify
|
||||
assert posts(:welcome).tagging
|
||||
posts(:welcome).tagging.update_attribute :taggable_type, 'PostWithHasOneNullify'
|
||||
post = find_post_with_dependency(1, :has_one, :tagging, :nullify)
|
||||
|
||||
old_count = Tagging.count
|
||||
post.destroy
|
||||
assert_equal old_count, Tagging.count
|
||||
assert_nil posts(:welcome).tagging(true)
|
||||
end
|
||||
|
||||
def test_has_many_with_piggyback
|
||||
assert_equal "2", categories(:sti_test).authors.first.post_id.to_s
|
||||
end
|
||||
|
||||
def test_include_has_many_through
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_authors = Post.find(:all, :include => :authors, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_authors.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].authors.length, assert_no_queries { posts_with_authors[i].authors.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_one
|
||||
post = Post.find_by_id(posts(:welcome).id, :include => :tagging)
|
||||
tagging = taggings(:welcome_general)
|
||||
assert_no_queries do
|
||||
assert_equal tagging, post.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_one_defined_in_abstract_parent
|
||||
item = Item.find_by_id(items(:dvd).id, :include => :tagging)
|
||||
tagging = taggings(:godfather)
|
||||
assert_no_queries do
|
||||
assert_equal tagging, item.tagging
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_many_through
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_tags.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_include_polymorphic_has_many
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_taggings.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_find_all
|
||||
assert_equal [categories(:general)], authors(:david).categories.find(:all)
|
||||
end
|
||||
|
||||
def test_has_many_find_first
|
||||
assert_equal categories(:general), authors(:david).categories.find(:first)
|
||||
end
|
||||
|
||||
def test_has_many_with_hash_conditions
|
||||
assert_equal categories(:general), authors(:david).categories_like_general.find(:first)
|
||||
end
|
||||
|
||||
def test_has_many_find_conditions
|
||||
assert_equal categories(:general), authors(:david).categories.find(:first, :conditions => "categories.name = 'General'")
|
||||
assert_equal nil, authors(:david).categories.find(:first, :conditions => "categories.name = 'Technology'")
|
||||
end
|
||||
|
||||
def test_has_many_class_methods_called_by_method_missing
|
||||
assert_equal categories(:general), authors(:david).categories.find_all_by_name('General').first
|
||||
assert_equal nil, authors(:david).categories.find_by_name('Technology')
|
||||
end
|
||||
|
||||
def test_has_many_array_methods_called_by_method_missing
|
||||
assert true, authors(:david).categories.any? { |category| category.name == 'General' }
|
||||
assert_nothing_raised { authors(:david).categories.sort }
|
||||
end
|
||||
|
||||
def test_has_many_going_through_join_model_with_custom_foreign_key
|
||||
assert_equal [], posts(:thinking).authors
|
||||
assert_equal [authors(:mary)], posts(:authorless).authors
|
||||
end
|
||||
|
||||
def test_both_scoped_and_explicit_joins_should_be_respected
|
||||
assert_nothing_raised do
|
||||
Post.send(:with_scope, :find => {:joins => "left outer join comments on comments.id = posts.id"}) do
|
||||
Post.find :all, :select => "comments.id, authors.id", :joins => "left outer join authors on authors.id = posts.author_id"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_belongs_to_polymorphic_with_counter_cache
|
||||
assert_equal 1, posts(:welcome)[:taggings_count]
|
||||
tagging = posts(:welcome).taggings.create(:tag => tags(:general))
|
||||
assert_equal 2, posts(:welcome, :reload)[:taggings_count]
|
||||
tagging.destroy
|
||||
assert_equal 1, posts(:welcome, :reload)[:taggings_count]
|
||||
end
|
||||
|
||||
def test_unavailable_through_reflection
|
||||
assert_raise(ActiveRecord::HasManyThroughAssociationNotFoundError) { authors(:david).nothings }
|
||||
end
|
||||
|
||||
def test_has_many_through_join_model_with_conditions
|
||||
assert_equal [], posts(:welcome).invalid_taggings
|
||||
assert_equal [], posts(:welcome).invalid_tags
|
||||
end
|
||||
|
||||
def test_has_many_polymorphic
|
||||
assert_raise ActiveRecord::HasManyThroughAssociationPolymorphicError do
|
||||
assert_equal posts(:welcome, :thinking), tags(:general).taggables
|
||||
end
|
||||
assert_raise ActiveRecord::EagerLoadPolymorphicError do
|
||||
assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_polymorphic_with_source_type
|
||||
assert_equal posts(:welcome, :thinking), tags(:general).tagged_posts
|
||||
end
|
||||
|
||||
def test_eager_has_many_polymorphic_with_source_type
|
||||
tag_with_include = Tag.find(tags(:general).id, :include => :tagged_posts)
|
||||
desired = posts(:welcome, :thinking)
|
||||
assert_no_queries do
|
||||
assert_equal desired, tag_with_include.tagged_posts
|
||||
end
|
||||
assert_equal 5, tag_with_include.taggings.length
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_all
|
||||
assert_equal comments(:greetings), authors(:david).comments.find(:all, :order => 'comments.id').first
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_all_with_custom_class
|
||||
assert_equal comments(:greetings), authors(:david).funky_comments.find(:all, :order => 'comments.id').first
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_first
|
||||
assert_equal comments(:greetings), authors(:david).comments.find(:first, :order => 'comments.id')
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_conditions
|
||||
options = { :conditions => "comments.#{QUOTED_TYPE}='SpecialComment'", :order => 'comments.id' }
|
||||
assert_equal comments(:does_it_hurt), authors(:david).comments.find(:first, options)
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_find_by_id
|
||||
assert_equal comments(:more_greetings), authors(:david).comments.find(2)
|
||||
end
|
||||
|
||||
def test_has_many_through_polymorphic_has_one
|
||||
assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).tagging
|
||||
end
|
||||
|
||||
def test_has_many_through_polymorphic_has_many
|
||||
assert_equal taggings(:welcome_general, :thinking_general), authors(:david).taggings.uniq.sort_by { |t| t.id }
|
||||
end
|
||||
|
||||
def test_include_has_many_through_polymorphic_has_many
|
||||
author = Author.find_by_id(authors(:david).id, :include => :taggings)
|
||||
expected_taggings = taggings(:welcome_general, :thinking_general)
|
||||
assert_no_queries do
|
||||
assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id }
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_through
|
||||
assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).tags }
|
||||
end
|
||||
|
||||
def test_has_many_through_habtm
|
||||
assert_raise(ActiveRecord::HasManyThroughSourceAssociationMacroError) { authors(:david).post_categories }
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_has_many
|
||||
author = Author.find :first, :conditions => ['name = ?', 'David'], :include => :comments, :order => 'comments.id'
|
||||
SpecialComment.new; VerySpecialComment.new
|
||||
assert_no_queries do
|
||||
assert_equal [1,2,3,5,6,7,8,9,10], author.comments.collect(&:id)
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_load_has_many_through_has_many_with_conditions
|
||||
post = Post.find(:first, :include => :invalid_tags)
|
||||
assert_no_queries do
|
||||
post.invalid_tags
|
||||
end
|
||||
end
|
||||
|
||||
def test_eager_belongs_to_and_has_one_not_singularized
|
||||
assert_nothing_raised do
|
||||
Author.find(:first, :include => :author_address)
|
||||
AuthorAddress.find(:first, :include => :author)
|
||||
end
|
||||
end
|
||||
|
||||
def test_self_referential_has_many_through
|
||||
assert_equal [authors(:mary)], authors(:david).favorite_authors
|
||||
assert_equal [], authors(:mary).favorite_authors
|
||||
end
|
||||
|
||||
def test_add_to_self_referential_has_many_through
|
||||
new_author = Author.create(:name => "Bob")
|
||||
authors(:david).author_favorites.create :favorite_author => new_author
|
||||
assert_equal new_author, authors(:david).reload.favorite_authors.first
|
||||
end
|
||||
|
||||
def test_has_many_through_uses_conditions_specified_on_the_has_many_association
|
||||
author = Author.find(:first)
|
||||
assert !author.comments.blank?
|
||||
assert author.nonexistant_comments.blank?
|
||||
end
|
||||
|
||||
def test_has_many_through_uses_correct_attributes
|
||||
assert_nil posts(:thinking).tags.find_by_name("General").attributes["tag_id"]
|
||||
end
|
||||
|
||||
def test_associating_unsaved_records_with_has_many_through
|
||||
saved_post = posts(:thinking)
|
||||
new_tag = Tag.new(:name => "new")
|
||||
|
||||
saved_post.tags << new_tag
|
||||
assert !new_tag.new_record? #consistent with habtm!
|
||||
assert !saved_post.new_record?
|
||||
assert saved_post.tags.include?(new_tag)
|
||||
|
||||
assert !new_tag.new_record?
|
||||
assert saved_post.reload.tags(true).include?(new_tag)
|
||||
|
||||
|
||||
new_post = Post.new(:title => "Association replacmenet works!", :body => "You best believe it.")
|
||||
saved_tag = tags(:general)
|
||||
|
||||
new_post.tags << saved_tag
|
||||
assert new_post.new_record?
|
||||
assert !saved_tag.new_record?
|
||||
assert new_post.tags.include?(saved_tag)
|
||||
|
||||
new_post.save!
|
||||
assert !new_post.new_record?
|
||||
assert new_post.reload.tags(true).include?(saved_tag)
|
||||
|
||||
assert posts(:thinking).tags.build.new_record?
|
||||
assert posts(:thinking).tags.new.new_record?
|
||||
end
|
||||
|
||||
def test_create_associate_when_adding_to_has_many_through
|
||||
count = posts(:thinking).tags.count
|
||||
push = Tag.create!(:name => 'pushme')
|
||||
post_thinking = posts(:thinking)
|
||||
assert_nothing_raised { post_thinking.tags << push }
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 1, post_thinking.tags.size)
|
||||
assert_equal(count + 1, post_thinking.tags(true).size)
|
||||
|
||||
assert_kind_of Tag, post_thinking.tags.create!(:name => 'foo')
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 2, post_thinking.tags.size)
|
||||
assert_equal(count + 2, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
|
||||
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
|
||||
message = "Expected a Tag in tags collection, got #{wrong.class}.")
|
||||
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
|
||||
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
|
||||
assert_equal(count + 4, post_thinking.tags.size)
|
||||
assert_equal(count + 4, post_thinking.tags(true).size)
|
||||
|
||||
# Raises if the wrong reflection name is used to set the Edge belongs_to
|
||||
assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
|
||||
end
|
||||
|
||||
def test_has_many_through_collection_size_doesnt_load_target_if_not_loaded
|
||||
author = authors(:david)
|
||||
assert_equal 9, author.comments.size
|
||||
assert !author.comments.loaded?
|
||||
end
|
||||
|
||||
def test_has_many_through_collection_size_uses_counter_cache_if_it_exists
|
||||
author = authors(:david)
|
||||
author.stubs(:read_attribute).with('comments_count').returns(100)
|
||||
assert_equal 100, author.comments.size
|
||||
assert !author.comments.loaded?
|
||||
end
|
||||
|
||||
def test_adding_junk_to_has_many_through_should_raise_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags << "Uhh what now?" }
|
||||
end
|
||||
|
||||
def test_adding_to_has_many_through_should_return_self
|
||||
tags = posts(:thinking).tags
|
||||
assert_equal tags, posts(:thinking).tags.push(tags(:general))
|
||||
end
|
||||
|
||||
def test_delete_associate_when_deleting_from_has_many_through_with_nonstandard_id
|
||||
count = books(:awdr).references.count
|
||||
references_before = books(:awdr).references
|
||||
book = Book.create!(:name => 'Getting Real')
|
||||
book_awdr = books(:awdr)
|
||||
book_awdr.references << book
|
||||
assert_equal(count + 1, book_awdr.references(true).size)
|
||||
|
||||
assert_nothing_raised { book_awdr.references.delete(book) }
|
||||
assert_equal(count, book_awdr.references.size)
|
||||
assert_equal(count, book_awdr.references(true).size)
|
||||
assert_equal(references_before.sort, book_awdr.references.sort)
|
||||
end
|
||||
|
||||
def test_delete_associate_when_deleting_from_has_many_through
|
||||
count = posts(:thinking).tags.count
|
||||
tags_before = posts(:thinking).tags
|
||||
tag = Tag.create!(:name => 'doomed')
|
||||
post_thinking = posts(:thinking)
|
||||
post_thinking.tags << tag
|
||||
assert_equal(count + 1, post_thinking.taggings(true).size)
|
||||
assert_equal(count + 1, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.delete(tag) }
|
||||
assert_equal(count, post_thinking.tags.size)
|
||||
assert_equal(count, post_thinking.tags(true).size)
|
||||
assert_equal(count, post_thinking.taggings(true).size)
|
||||
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
||||
end
|
||||
|
||||
def test_delete_associate_when_deleting_from_has_many_through_with_multiple_tags
|
||||
count = posts(:thinking).tags.count
|
||||
tags_before = posts(:thinking).tags
|
||||
doomed = Tag.create!(:name => 'doomed')
|
||||
doomed2 = Tag.create!(:name => 'doomed2')
|
||||
quaked = Tag.create!(:name => 'quaked')
|
||||
post_thinking = posts(:thinking)
|
||||
post_thinking.tags << doomed << doomed2
|
||||
assert_equal(count + 2, post_thinking.tags(true).size)
|
||||
|
||||
assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
|
||||
assert_equal(count, post_thinking.tags.size)
|
||||
assert_equal(count, post_thinking.tags(true).size)
|
||||
assert_equal(tags_before.sort, post_thinking.tags.sort)
|
||||
end
|
||||
|
||||
def test_deleting_junk_from_has_many_through_should_raise_type_mismatch
|
||||
assert_raise(ActiveRecord::AssociationTypeMismatch) { posts(:thinking).tags.delete("Uhh what now?") }
|
||||
end
|
||||
|
||||
def test_has_many_through_sum_uses_calculations
|
||||
assert_nothing_raised { authors(:david).comments.sum(:post_id) }
|
||||
end
|
||||
|
||||
def test_calculations_on_has_many_through_should_disambiguate_fields
|
||||
assert_nothing_raised { authors(:david).categories.maximum(:id) }
|
||||
end
|
||||
|
||||
def test_calculations_on_has_many_through_should_not_disambiguate_fields_unless_necessary
|
||||
assert_nothing_raised { authors(:david).categories.maximum("categories.id") }
|
||||
end
|
||||
|
||||
def test_has_many_through_has_many_with_sti
|
||||
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
|
||||
end
|
||||
|
||||
def test_uniq_has_many_through_should_retain_order
|
||||
comment_ids = authors(:david).comments.map(&:id)
|
||||
assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
|
||||
assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
|
||||
end
|
||||
|
||||
def test_polymorphic_has_many
|
||||
expected = taggings(:welcome_general)
|
||||
p = Post.find(posts(:welcome).id, :include => :taggings)
|
||||
assert_no_queries {assert p.taggings.include?(expected)}
|
||||
assert posts(:welcome).taggings.include?(taggings(:welcome_general))
|
||||
end
|
||||
|
||||
def test_polymorphic_has_one
|
||||
expected = posts(:welcome)
|
||||
|
||||
tagging = Tagging.find(taggings(:welcome_general).id, :include => :taggable)
|
||||
assert_no_queries { assert_equal expected, tagging.taggable}
|
||||
end
|
||||
|
||||
def test_polymorphic_belongs_to
|
||||
p = Post.find(posts(:welcome).id, :include => {:taggings => :taggable})
|
||||
assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable}
|
||||
end
|
||||
|
||||
def test_preload_polymorphic_has_many_through
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_tags.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_preload_polymorph_many_types
|
||||
taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel']
|
||||
assert_no_queries do
|
||||
taggings.first.taggable.id
|
||||
taggings[1].taggable.id
|
||||
end
|
||||
|
||||
taggables = taggings.map(&:taggable)
|
||||
assert taggables.include?(items(:dvd))
|
||||
assert taggables.include?(posts(:welcome))
|
||||
end
|
||||
|
||||
def test_preload_nil_polymorphic_belongs_to
|
||||
assert_nothing_raised do
|
||||
taggings = Tagging.find(:all, :include => :taggable, :conditions => ['taggable_type IS NULL'])
|
||||
end
|
||||
end
|
||||
|
||||
def test_preload_polymorphic_has_many
|
||||
posts = Post.find(:all, :order => 'posts.id')
|
||||
posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id')
|
||||
assert_equal posts.length, posts_with_taggings.length
|
||||
posts.length.times do |i|
|
||||
assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length }
|
||||
end
|
||||
end
|
||||
|
||||
def test_belongs_to_shared_parent
|
||||
comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 1')
|
||||
assert_no_queries do
|
||||
assert_equal comments.first.post, comments[1].post
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_include_uses_array_include_after_loaded
|
||||
david = authors(:david)
|
||||
david.categories.class # force load target
|
||||
|
||||
category = david.categories.first
|
||||
|
||||
assert_no_queries do
|
||||
assert david.categories.loaded?
|
||||
assert david.categories.include?(category)
|
||||
end
|
||||
end
|
||||
|
||||
def test_has_many_through_include_checks_if_record_exists_if_target_not_loaded
|
||||
david = authors(:david)
|
||||
category = david.categories.first
|
||||
|
||||
david.reload
|
||||
assert ! david.categories.loaded?
|
||||
assert_queries(1) do
|
||||
assert david.categories.include?(category)
|
||||
end
|
||||
assert ! david.categories.loaded?
|
||||
end
|
||||
|
||||
def test_has_many_through_include_returns_false_for_non_matching_record_to_verify_scoping
|
||||
david = authors(:david)
|
||||
category = Category.create!(:name => 'Not Associated')
|
||||
|
||||
assert ! david.categories.loaded?
|
||||
assert ! david.categories.include?(category)
|
||||
end
|
||||
|
||||
def test_has_many_through_goes_through_all_sti_classes
|
||||
sub_sti_post = SubStiPost.create!(:title => 'test', :body => 'test', :author_id => 1)
|
||||
new_comment = sub_sti_post.comments.create(:body => 'test')
|
||||
|
||||
assert_equal [9, 10, new_comment.id], authors(:david).sti_post_comments.map(&:id).sort
|
||||
end
|
||||
|
||||
private
|
||||
# create dynamic Post models to allow different dependency options
|
||||
def find_post_with_dependency(post_id, association, association_name, dependency)
|
||||
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
|
||||
Post.find(post_id).update_attribute :type, class_name
|
||||
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
|
||||
klass.set_table_name 'posts'
|
||||
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
|
||||
klass.find(post_id)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,301 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/computer'
|
||||
require 'models/customer'
|
||||
require 'models/order'
|
||||
require 'models/categorization'
|
||||
require 'models/category'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/comment'
|
||||
require 'models/tag'
|
||||
require 'models/tagging'
|
||||
require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/parrot'
|
||||
require 'models/pirate'
|
||||
require 'models/ship'
|
||||
require 'models/ship_part'
|
||||
require 'models/treasure'
|
||||
require 'models/price_estimate'
|
||||
require 'models/club'
|
||||
require 'models/member'
|
||||
require 'models/membership'
|
||||
require 'models/sponsor'
|
||||
|
||||
class AssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :developers_projects,
|
||||
:computers, :people, :readers
|
||||
|
||||
def test_loading_the_association_target_should_keep_child_records_marked_for_destruction
|
||||
ship = Ship.create!(:name => "The good ship Dollypop")
|
||||
part = ship.parts.create!(:name => "Mast")
|
||||
part.mark_for_destruction
|
||||
ship.parts.send(:load_target)
|
||||
assert ship.parts[0].marked_for_destruction?
|
||||
end
|
||||
|
||||
def test_loading_the_association_target_should_load_most_recent_attributes_for_child_records_marked_for_destruction
|
||||
ship = Ship.create!(:name => "The good ship Dollypop")
|
||||
part = ship.parts.create!(:name => "Mast")
|
||||
part.mark_for_destruction
|
||||
ShipPart.find(part.id).update_attribute(:name, 'Deck')
|
||||
ship.parts.send(:load_target)
|
||||
assert_equal 'Deck', ship.parts[0].name
|
||||
end
|
||||
|
||||
def test_include_with_order_works
|
||||
assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)}
|
||||
assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)}
|
||||
end
|
||||
|
||||
def test_bad_collection_keys
|
||||
assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do
|
||||
Class.new(ActiveRecord::Base).has_many(:wheels, :name => 'wheels')
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_construct_new_finder_sql_after_create
|
||||
person = Person.new :first_name => 'clark'
|
||||
assert_equal [], person.readers.find(:all)
|
||||
person.save!
|
||||
reader = Reader.create! :person => person, :post => Post.new(:title => "foo", :body => "bar")
|
||||
assert person.readers.find(reader.id)
|
||||
end
|
||||
|
||||
def test_force_reload
|
||||
firm = Firm.new("name" => "A New Firm, Inc")
|
||||
firm.save
|
||||
firm.clients.each {|c|} # forcing to load all clients
|
||||
assert firm.clients.empty?, "New firm shouldn't have client objects"
|
||||
assert_equal 0, firm.clients.size, "New firm should have 0 clients"
|
||||
|
||||
client = Client.new("name" => "TheClient.com", "firm_id" => firm.id)
|
||||
client.save
|
||||
|
||||
assert firm.clients.empty?, "New firm should have cached no client objects"
|
||||
assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
|
||||
|
||||
assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
|
||||
assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
|
||||
end
|
||||
|
||||
def test_force_reload_is_uncached
|
||||
firm = Firm.create!("name" => "A New Firm, Inc")
|
||||
client = Client.create!("name" => "TheClient.com", :firm => firm)
|
||||
ActiveRecord::Base.cache do
|
||||
firm.clients.each {}
|
||||
assert_queries(0) { assert_not_nil firm.clients.each {} }
|
||||
assert_queries(1) { assert_not_nil firm.clients(true).each {} }
|
||||
end
|
||||
end
|
||||
|
||||
def test_using_limitable_reflections_helper
|
||||
using_limitable_reflections = lambda { |reflections| ActiveRecord::Base.send :using_limitable_reflections?, reflections }
|
||||
belongs_to_reflections = [Tagging.reflect_on_association(:tag), Tagging.reflect_on_association(:super_tag)]
|
||||
has_many_reflections = [Tag.reflect_on_association(:taggings), Developer.reflect_on_association(:projects)]
|
||||
mixed_reflections = (belongs_to_reflections + has_many_reflections).uniq
|
||||
assert using_limitable_reflections.call(belongs_to_reflections), "Belong to associations are limitable"
|
||||
assert !using_limitable_reflections.call(has_many_reflections), "All has many style associations are not limitable"
|
||||
assert !using_limitable_reflections.call(mixed_reflections), "No collection associations (has many style) should pass"
|
||||
end
|
||||
|
||||
def test_storing_in_pstore
|
||||
require "tmpdir"
|
||||
store_filename = File.join(Dir.tmpdir, "ar-pstore-association-test")
|
||||
File.delete(store_filename) if File.exist?(store_filename)
|
||||
require "pstore"
|
||||
apple = Firm.create("name" => "Apple")
|
||||
natural = Client.new("name" => "Natural Company")
|
||||
apple.clients << natural
|
||||
|
||||
db = PStore.new(store_filename)
|
||||
db.transaction do
|
||||
db["apple"] = apple
|
||||
end
|
||||
|
||||
db = PStore.new(store_filename)
|
||||
db.transaction do
|
||||
assert_equal "Natural Company", db["apple"].clients.first.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class AssociationProxyTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :posts, :categorizations, :categories, :developers, :projects, :developers_projects
|
||||
|
||||
def test_proxy_accessors
|
||||
welcome = posts(:welcome)
|
||||
assert_equal welcome, welcome.author.proxy_owner
|
||||
assert_equal welcome.class.reflect_on_association(:author), welcome.author.proxy_reflection
|
||||
welcome.author.class # force load target
|
||||
assert_equal welcome.author, welcome.author.proxy_target
|
||||
|
||||
david = authors(:david)
|
||||
assert_equal david, david.posts.proxy_owner
|
||||
assert_equal david.class.reflect_on_association(:posts), david.posts.proxy_reflection
|
||||
david.posts.class # force load target
|
||||
assert_equal david.posts, david.posts.proxy_target
|
||||
|
||||
assert_equal david, david.posts_with_extension.testing_proxy_owner
|
||||
assert_equal david.class.reflect_on_association(:posts_with_extension), david.posts_with_extension.testing_proxy_reflection
|
||||
david.posts_with_extension.class # force load target
|
||||
assert_equal david.posts_with_extension, david.posts_with_extension.testing_proxy_target
|
||||
end
|
||||
|
||||
def test_push_does_not_load_target
|
||||
david = authors(:david)
|
||||
|
||||
david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
|
||||
assert !david.posts.loaded?
|
||||
assert david.posts.include?(post)
|
||||
end
|
||||
|
||||
def test_push_has_many_through_does_not_load_target
|
||||
david = authors(:david)
|
||||
|
||||
david.categories << categories(:technology)
|
||||
assert !david.categories.loaded?
|
||||
assert david.categories.include?(categories(:technology))
|
||||
end
|
||||
|
||||
def test_push_followed_by_save_does_not_load_target
|
||||
david = authors(:david)
|
||||
|
||||
david.posts << (post = Post.new(:title => "New on Edge", :body => "More cool stuff!"))
|
||||
assert !david.posts.loaded?
|
||||
david.save
|
||||
assert !david.posts.loaded?
|
||||
assert david.posts.include?(post)
|
||||
end
|
||||
|
||||
def test_push_does_not_lose_additions_to_new_record
|
||||
josh = Author.new(:name => "Josh")
|
||||
josh.posts << Post.new(:title => "New on Edge", :body => "More cool stuff!")
|
||||
assert josh.posts.loaded?
|
||||
assert_equal 1, josh.posts.size
|
||||
end
|
||||
|
||||
def test_save_on_parent_does_not_load_target
|
||||
david = developers(:david)
|
||||
|
||||
assert !david.projects.loaded?
|
||||
david.update_attribute(:created_at, Time.now)
|
||||
assert !david.projects.loaded?
|
||||
end
|
||||
|
||||
def test_inspect_does_not_reload_a_not_yet_loaded_target
|
||||
andreas = Developer.new :name => 'Andreas', :log => 'new developer added'
|
||||
assert !andreas.audit_logs.loaded?
|
||||
assert_match(/message: "new developer added"/, andreas.audit_logs.inspect)
|
||||
end
|
||||
|
||||
def test_save_on_parent_saves_children
|
||||
developer = Developer.create :name => "Bryan", :salary => 50_000
|
||||
assert_equal 1, developer.reload.audit_logs.size
|
||||
end
|
||||
|
||||
def test_create_via_association_with_block
|
||||
post = authors(:david).posts.create(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
|
||||
assert_equal post.title, "New on Edge"
|
||||
assert_equal post.body, "More cool stuff!"
|
||||
end
|
||||
|
||||
def test_create_with_bang_via_association_with_block
|
||||
post = authors(:david).posts.create!(:title => "New on Edge") {|p| p.body = "More cool stuff!"}
|
||||
assert_equal post.title, "New on Edge"
|
||||
assert_equal post.body, "More cool stuff!"
|
||||
end
|
||||
|
||||
def test_failed_reload_returns_nil
|
||||
p = setup_dangling_association
|
||||
assert_nil p.author.reload
|
||||
end
|
||||
|
||||
def test_failed_reset_returns_nil
|
||||
p = setup_dangling_association
|
||||
assert_nil p.author.reset
|
||||
end
|
||||
|
||||
def test_reload_returns_assocition
|
||||
david = developers(:david)
|
||||
assert_nothing_raised do
|
||||
assert_equal david.projects, david.projects.reload.reload
|
||||
end
|
||||
end
|
||||
|
||||
def setup_dangling_association
|
||||
josh = Author.create(:name => "Josh")
|
||||
p = Post.create(:title => "New on Edge", :body => "More cool stuff!", :author => josh)
|
||||
josh.destroy
|
||||
p
|
||||
end
|
||||
end
|
||||
|
||||
class OverridingAssociationsTest < ActiveRecord::TestCase
|
||||
class Person < ActiveRecord::Base; end
|
||||
class DifferentPerson < ActiveRecord::Base; end
|
||||
|
||||
class PeopleList < ActiveRecord::Base
|
||||
has_and_belongs_to_many :has_and_belongs_to_many, :before_add => :enlist
|
||||
has_many :has_many, :before_add => :enlist
|
||||
belongs_to :belongs_to
|
||||
has_one :has_one
|
||||
end
|
||||
|
||||
class DifferentPeopleList < PeopleList
|
||||
# Different association with the same name, callbacks should be omitted here.
|
||||
has_and_belongs_to_many :has_and_belongs_to_many, :class_name => 'DifferentPerson'
|
||||
has_many :has_many, :class_name => 'DifferentPerson'
|
||||
belongs_to :belongs_to, :class_name => 'DifferentPerson'
|
||||
has_one :has_one, :class_name => 'DifferentPerson'
|
||||
end
|
||||
|
||||
def test_habtm_association_redefinition_callbacks_should_differ_and_not_inherited
|
||||
# redeclared association on AR descendant should not inherit callbacks from superclass
|
||||
callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
|
||||
assert_equal([:enlist], callbacks)
|
||||
callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_and_belongs_to_many)
|
||||
assert_equal([], callbacks)
|
||||
end
|
||||
|
||||
def test_has_many_association_redefinition_callbacks_should_differ_and_not_inherited
|
||||
# redeclared association on AR descendant should not inherit callbacks from superclass
|
||||
callbacks = PeopleList.read_inheritable_attribute(:before_add_for_has_many)
|
||||
assert_equal([:enlist], callbacks)
|
||||
callbacks = DifferentPeopleList.read_inheritable_attribute(:before_add_for_has_many)
|
||||
assert_equal([], callbacks)
|
||||
end
|
||||
|
||||
def test_habtm_association_redefinition_reflections_should_differ_and_not_inherited
|
||||
assert_not_equal(
|
||||
PeopleList.reflect_on_association(:has_and_belongs_to_many),
|
||||
DifferentPeopleList.reflect_on_association(:has_and_belongs_to_many)
|
||||
)
|
||||
end
|
||||
|
||||
def test_has_many_association_redefinition_reflections_should_differ_and_not_inherited
|
||||
assert_not_equal(
|
||||
PeopleList.reflect_on_association(:has_many),
|
||||
DifferentPeopleList.reflect_on_association(:has_many)
|
||||
)
|
||||
end
|
||||
|
||||
def test_belongs_to_association_redefinition_reflections_should_differ_and_not_inherited
|
||||
assert_not_equal(
|
||||
PeopleList.reflect_on_association(:belongs_to),
|
||||
DifferentPeopleList.reflect_on_association(:belongs_to)
|
||||
)
|
||||
end
|
||||
|
||||
def test_has_one_association_redefinition_reflections_should_differ_and_not_inherited
|
||||
assert_not_equal(
|
||||
PeopleList.reflect_on_association(:has_one),
|
||||
DifferentPeopleList.reflect_on_association(:has_one)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,305 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/minimalistic'
|
||||
|
||||
class AttributeMethodsTest < ActiveRecord::TestCase
|
||||
fixtures :topics
|
||||
def setup
|
||||
@old_suffixes = ActiveRecord::Base.send(:attribute_method_suffixes).dup
|
||||
@target = Class.new(ActiveRecord::Base)
|
||||
@target.table_name = 'topics'
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.send(:attribute_method_suffixes).clear
|
||||
ActiveRecord::Base.attribute_method_suffix *@old_suffixes
|
||||
end
|
||||
|
||||
def test_match_attribute_method_query_returns_match_data
|
||||
assert_not_nil md = @target.match_attribute_method?('title=')
|
||||
assert_equal 'title', md.pre_match
|
||||
assert_equal ['='], md.captures
|
||||
|
||||
%w(_hello_world ist! _maybe?).each do |suffix|
|
||||
@target.class_eval "def attribute#{suffix}(*args) args end"
|
||||
@target.attribute_method_suffix suffix
|
||||
|
||||
assert_not_nil md = @target.match_attribute_method?("title#{suffix}")
|
||||
assert_equal 'title', md.pre_match
|
||||
assert_equal [suffix], md.captures
|
||||
end
|
||||
end
|
||||
|
||||
def test_declared_attribute_method_affects_respond_to_and_method_missing
|
||||
topic = @target.new(:title => 'Budget')
|
||||
assert topic.respond_to?('title')
|
||||
assert_equal 'Budget', topic.title
|
||||
assert !topic.respond_to?('title_hello_world')
|
||||
assert_raise(NoMethodError) { topic.title_hello_world }
|
||||
|
||||
%w(_hello_world _it! _candidate= able?).each do |suffix|
|
||||
@target.class_eval "def attribute#{suffix}(*args) args end"
|
||||
@target.attribute_method_suffix suffix
|
||||
|
||||
meth = "title#{suffix}"
|
||||
assert topic.respond_to?(meth)
|
||||
assert_equal ['title'], topic.send(meth)
|
||||
assert_equal ['title', 'a'], topic.send(meth, 'a')
|
||||
assert_equal ['title', 1, 2, 3], topic.send(meth, 1, 2, 3)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_unserialize_attributes_for_frozen_records
|
||||
myobj = {:value1 => :value2}
|
||||
topic = Topic.create("content" => myobj)
|
||||
topic.freeze
|
||||
assert_equal myobj, topic.content
|
||||
end
|
||||
|
||||
def test_typecast_attribute_from_select_to_false
|
||||
topic = Topic.create(:title => 'Budget')
|
||||
topic = Topic.find(:first, :select => "topics.*, 1=2 as is_test")
|
||||
assert !topic.is_test?
|
||||
end
|
||||
|
||||
def test_typecast_attribute_from_select_to_true
|
||||
topic = Topic.create(:title => 'Budget')
|
||||
topic = Topic.find(:first, :select => "topics.*, 2=2 as is_test")
|
||||
assert topic.is_test?
|
||||
end
|
||||
|
||||
def test_kernel_methods_not_implemented_in_activerecord
|
||||
%w(test name display y).each do |method|
|
||||
assert !ActiveRecord::Base.instance_method_already_implemented?(method), "##{method} is defined"
|
||||
end
|
||||
end
|
||||
|
||||
def test_primary_key_implemented
|
||||
assert Class.new(ActiveRecord::Base).instance_method_already_implemented?('id')
|
||||
end
|
||||
|
||||
def test_defined_kernel_methods_implemented_in_model
|
||||
%w(test name display y).each do |method|
|
||||
klass = Class.new ActiveRecord::Base
|
||||
klass.class_eval "def #{method}() 'defined #{method}' end"
|
||||
assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
|
||||
end
|
||||
end
|
||||
|
||||
def test_defined_kernel_methods_implemented_in_model_abstract_subclass
|
||||
%w(test name display y).each do |method|
|
||||
abstract = Class.new ActiveRecord::Base
|
||||
abstract.class_eval "def #{method}() 'defined #{method}' end"
|
||||
abstract.abstract_class = true
|
||||
klass = Class.new abstract
|
||||
assert klass.instance_method_already_implemented?(method), "##{method} is not defined"
|
||||
end
|
||||
end
|
||||
|
||||
def test_raises_dangerous_attribute_error_when_defining_activerecord_method_in_model
|
||||
%w(save create_or_update).each do |method|
|
||||
klass = Class.new ActiveRecord::Base
|
||||
klass.class_eval "def #{method}() 'defined #{method}' end"
|
||||
assert_raise ActiveRecord::DangerousAttributeError do
|
||||
klass.instance_method_already_implemented?(method)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_time_related_columns_are_meant_to_be_cached_by_default
|
||||
expected = %w(datetime timestamp time date).sort
|
||||
assert_equal expected, ActiveRecord::Base.attribute_types_cached_by_default.map(&:to_s).sort
|
||||
end
|
||||
|
||||
def test_declaring_attributes_as_cached_adds_them_to_the_attributes_cached_by_default
|
||||
default_attributes = Topic.cached_attributes
|
||||
Topic.cache_attributes :replies_count
|
||||
expected = default_attributes + ["replies_count"]
|
||||
assert_equal expected.sort, Topic.cached_attributes.sort
|
||||
Topic.instance_variable_set "@cached_attributes", nil
|
||||
end
|
||||
|
||||
def test_time_related_columns_are_actually_cached
|
||||
column_types = %w(datetime timestamp time date).map(&:to_sym)
|
||||
column_names = Topic.columns.select{|c| column_types.include?(c.type) }.map(&:name)
|
||||
|
||||
assert_equal column_names.sort, Topic.cached_attributes.sort
|
||||
assert_equal time_related_columns_on_topic.sort, Topic.cached_attributes.sort
|
||||
end
|
||||
|
||||
def test_accessing_cached_attributes_caches_the_converted_values_and_nothing_else
|
||||
t = topics(:first)
|
||||
cache = t.instance_variable_get "@attributes_cache"
|
||||
|
||||
assert_not_nil cache
|
||||
assert cache.empty?
|
||||
|
||||
all_columns = Topic.columns.map(&:name)
|
||||
cached_columns = time_related_columns_on_topic
|
||||
uncached_columns = all_columns - cached_columns
|
||||
|
||||
all_columns.each do |attr_name|
|
||||
attribute_gets_cached = Topic.cache_attribute?(attr_name)
|
||||
val = t.send attr_name unless attr_name == "type"
|
||||
if attribute_gets_cached
|
||||
assert cached_columns.include?(attr_name)
|
||||
assert_equal val, cache[attr_name]
|
||||
else
|
||||
assert uncached_columns.include?(attr_name)
|
||||
assert !cache.include?(attr_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_time_attributes_are_retrieved_in_current_time_zone
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
utc_time = Time.utc(2008, 1, 1)
|
||||
record = @target.new
|
||||
record[:written_on] = utc_time
|
||||
assert_equal utc_time, record.written_on # record.written on is equal to (i.e., simultaneous with) utc_time
|
||||
assert_kind_of ActiveSupport::TimeWithZone, record.written_on # but is a TimeWithZone
|
||||
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone # and is in the current Time.zone
|
||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time # and represents time values adjusted accordingly
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_to_utc
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
utc_time = Time.utc(2008, 1, 1)
|
||||
record = @target.new
|
||||
record.written_on = utc_time
|
||||
assert_equal utc_time, record.written_on
|
||||
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_in_other_time_zone
|
||||
utc_time = Time.utc(2008, 1, 1)
|
||||
cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
record = @target.new
|
||||
record.written_on = cst_time
|
||||
assert_equal utc_time, record.written_on
|
||||
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_with_string
|
||||
utc_time = Time.utc(2008, 1, 1)
|
||||
(-11..13).each do |timezone_offset|
|
||||
time_string = utc_time.in_time_zone(timezone_offset).to_s
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
record = @target.new
|
||||
record.written_on = time_string
|
||||
assert_equal Time.zone.parse(time_string), record.written_on
|
||||
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_to_blank_string_returns_nil
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
record = @target.new
|
||||
record.written_on = ' '
|
||||
assert_nil record.written_on
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_interprets_time_zone_unaware_string_in_time_zone
|
||||
time_string = 'Tue Jan 01 00:00:00 2008'
|
||||
(-11..13).each do |timezone_offset|
|
||||
in_time_zone timezone_offset do
|
||||
record = @target.new
|
||||
record.written_on = time_string
|
||||
assert_equal Time.zone.parse(time_string), record.written_on
|
||||
assert_equal ActiveSupport::TimeZone[timezone_offset], record.written_on.time_zone
|
||||
assert_equal Time.utc(2008, 1, 1), record.written_on.time
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_aware_attribute_in_current_time_zone
|
||||
utc_time = Time.utc(2008, 1, 1)
|
||||
in_time_zone "Pacific Time (US & Canada)" do
|
||||
record = @target.new
|
||||
record.written_on = utc_time.in_time_zone
|
||||
assert_equal utc_time, record.written_on
|
||||
assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record.written_on.time_zone
|
||||
assert_equal Time.utc(2007, 12, 31, 16), record.written_on.time
|
||||
end
|
||||
end
|
||||
|
||||
def test_setting_time_zone_conversion_for_attributes_should_write_value_on_class_variable
|
||||
Topic.skip_time_zone_conversion_for_attributes = [:field_a]
|
||||
Minimalistic.skip_time_zone_conversion_for_attributes = [:field_b]
|
||||
|
||||
assert_equal [:field_a], Topic.skip_time_zone_conversion_for_attributes
|
||||
assert_equal [:field_b], Minimalistic.skip_time_zone_conversion_for_attributes
|
||||
end
|
||||
|
||||
def test_read_attributes_respect_access_control
|
||||
privatize("title")
|
||||
|
||||
topic = @target.new(:title => "The pros and cons of programming naked.")
|
||||
assert !topic.respond_to?(:title)
|
||||
exception = assert_raise(NoMethodError) { topic.title }
|
||||
assert_equal "Attempt to call private method", exception.message
|
||||
assert_equal "I'm private", topic.send(:title)
|
||||
end
|
||||
|
||||
def test_write_attributes_respect_access_control
|
||||
privatize("title=(value)")
|
||||
|
||||
topic = @target.new
|
||||
assert !topic.respond_to?(:title=)
|
||||
exception = assert_raise(NoMethodError) { topic.title = "Pants"}
|
||||
assert_equal "Attempt to call private method", exception.message
|
||||
topic.send(:title=, "Very large pants")
|
||||
end
|
||||
|
||||
def test_question_attributes_respect_access_control
|
||||
privatize("title?")
|
||||
|
||||
topic = @target.new(:title => "Isaac Newton's pants")
|
||||
assert !topic.respond_to?(:title?)
|
||||
exception = assert_raise(NoMethodError) { topic.title? }
|
||||
assert_equal "Attempt to call private method", exception.message
|
||||
assert topic.send(:title?)
|
||||
end
|
||||
|
||||
def test_bulk_update_respects_access_control
|
||||
privatize("title=(value)")
|
||||
|
||||
assert_raise(ActiveRecord::UnknownAttributeError) { topic = @target.new(:title => "Rants about pants") }
|
||||
assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
|
||||
end
|
||||
|
||||
private
|
||||
def time_related_columns_on_topic
|
||||
Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
|
||||
end
|
||||
|
||||
def in_time_zone(zone)
|
||||
old_zone = Time.zone
|
||||
old_tz = ActiveRecord::Base.time_zone_aware_attributes
|
||||
|
||||
Time.zone = zone ? ActiveSupport::TimeZone[zone] : nil
|
||||
ActiveRecord::Base.time_zone_aware_attributes = !zone.nil?
|
||||
yield
|
||||
ensure
|
||||
Time.zone = old_zone
|
||||
ActiveRecord::Base.time_zone_aware_attributes = old_tz
|
||||
end
|
||||
|
||||
def privatize(method_signature)
|
||||
@target.class_eval <<-private_method
|
||||
private
|
||||
def #{method_signature}
|
||||
"I'm private"
|
||||
end
|
||||
private_method
|
||||
end
|
||||
end
|
||||
File diff suppressed because it is too large
Load diff
2143
vendor/rails/activerecord/test/cases/base_test.rb
vendored
2143
vendor/rails/activerecord/test/cases/base_test.rb
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,81 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/post'
|
||||
|
||||
class EachTest < ActiveRecord::TestCase
|
||||
fixtures :posts
|
||||
|
||||
def setup
|
||||
@posts = Post.all(:order => "id asc")
|
||||
@total = Post.count
|
||||
end
|
||||
|
||||
def test_each_should_excecute_one_query_per_batch
|
||||
assert_queries(Post.count + 1) do
|
||||
Post.find_each(:batch_size => 1) do |post|
|
||||
assert_kind_of Post, post
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_should_raise_if_the_order_is_set
|
||||
assert_raise(RuntimeError) do
|
||||
Post.find_each(:order => "title") { |post| post }
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_should_raise_if_the_limit_is_set
|
||||
assert_raise(RuntimeError) do
|
||||
Post.find_each(:limit => 1) { |post| post }
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_in_batches_should_return_batches
|
||||
assert_queries(Post.count + 1) do
|
||||
Post.find_in_batches(:batch_size => 1) do |batch|
|
||||
assert_kind_of Array, batch
|
||||
assert_kind_of Post, batch.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_in_batches_should_start_from_the_start_option
|
||||
assert_queries(Post.count) do
|
||||
Post.find_in_batches(:batch_size => 1, :start => 2) do |batch|
|
||||
assert_kind_of Array, batch
|
||||
assert_kind_of Post, batch.first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_in_batches_shouldnt_excute_query_unless_needed
|
||||
post_count = Post.count
|
||||
|
||||
assert_queries(2) do
|
||||
Post.find_in_batches(:batch_size => post_count) {|batch| assert_kind_of Array, batch }
|
||||
end
|
||||
|
||||
assert_queries(1) do
|
||||
Post.find_in_batches(:batch_size => post_count + 1) {|batch| assert_kind_of Array, batch }
|
||||
end
|
||||
end
|
||||
|
||||
def test_find_in_batches_doesnt_clog_conditions
|
||||
Post.find_in_batches(:conditions => {:id => posts(:welcome).id}) do
|
||||
assert_nothing_raised { Post.find(posts(:thinking).id) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_should_raise_if_select_is_set_without_id
|
||||
assert_raise(RuntimeError) do
|
||||
Post.find_each(:select => :title, :batch_size => 1) { |post| post }
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_should_execute_if_id_is_in_select
|
||||
assert_queries(4) do
|
||||
Post.find_each(:select => "id, title, type", :batch_size => 2) do |post|
|
||||
assert_kind_of Post, post
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
# Without using prepared statements, it makes no sense to test
|
||||
# BLOB data with DB2 or Firebird, because the length of a statement
|
||||
# is limited to 32KB.
|
||||
unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
|
||||
require 'models/binary'
|
||||
|
||||
class BinaryTest < ActiveRecord::TestCase
|
||||
FIXTURES = %w(flowers.jpg example.log)
|
||||
|
||||
def test_load_save
|
||||
Binary.delete_all
|
||||
|
||||
FIXTURES.each do |filename|
|
||||
data = File.read(ASSETS_ROOT + "/#{filename}")
|
||||
data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
|
||||
data.freeze
|
||||
|
||||
bin = Binary.new(:data => data)
|
||||
assert_equal data, bin.data, 'Newly assigned data differs from original'
|
||||
|
||||
bin.save!
|
||||
assert_equal data, bin.data, 'Data differs from original after save'
|
||||
|
||||
assert_equal data, bin.reload.data, 'Reloaded data differs from original'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,347 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/company'
|
||||
require 'models/topic'
|
||||
require 'models/edge'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
require 'models/toy'
|
||||
require 'models/club'
|
||||
require 'models/organization'
|
||||
|
||||
Company.has_many :accounts
|
||||
|
||||
class NumericData < ActiveRecord::Base
|
||||
self.table_name = 'numeric_data'
|
||||
end
|
||||
|
||||
class CalculationsTest < ActiveRecord::TestCase
|
||||
fixtures :companies, :accounts, :topics, :owners, :pets, :toys
|
||||
|
||||
def test_should_sum_field
|
||||
assert_equal 318, Account.sum(:credit_limit)
|
||||
end
|
||||
|
||||
def test_should_average_field
|
||||
value = Account.average(:credit_limit)
|
||||
assert_equal 53.0, value
|
||||
end
|
||||
|
||||
def test_should_return_nil_as_average
|
||||
assert_nil NumericData.average(:bank_balance)
|
||||
end
|
||||
|
||||
def test_type_cast_calculated_value_should_convert_db_averages_of_fixnum_class_to_decimal
|
||||
assert_equal 0, NumericData.send(:type_cast_calculated_value, 0, nil, 'avg')
|
||||
assert_equal 53.0, NumericData.send(:type_cast_calculated_value, 53, nil, 'avg')
|
||||
end
|
||||
|
||||
def test_should_get_maximum_of_field
|
||||
assert_equal 60, Account.maximum(:credit_limit)
|
||||
end
|
||||
|
||||
def test_should_get_maximum_of_field_with_include
|
||||
assert_equal 50, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
|
||||
end
|
||||
|
||||
def test_should_get_maximum_of_field_with_scoped_include
|
||||
Account.with_scope :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
|
||||
assert_equal 50, Account.maximum(:credit_limit)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_get_minimum_of_field
|
||||
assert_equal 50, Account.minimum(:credit_limit)
|
||||
end
|
||||
|
||||
def test_should_group_by_field
|
||||
c = Account.sum(:credit_limit, :group => :firm_id)
|
||||
[1,6,2].each { |firm_id| assert c.keys.include?(firm_id) }
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field
|
||||
c = Account.sum(:credit_limit, :group => :firm_id)
|
||||
assert_equal 50, c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_order_by_grouped_field
|
||||
c = Account.sum(:credit_limit, :group => :firm_id, :order => "firm_id")
|
||||
assert_equal [1, 2, 6, 9], c.keys.compact
|
||||
end
|
||||
|
||||
def test_should_order_by_calculation
|
||||
c = Account.sum(:credit_limit, :group => :firm_id, :order => "sum_credit_limit desc, firm_id")
|
||||
assert_equal [105, 60, 53, 50, 50], c.keys.collect { |k| c[k] }
|
||||
assert_equal [6, 2, 9, 1], c.keys.compact
|
||||
end
|
||||
|
||||
def test_should_limit_calculation
|
||||
c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
|
||||
:group => :firm_id, :order => "firm_id", :limit => 2)
|
||||
assert_equal [1, 2], c.keys.compact
|
||||
end
|
||||
|
||||
def test_should_limit_calculation_with_offset
|
||||
c = Account.sum(:credit_limit, :conditions => "firm_id IS NOT NULL",
|
||||
:group => :firm_id, :order => "firm_id", :limit => 2, :offset => 1)
|
||||
assert_equal [2, 6], c.keys.compact
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_having_condition
|
||||
c = Account.sum(:credit_limit, :group => :firm_id,
|
||||
:having => 'sum(credit_limit) > 50')
|
||||
assert_nil c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_having_sanitized_condition
|
||||
c = Account.sum(:credit_limit, :group => :firm_id,
|
||||
:having => ['sum(credit_limit) > ?', 50])
|
||||
assert_nil c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_association
|
||||
c = Account.sum(:credit_limit, :group => :firm)
|
||||
assert_equal 50, c[companies(:first_firm)]
|
||||
assert_equal 105, c[companies(:rails_core)]
|
||||
assert_equal 60, c[companies(:first_client)]
|
||||
end
|
||||
|
||||
def test_should_sum_field_with_conditions
|
||||
assert_equal 105, Account.sum(:credit_limit, :conditions => 'firm_id = 6')
|
||||
end
|
||||
|
||||
def test_should_return_zero_if_sum_conditions_return_nothing
|
||||
assert_equal 0, Account.sum(:credit_limit, :conditions => '1 = 2')
|
||||
assert_equal 0, companies(:rails_core).companies.sum(:id, :conditions => '1 = 2')
|
||||
end
|
||||
|
||||
def test_sum_should_return_valid_values_for_decimals
|
||||
NumericData.create(:bank_balance => 19.83)
|
||||
assert_equal 19.83, NumericData.sum(:bank_balance)
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_with_conditions
|
||||
c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
|
||||
:group => :firm_id)
|
||||
assert_nil c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_with_conditions_and_having
|
||||
c = Account.sum(:credit_limit, :conditions => 'firm_id > 1',
|
||||
:group => :firm_id,
|
||||
:having => 'sum(credit_limit) > 60')
|
||||
assert_nil c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_nil c[2]
|
||||
end
|
||||
|
||||
def test_should_group_by_fields_with_table_alias
|
||||
c = Account.sum(:credit_limit, :group => 'accounts.firm_id')
|
||||
assert_equal 50, c[1]
|
||||
assert_equal 105, c[6]
|
||||
assert_equal 60, c[2]
|
||||
end
|
||||
|
||||
def test_should_calculate_with_invalid_field
|
||||
assert_equal 6, Account.calculate(:count, '*')
|
||||
assert_equal 6, Account.calculate(:count, :all)
|
||||
end
|
||||
|
||||
def test_should_calculate_grouped_with_invalid_field
|
||||
c = Account.count(:all, :group => 'accounts.firm_id')
|
||||
assert_equal 1, c[1]
|
||||
assert_equal 2, c[6]
|
||||
assert_equal 1, c[2]
|
||||
end
|
||||
|
||||
def test_should_calculate_grouped_association_with_invalid_field
|
||||
c = Account.count(:all, :group => :firm)
|
||||
assert_equal 1, c[companies(:first_firm)]
|
||||
assert_equal 2, c[companies(:rails_core)]
|
||||
assert_equal 1, c[companies(:first_client)]
|
||||
end
|
||||
|
||||
def test_should_group_by_association_with_non_numeric_foreign_key
|
||||
ActiveRecord::Base.connection.expects(:select_all).returns([{"count_all" => 1, "firm_id" => "ABC"}])
|
||||
|
||||
firm = mock()
|
||||
firm.expects(:id).returns("ABC")
|
||||
firm.expects(:class).returns(Firm)
|
||||
Company.expects(:find).with(["ABC"]).returns([firm])
|
||||
|
||||
column = mock()
|
||||
column.expects(:name).at_least_once.returns(:firm_id)
|
||||
column.expects(:type_cast).with("ABC").returns("ABC")
|
||||
Account.expects(:columns).at_least_once.returns([column])
|
||||
|
||||
c = Account.count(:all, :group => :firm)
|
||||
first_key = c.keys.first
|
||||
assert_equal Firm, first_key.class
|
||||
assert_equal 1, c[first_key]
|
||||
end
|
||||
|
||||
def test_should_calculate_grouped_association_with_foreign_key_option
|
||||
Account.belongs_to :another_firm, :class_name => 'Firm', :foreign_key => 'firm_id'
|
||||
c = Account.count(:all, :group => :another_firm)
|
||||
assert_equal 1, c[companies(:first_firm)]
|
||||
assert_equal 2, c[companies(:rails_core)]
|
||||
assert_equal 1, c[companies(:first_client)]
|
||||
end
|
||||
|
||||
def test_should_not_modify_options_when_using_includes
|
||||
options = {:conditions => 'companies.id > 1', :include => :firm}
|
||||
options_copy = options.dup
|
||||
|
||||
Account.count(:all, options)
|
||||
assert_equal options_copy, options
|
||||
end
|
||||
|
||||
def test_should_calculate_grouped_by_function
|
||||
c = Company.count(:all, :group => "UPPER(#{QUOTED_TYPE})")
|
||||
assert_equal 2, c[nil]
|
||||
assert_equal 1, c['DEPENDENTFIRM']
|
||||
assert_equal 3, c['CLIENT']
|
||||
assert_equal 2, c['FIRM']
|
||||
end
|
||||
|
||||
def test_should_calculate_grouped_by_function_with_table_alias
|
||||
c = Company.count(:all, :group => "UPPER(companies.#{QUOTED_TYPE})")
|
||||
assert_equal 2, c[nil]
|
||||
assert_equal 1, c['DEPENDENTFIRM']
|
||||
assert_equal 3, c['CLIENT']
|
||||
assert_equal 2, c['FIRM']
|
||||
end
|
||||
|
||||
def test_should_not_overshadow_enumerable_sum
|
||||
assert_equal 6, [1, 2, 3].sum(&:abs)
|
||||
end
|
||||
|
||||
def test_should_sum_scoped_field
|
||||
assert_equal 15, companies(:rails_core).companies.sum(:id)
|
||||
end
|
||||
|
||||
def test_should_sum_scoped_field_with_from
|
||||
assert_equal Club.count, Organization.clubs.count
|
||||
end
|
||||
|
||||
def test_should_sum_scoped_field_with_conditions
|
||||
assert_equal 8, companies(:rails_core).companies.sum(:id, :conditions => 'id > 7')
|
||||
end
|
||||
|
||||
def test_should_group_by_scoped_field
|
||||
c = companies(:rails_core).companies.sum(:id, :group => :name)
|
||||
assert_equal 7, c['Leetsoft']
|
||||
assert_equal 8, c['Jadedpixel']
|
||||
end
|
||||
|
||||
def test_should_group_by_summed_field_with_conditions_and_having
|
||||
c = companies(:rails_core).companies.sum(:id, :group => :name,
|
||||
:having => 'sum(id) > 7')
|
||||
assert_nil c['Leetsoft']
|
||||
assert_equal 8, c['Jadedpixel']
|
||||
end
|
||||
|
||||
def test_should_reject_invalid_options
|
||||
assert_nothing_raised do
|
||||
[:count, :sum].each do |func|
|
||||
# empty options are valid
|
||||
Company.send(:validate_calculation_options, func)
|
||||
# these options are valid for all calculations
|
||||
[:select, :conditions, :joins, :order, :group, :having, :distinct].each do |opt|
|
||||
Company.send(:validate_calculation_options, func, opt => true)
|
||||
end
|
||||
end
|
||||
|
||||
# :include is only valid on :count
|
||||
Company.send(:validate_calculation_options, :count, :include => true)
|
||||
end
|
||||
|
||||
assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :sum, :foo => :bar) }
|
||||
assert_raise(ArgumentError) { Company.send(:validate_calculation_options, :count, :foo => :bar) }
|
||||
end
|
||||
|
||||
def test_should_count_selected_field_with_include
|
||||
assert_equal 6, Account.count(:distinct => true, :include => :firm)
|
||||
assert_equal 4, Account.count(:distinct => true, :include => :firm, :select => :credit_limit)
|
||||
end
|
||||
|
||||
def test_should_count_manual_select_with_include
|
||||
assert_equal 6, Account.count(:select => "DISTINCT accounts.id", :include => :firm)
|
||||
end
|
||||
|
||||
def test_count_with_column_parameter
|
||||
assert_equal 5, Account.count(:firm_id)
|
||||
end
|
||||
|
||||
def test_count_with_column_and_options_parameter
|
||||
assert_equal 2, Account.count(:firm_id, :conditions => "credit_limit = 50")
|
||||
end
|
||||
|
||||
def test_count_with_no_parameters_isnt_deprecated
|
||||
assert_not_deprecated { Account.count }
|
||||
end
|
||||
|
||||
def test_count_with_too_many_parameters_raises
|
||||
assert_raise(ArgumentError) { Account.count(1, 2, 3) }
|
||||
end
|
||||
|
||||
def test_count_with_scoped_has_many_through_association
|
||||
assert_equal 1, owners(:blackbeard).toys.with_name('Bone').count
|
||||
end
|
||||
|
||||
def test_should_sum_expression
|
||||
assert_equal 636, Account.sum("2 * credit_limit").to_i
|
||||
end
|
||||
|
||||
def test_count_with_from_option
|
||||
assert_equal Company.count(:all), Company.count(:all, :from => 'companies')
|
||||
assert_equal Account.count(:all, :conditions => "credit_limit = 50"),
|
||||
Account.count(:all, :from => 'accounts', :conditions => "credit_limit = 50")
|
||||
assert_equal Company.count(:type, :conditions => {:type => "Firm"}),
|
||||
Company.count(:type, :conditions => {:type => "Firm"}, :from => 'companies')
|
||||
end
|
||||
|
||||
def test_sum_with_from_option
|
||||
assert_equal Account.sum(:credit_limit), Account.sum(:credit_limit, :from => 'accounts')
|
||||
assert_equal Account.sum(:credit_limit, :conditions => "credit_limit > 50"),
|
||||
Account.sum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
|
||||
end
|
||||
|
||||
def test_average_with_from_option
|
||||
assert_equal Account.average(:credit_limit), Account.average(:credit_limit, :from => 'accounts')
|
||||
assert_equal Account.average(:credit_limit, :conditions => "credit_limit > 50"),
|
||||
Account.average(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
|
||||
end
|
||||
|
||||
def test_minimum_with_from_option
|
||||
assert_equal Account.minimum(:credit_limit), Account.minimum(:credit_limit, :from => 'accounts')
|
||||
assert_equal Account.minimum(:credit_limit, :conditions => "credit_limit > 50"),
|
||||
Account.minimum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
|
||||
end
|
||||
|
||||
def test_maximum_with_from_option
|
||||
assert_equal Account.maximum(:credit_limit), Account.maximum(:credit_limit, :from => 'accounts')
|
||||
assert_equal Account.maximum(:credit_limit, :conditions => "credit_limit > 50"),
|
||||
Account.maximum(:credit_limit, :from => 'accounts', :conditions => "credit_limit > 50")
|
||||
end
|
||||
|
||||
def test_from_option_with_specified_index
|
||||
if Edge.connection.adapter_name == 'MySQL'
|
||||
assert_equal Edge.count(:all), Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)')
|
||||
assert_equal Edge.count(:all, :conditions => 'sink_id < 5'),
|
||||
Edge.count(:all, :from => 'edges USE INDEX(unique_edge_index)', :conditions => 'sink_id < 5')
|
||||
end
|
||||
end
|
||||
|
||||
def test_from_option_with_table_different_than_class
|
||||
assert_equal Account.count(:all), Company.count(:all, :from => 'accounts')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
attr_accessor :callers
|
||||
|
||||
before_validation :record_callers
|
||||
|
||||
def after_validation
|
||||
record_callers
|
||||
end
|
||||
|
||||
def record_callers
|
||||
callers << self.class if callers
|
||||
end
|
||||
end
|
||||
|
||||
class CommentObserver < ActiveRecord::Observer
|
||||
attr_accessor :callers
|
||||
|
||||
def after_validation(model)
|
||||
callers << self.class if callers
|
||||
end
|
||||
end
|
||||
|
||||
class CallbacksObserversTest < ActiveRecord::TestCase
|
||||
def test_model_callbacks_fire_before_observers_are_notified
|
||||
callers = []
|
||||
|
||||
comment = Comment.new
|
||||
comment.callers = callers
|
||||
|
||||
CommentObserver.instance.callers = callers
|
||||
|
||||
comment.valid?
|
||||
|
||||
assert_equal [Comment, Comment, CommentObserver], callers, "model callbacks did not fire before observers were notified"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,438 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class CallbackDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
|
||||
class << self
|
||||
def callback_string(callback_method)
|
||||
"history << [#{callback_method.to_sym.inspect}, :string]"
|
||||
end
|
||||
|
||||
def callback_proc(callback_method)
|
||||
Proc.new { |model| model.history << [callback_method, :proc] }
|
||||
end
|
||||
|
||||
def define_callback_method(callback_method)
|
||||
define_method("#{callback_method}_method") do |model|
|
||||
model.history << [callback_method, :method]
|
||||
end
|
||||
end
|
||||
|
||||
def callback_object(callback_method)
|
||||
klass = Class.new
|
||||
klass.send(:define_method, callback_method) do |model|
|
||||
model.history << [callback_method, :object]
|
||||
end
|
||||
klass.new
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Callbacks::CALLBACKS.each do |callback_method|
|
||||
callback_method_sym = callback_method.to_sym
|
||||
define_callback_method(callback_method_sym)
|
||||
send(callback_method, callback_method_sym)
|
||||
send(callback_method, callback_string(callback_method_sym))
|
||||
send(callback_method, callback_proc(callback_method_sym))
|
||||
send(callback_method, callback_object(callback_method_sym))
|
||||
send(callback_method) { |model| model.history << [callback_method_sym, :block] }
|
||||
end
|
||||
|
||||
def history
|
||||
@history ||= []
|
||||
end
|
||||
|
||||
# after_initialize and after_find are invoked only if instance methods have been defined.
|
||||
def after_initialize
|
||||
end
|
||||
|
||||
def after_find
|
||||
end
|
||||
end
|
||||
|
||||
class ParentDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
attr_accessor :after_save_called
|
||||
before_validation {|record| record.after_save_called = true}
|
||||
end
|
||||
|
||||
class ChildDeveloper < ParentDeveloper
|
||||
|
||||
end
|
||||
|
||||
class RecursiveCallbackDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
|
||||
before_save :on_before_save
|
||||
after_save :on_after_save
|
||||
|
||||
attr_reader :on_before_save_called, :on_after_save_called
|
||||
|
||||
def on_before_save
|
||||
@on_before_save_called ||= 0
|
||||
@on_before_save_called += 1
|
||||
save unless @on_before_save_called > 1
|
||||
end
|
||||
|
||||
def on_after_save
|
||||
@on_after_save_called ||= 0
|
||||
@on_after_save_called += 1
|
||||
save unless @on_after_save_called > 1
|
||||
end
|
||||
end
|
||||
|
||||
class ImmutableDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
|
||||
validates_inclusion_of :salary, :in => 50000..200000
|
||||
|
||||
before_save :cancel
|
||||
before_destroy :cancel
|
||||
|
||||
def cancelled?
|
||||
@cancelled == true
|
||||
end
|
||||
|
||||
private
|
||||
def cancel
|
||||
@cancelled = true
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class ImmutableMethodDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
|
||||
validates_inclusion_of :salary, :in => 50000..200000
|
||||
|
||||
def cancelled?
|
||||
@cancelled == true
|
||||
end
|
||||
|
||||
def before_save
|
||||
@cancelled = true
|
||||
false
|
||||
end
|
||||
|
||||
def before_destroy
|
||||
@cancelled = true
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
class CallbackCancellationDeveloper < ActiveRecord::Base
|
||||
set_table_name 'developers'
|
||||
|
||||
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
|
||||
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
|
||||
|
||||
def before_save; !@cancel_before_save; end
|
||||
def before_create; !@cancel_before_create; end
|
||||
def before_update; !@cancel_before_update; end
|
||||
def before_destroy; !@cancel_before_destroy; end
|
||||
|
||||
def after_save; @after_save_called = true; end
|
||||
def after_update; @after_update_called = true; end
|
||||
def after_create; @after_create_called = true; end
|
||||
def after_destroy; @after_destroy_called = true; end
|
||||
end
|
||||
|
||||
class CallbacksTest < ActiveRecord::TestCase
|
||||
fixtures :developers
|
||||
|
||||
def test_initialize
|
||||
david = CallbackDeveloper.new
|
||||
assert_equal [
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_find
|
||||
david = CallbackDeveloper.find(1)
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_new_valid?
|
||||
david = CallbackDeveloper.new
|
||||
david.valid?
|
||||
assert_equal [
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_validation, :string ],
|
||||
[ :before_validation, :proc ],
|
||||
[ :before_validation, :object ],
|
||||
[ :before_validation, :block ],
|
||||
[ :before_validation_on_create, :string ],
|
||||
[ :before_validation_on_create, :proc ],
|
||||
[ :before_validation_on_create, :object ],
|
||||
[ :before_validation_on_create, :block ],
|
||||
[ :after_validation, :string ],
|
||||
[ :after_validation, :proc ],
|
||||
[ :after_validation, :object ],
|
||||
[ :after_validation, :block ],
|
||||
[ :after_validation_on_create, :string ],
|
||||
[ :after_validation_on_create, :proc ],
|
||||
[ :after_validation_on_create, :object ],
|
||||
[ :after_validation_on_create, :block ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_existing_valid?
|
||||
david = CallbackDeveloper.find(1)
|
||||
david.valid?
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_validation, :string ],
|
||||
[ :before_validation, :proc ],
|
||||
[ :before_validation, :object ],
|
||||
[ :before_validation, :block ],
|
||||
[ :before_validation_on_update, :string ],
|
||||
[ :before_validation_on_update, :proc ],
|
||||
[ :before_validation_on_update, :object ],
|
||||
[ :before_validation_on_update, :block ],
|
||||
[ :after_validation, :string ],
|
||||
[ :after_validation, :proc ],
|
||||
[ :after_validation, :object ],
|
||||
[ :after_validation, :block ],
|
||||
[ :after_validation_on_update, :string ],
|
||||
[ :after_validation_on_update, :proc ],
|
||||
[ :after_validation_on_update, :object ],
|
||||
[ :after_validation_on_update, :block ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_create
|
||||
david = CallbackDeveloper.create('name' => 'David', 'salary' => 1000000)
|
||||
assert_equal [
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_validation, :string ],
|
||||
[ :before_validation, :proc ],
|
||||
[ :before_validation, :object ],
|
||||
[ :before_validation, :block ],
|
||||
[ :before_validation_on_create, :string ],
|
||||
[ :before_validation_on_create, :proc ],
|
||||
[ :before_validation_on_create, :object ],
|
||||
[ :before_validation_on_create, :block ],
|
||||
[ :after_validation, :string ],
|
||||
[ :after_validation, :proc ],
|
||||
[ :after_validation, :object ],
|
||||
[ :after_validation, :block ],
|
||||
[ :after_validation_on_create, :string ],
|
||||
[ :after_validation_on_create, :proc ],
|
||||
[ :after_validation_on_create, :object ],
|
||||
[ :after_validation_on_create, :block ],
|
||||
[ :before_save, :string ],
|
||||
[ :before_save, :proc ],
|
||||
[ :before_save, :object ],
|
||||
[ :before_save, :block ],
|
||||
[ :before_create, :string ],
|
||||
[ :before_create, :proc ],
|
||||
[ :before_create, :object ],
|
||||
[ :before_create, :block ],
|
||||
[ :after_create, :string ],
|
||||
[ :after_create, :proc ],
|
||||
[ :after_create, :object ],
|
||||
[ :after_create, :block ],
|
||||
[ :after_save, :string ],
|
||||
[ :after_save, :proc ],
|
||||
[ :after_save, :object ],
|
||||
[ :after_save, :block ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_save
|
||||
david = CallbackDeveloper.find(1)
|
||||
david.save
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_validation, :string ],
|
||||
[ :before_validation, :proc ],
|
||||
[ :before_validation, :object ],
|
||||
[ :before_validation, :block ],
|
||||
[ :before_validation_on_update, :string ],
|
||||
[ :before_validation_on_update, :proc ],
|
||||
[ :before_validation_on_update, :object ],
|
||||
[ :before_validation_on_update, :block ],
|
||||
[ :after_validation, :string ],
|
||||
[ :after_validation, :proc ],
|
||||
[ :after_validation, :object ],
|
||||
[ :after_validation, :block ],
|
||||
[ :after_validation_on_update, :string ],
|
||||
[ :after_validation_on_update, :proc ],
|
||||
[ :after_validation_on_update, :object ],
|
||||
[ :after_validation_on_update, :block ],
|
||||
[ :before_save, :string ],
|
||||
[ :before_save, :proc ],
|
||||
[ :before_save, :object ],
|
||||
[ :before_save, :block ],
|
||||
[ :before_update, :string ],
|
||||
[ :before_update, :proc ],
|
||||
[ :before_update, :object ],
|
||||
[ :before_update, :block ],
|
||||
[ :after_update, :string ],
|
||||
[ :after_update, :proc ],
|
||||
[ :after_update, :object ],
|
||||
[ :after_update, :block ],
|
||||
[ :after_save, :string ],
|
||||
[ :after_save, :proc ],
|
||||
[ :after_save, :object ],
|
||||
[ :after_save, :block ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
david = CallbackDeveloper.find(1)
|
||||
david.destroy
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_destroy, :string ],
|
||||
[ :before_destroy, :proc ],
|
||||
[ :before_destroy, :object ],
|
||||
[ :before_destroy, :block ],
|
||||
[ :after_destroy, :string ],
|
||||
[ :after_destroy, :proc ],
|
||||
[ :after_destroy, :object ],
|
||||
[ :after_destroy, :block ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_delete
|
||||
david = CallbackDeveloper.find(1)
|
||||
CallbackDeveloper.delete(david.id)
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_before_save_returning_false
|
||||
david = ImmutableDeveloper.find(1)
|
||||
assert david.valid?
|
||||
assert !david.save
|
||||
assert_raise(ActiveRecord::RecordNotSaved) { david.save! }
|
||||
|
||||
david = ImmutableDeveloper.find(1)
|
||||
david.salary = 10_000_000
|
||||
assert !david.valid?
|
||||
assert !david.save
|
||||
assert_raise(ActiveRecord::RecordInvalid) { david.save! }
|
||||
|
||||
someone = CallbackCancellationDeveloper.find(1)
|
||||
someone.cancel_before_save = true
|
||||
assert someone.valid?
|
||||
assert !someone.save
|
||||
assert_save_callbacks_not_called(someone)
|
||||
end
|
||||
|
||||
def test_before_create_returning_false
|
||||
someone = CallbackCancellationDeveloper.new
|
||||
someone.cancel_before_create = true
|
||||
assert someone.valid?
|
||||
assert !someone.save
|
||||
assert_save_callbacks_not_called(someone)
|
||||
end
|
||||
|
||||
def test_before_update_returning_false
|
||||
someone = CallbackCancellationDeveloper.find(1)
|
||||
someone.cancel_before_update = true
|
||||
assert someone.valid?
|
||||
assert !someone.save
|
||||
assert_save_callbacks_not_called(someone)
|
||||
end
|
||||
|
||||
def test_before_destroy_returning_false
|
||||
david = ImmutableDeveloper.find(1)
|
||||
assert !david.destroy
|
||||
assert_not_nil ImmutableDeveloper.find_by_id(1)
|
||||
|
||||
someone = CallbackCancellationDeveloper.find(1)
|
||||
someone.cancel_before_destroy = true
|
||||
assert !someone.destroy
|
||||
assert !someone.after_destroy_called
|
||||
end
|
||||
|
||||
def assert_save_callbacks_not_called(someone)
|
||||
assert !someone.after_save_called
|
||||
assert !someone.after_create_called
|
||||
assert !someone.after_update_called
|
||||
end
|
||||
private :assert_save_callbacks_not_called
|
||||
|
||||
def test_zzz_callback_returning_false # must be run last since we modify CallbackDeveloper
|
||||
david = CallbackDeveloper.find(1)
|
||||
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :returning_false]; return false }
|
||||
CallbackDeveloper.before_validation proc { |model| model.history << [:before_validation, :should_never_get_here] }
|
||||
david.save
|
||||
assert_equal [
|
||||
[ :after_find, :string ],
|
||||
[ :after_find, :proc ],
|
||||
[ :after_find, :object ],
|
||||
[ :after_find, :block ],
|
||||
[ :after_initialize, :string ],
|
||||
[ :after_initialize, :proc ],
|
||||
[ :after_initialize, :object ],
|
||||
[ :after_initialize, :block ],
|
||||
[ :before_validation, :string ],
|
||||
[ :before_validation, :proc ],
|
||||
[ :before_validation, :object ],
|
||||
[ :before_validation, :block ],
|
||||
[ :before_validation, :returning_false ]
|
||||
], david.history
|
||||
end
|
||||
|
||||
def test_inheritence_of_callbacks
|
||||
parent = ParentDeveloper.new
|
||||
assert !parent.after_save_called
|
||||
parent.save
|
||||
assert parent.after_save_called
|
||||
|
||||
child = ChildDeveloper.new
|
||||
assert !child.after_save_called
|
||||
child.save
|
||||
assert child.after_save_called
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
require 'test/unit'
|
||||
require "cases/helper"
|
||||
require 'active_support/core_ext/class/inheritable_attributes'
|
||||
|
||||
class A
|
||||
include ClassInheritableAttributes
|
||||
end
|
||||
|
||||
class B < A
|
||||
write_inheritable_array "first", [ :one, :two ]
|
||||
end
|
||||
|
||||
class C < A
|
||||
write_inheritable_array "first", [ :three ]
|
||||
end
|
||||
|
||||
class D < B
|
||||
write_inheritable_array "first", [ :four ]
|
||||
end
|
||||
|
||||
|
||||
class ClassInheritableAttributesTest < ActiveRecord::TestCase
|
||||
def test_first_level
|
||||
assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
|
||||
assert_equal [ :three ], C.read_inheritable_attribute("first")
|
||||
end
|
||||
|
||||
def test_second_level
|
||||
assert_equal [ :one, :two, :four ], D.read_inheritable_attribute("first")
|
||||
assert_equal [ :one, :two ], B.read_inheritable_attribute("first")
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
|
||||
class TestColumnAlias < ActiveRecord::TestCase
|
||||
fixtures :topics
|
||||
|
||||
QUERY = if 'Oracle' == ActiveRecord::Base.connection.adapter_name
|
||||
'SELECT id AS pk FROM topics WHERE ROWNUM < 2'
|
||||
else
|
||||
'SELECT id AS pk FROM topics'
|
||||
end
|
||||
|
||||
def test_column_alias
|
||||
records = Topic.connection.select_all(QUERY)
|
||||
assert_equal 'pk', records[0].keys[0]
|
||||
end
|
||||
end
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class ColumnDefinitionTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter.new(nil)
|
||||
def @adapter.native_database_types
|
||||
{:string => "varchar"}
|
||||
end
|
||||
end
|
||||
|
||||
# Avoid column definitions in create table statements like:
|
||||
# `title` varchar(255) DEFAULT NULL
|
||||
def test_should_not_include_default_clause_when_default_is_null
|
||||
column = ActiveRecord::ConnectionAdapters::Column.new("title", nil, "varchar(20)")
|
||||
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
|
||||
@adapter, column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
assert_equal "title varchar(20)", column_def.to_sql
|
||||
end
|
||||
|
||||
def test_should_include_default_clause_when_default_is_present
|
||||
column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)")
|
||||
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
|
||||
@adapter, column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
assert_equal %Q{title varchar(20) DEFAULT 'Hello'}, column_def.to_sql
|
||||
end
|
||||
|
||||
def test_should_specify_not_null_if_null_option_is_false
|
||||
column = ActiveRecord::ConnectionAdapters::Column.new("title", "Hello", "varchar(20)", false)
|
||||
column_def = ActiveRecord::ConnectionAdapters::ColumnDefinition.new(
|
||||
@adapter, column.name, "string",
|
||||
column.limit, column.precision, column.scale, column.default, column.null)
|
||||
assert_equal %Q{title varchar(20) DEFAULT 'Hello' NOT NULL}, column_def.to_sql
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
def test_should_set_default_for_mysql_binary_data_types
|
||||
binary_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "binary(1)")
|
||||
assert_equal "a", binary_column.default
|
||||
|
||||
varbinary_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "varbinary(1)")
|
||||
assert_equal "a", varbinary_column.default
|
||||
end
|
||||
|
||||
def test_should_not_set_default_for_blob_and_text_data_types
|
||||
assert_raise ArgumentError do
|
||||
ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "a", "blob")
|
||||
end
|
||||
|
||||
assert_raise ArgumentError do
|
||||
ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", "Hello", "text")
|
||||
end
|
||||
|
||||
text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text")
|
||||
assert_equal nil, text_column.default
|
||||
|
||||
not_null_text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text", false)
|
||||
assert_equal "", not_null_text_column.default
|
||||
end
|
||||
|
||||
def test_has_default_should_return_false_for_blog_and_test_data_types
|
||||
blob_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "blob")
|
||||
assert !blob_column.has_default?
|
||||
|
||||
text_column = ActiveRecord::ConnectionAdapters::MysqlColumn.new("title", nil, "text")
|
||||
assert !text_column.has_default?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class ConnectionManagementTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@env = {}
|
||||
@app = stub('App')
|
||||
@management = ActiveRecord::ConnectionAdapters::ConnectionManagement.new(@app)
|
||||
|
||||
@connections_cleared = false
|
||||
ActiveRecord::Base.stubs(:clear_active_connections!).with { @connections_cleared = true }
|
||||
end
|
||||
|
||||
test "clears active connections after each call" do
|
||||
@app.expects(:call).with(@env)
|
||||
@management.call(@env)
|
||||
assert @connections_cleared
|
||||
end
|
||||
|
||||
test "doesn't clear active connections when running in a test case" do
|
||||
@env['rack.test'] = true
|
||||
@app.expects(:call).with(@env)
|
||||
@management.call(@env)
|
||||
assert !@connections_cleared
|
||||
end
|
||||
end
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class FirebirdConnectionTest < ActiveRecord::TestCase
|
||||
def test_charset_properly_set
|
||||
fb_conn = ActiveRecord::Base.connection.instance_variable_get(:@connection)
|
||||
assert_equal 'UTF8', fb_conn.database.character_set
|
||||
end
|
||||
end
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class MysqlConnectionTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
super
|
||||
@connection = ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def test_mysql_reconnect_attribute_after_connection_with_reconnect_true
|
||||
run_without_connection do |orig_connection|
|
||||
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true}))
|
||||
assert ActiveRecord::Base.connection.raw_connection.reconnect
|
||||
end
|
||||
end
|
||||
|
||||
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
|
||||
run_without_connection do |orig_connection|
|
||||
ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
|
||||
assert !ActiveRecord::Base.connection.raw_connection.reconnect
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_automatic_reconnection_after_timeout
|
||||
assert @connection.active?
|
||||
@connection.update('set @@wait_timeout=1')
|
||||
sleep 2
|
||||
assert !@connection.active?
|
||||
end
|
||||
|
||||
def test_successful_reconnection_after_timeout_with_manual_reconnect
|
||||
assert @connection.active?
|
||||
@connection.update('set @@wait_timeout=1')
|
||||
sleep 2
|
||||
@connection.reconnect!
|
||||
assert @connection.active?
|
||||
end
|
||||
|
||||
def test_successful_reconnection_after_timeout_with_verify
|
||||
assert @connection.active?
|
||||
@connection.update('set @@wait_timeout=1')
|
||||
sleep 2
|
||||
@connection.verify!
|
||||
assert @connection.active?
|
||||
end
|
||||
|
||||
# Test that MySQL allows multiple results for stored procedures
|
||||
if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
|
||||
def test_multi_results
|
||||
rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
|
||||
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
|
||||
assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def run_without_connection
|
||||
original_connection = ActiveRecord::Base.remove_connection
|
||||
begin
|
||||
yield original_connection
|
||||
ensure
|
||||
ActiveRecord::Base.establish_connection(original_connection)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class CopyTableTest < ActiveRecord::TestCase
|
||||
fixtures :companies, :comments
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
class << @connection
|
||||
public :copy_table, :table_structure, :indexes
|
||||
end
|
||||
end
|
||||
|
||||
def test_copy_table(from = 'customers', to = 'customers2', options = {})
|
||||
assert_nothing_raised {copy_table(from, to, options)}
|
||||
assert_equal row_count(from), row_count(to)
|
||||
|
||||
if block_given?
|
||||
yield from, to, options
|
||||
else
|
||||
assert_equal column_names(from), column_names(to)
|
||||
end
|
||||
|
||||
@connection.drop_table(to) rescue nil
|
||||
end
|
||||
|
||||
def test_copy_table_renaming_column
|
||||
test_copy_table('customers', 'customers2',
|
||||
:rename => {'name' => 'person_name'}) do |from, to, options|
|
||||
expected = column_values(from, 'name')
|
||||
assert expected.any?, 'only nils in resultset; real values are needed'
|
||||
assert_equal expected, column_values(to, 'person_name')
|
||||
end
|
||||
end
|
||||
|
||||
def test_copy_table_with_index
|
||||
test_copy_table('comments', 'comments_with_index') do
|
||||
@connection.add_index('comments_with_index', ['post_id', 'type'])
|
||||
test_copy_table('comments_with_index', 'comments_with_index2') do
|
||||
assert_equal table_indexes_without_name('comments_with_index'),
|
||||
table_indexes_without_name('comments_with_index2')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_copy_table_without_primary_key
|
||||
test_copy_table('developers_projects', 'programmers_projects')
|
||||
end
|
||||
|
||||
def test_copy_table_with_id_col_that_is_not_primary_key
|
||||
test_copy_table('goofy_string_id', 'goofy_string_id2') do |from, to, options|
|
||||
original_id = @connection.columns('goofy_string_id').detect{|col| col.name == 'id' }
|
||||
copied_id = @connection.columns('goofy_string_id2').detect{|col| col.name == 'id' }
|
||||
assert_equal original_id.type, copied_id.type
|
||||
assert_equal original_id.sql_type, copied_id.sql_type
|
||||
assert_equal original_id.limit, copied_id.limit
|
||||
assert_equal original_id.primary, copied_id.primary
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def copy_table(from, to, options = {})
|
||||
@connection.copy_table(from, to, {:temporary => true}.merge(options))
|
||||
end
|
||||
|
||||
def column_names(table)
|
||||
@connection.table_structure(table).map {|column| column['name']}
|
||||
end
|
||||
|
||||
def column_values(table, column)
|
||||
@connection.select_all("SELECT #{column} FROM #{table} ORDER BY id").map {|row| row[column]}
|
||||
end
|
||||
|
||||
def table_indexes_without_name(table)
|
||||
@connection.indexes('comments_with_index').delete(:name)
|
||||
end
|
||||
|
||||
def row_count(table)
|
||||
@connection.select_one("SELECT COUNT(*) AS count FROM #{table}")['count']
|
||||
end
|
||||
end
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/category'
|
||||
require 'models/categorization'
|
||||
|
||||
class CounterCacheTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :categories, :categorizations
|
||||
|
||||
class SpecialTopic < ::Topic
|
||||
has_many :special_replies, :foreign_key => 'parent_id'
|
||||
end
|
||||
|
||||
class SpecialReply < ::Reply
|
||||
belongs_to :special_topic, :foreign_key => 'parent_id', :counter_cache => 'replies_count'
|
||||
end
|
||||
|
||||
test "increment counter" do
|
||||
topic = Topic.find(1)
|
||||
assert_difference 'topic.reload.replies_count' do
|
||||
Topic.increment_counter(:replies_count, topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
test "decrement counter" do
|
||||
topic = Topic.find(1)
|
||||
assert_difference 'topic.reload.replies_count', -1 do
|
||||
Topic.decrement_counter(:replies_count, topic.id)
|
||||
end
|
||||
end
|
||||
|
||||
test "reset counters" do
|
||||
topic = Topic.find(1)
|
||||
# throw the count off by 1
|
||||
Topic.increment_counter(:replies_count, topic.id)
|
||||
|
||||
# check that it gets reset
|
||||
assert_difference 'topic.reload.replies_count', -1 do
|
||||
Topic.reset_counters(topic.id, :replies)
|
||||
end
|
||||
end
|
||||
|
||||
test "reset counters with string argument" do
|
||||
topic = Topic.find(1)
|
||||
Topic.increment_counter('replies_count', topic.id)
|
||||
|
||||
assert_difference 'topic.reload.replies_count', -1 do
|
||||
Topic.reset_counters(topic.id, 'replies')
|
||||
end
|
||||
end
|
||||
|
||||
test "reset counters with modularized and camelized classnames" do
|
||||
special = SpecialTopic.create!(:title => 'Special')
|
||||
SpecialTopic.increment_counter(:replies_count, special.id)
|
||||
|
||||
assert_difference 'special.reload.replies_count', -1 do
|
||||
SpecialTopic.reset_counters(special.id, :special_replies)
|
||||
end
|
||||
end
|
||||
|
||||
test "update counter with initial null value" do
|
||||
category = categories(:general)
|
||||
assert_equal 2, category.categorizations.count
|
||||
assert_nil category.categorizations_count
|
||||
|
||||
Category.update_counters(category.id, :categorizations_count => category.categorizations.count)
|
||||
assert_equal 2, category.reload.categorizations_count
|
||||
end
|
||||
|
||||
test "update counter for decrement" do
|
||||
topic = Topic.find(1)
|
||||
assert_difference 'topic.reload.replies_count', -3 do
|
||||
Topic.update_counters(topic.id, :replies_count => -3)
|
||||
end
|
||||
end
|
||||
|
||||
test "update counters of multiple records" do
|
||||
t1, t2 = topics(:first, :second)
|
||||
|
||||
assert_difference ['t1.reload.replies_count', 't2.reload.replies_count'], 2 do
|
||||
Topic.update_counters([t1.id, t2.id], :replies_count => 2)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class DatabaseStatementsTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
end
|
||||
|
||||
def test_insert_should_return_the_inserted_id
|
||||
id = @connection.insert("INSERT INTO accounts (firm_id,credit_limit) VALUES (42,5000)")
|
||||
assert_not_nil id
|
||||
end
|
||||
end
|
||||
|
|
@ -1,204 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class PostgresqlArray < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlMoney < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlNumber < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlTime < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlNetworkAddress < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlBitString < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlOid < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class PostgresqlDataTypeTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute("set lc_monetary = 'C'")
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_arrays (commission_by_quarter, nicknames) VALUES ( '{35000,21000,18000,17000}', '{foo,bar,baz}' )")
|
||||
@first_array = PostgresqlArray.find(1)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('567.89'::money)")
|
||||
@connection.execute("INSERT INTO postgresql_moneys (wealth) VALUES ('-567.89'::money)")
|
||||
@first_money = PostgresqlMoney.find(1)
|
||||
@second_money = PostgresqlMoney.find(2)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_numbers (single, double) VALUES (123.456, 123456.789)")
|
||||
@first_number = PostgresqlNumber.find(1)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_times (time_interval) VALUES ('1 year 2 days ago')")
|
||||
@first_time = PostgresqlTime.find(1)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_network_addresses (cidr_address, inet_address, mac_address) VALUES('192.168.0/24', '172.16.1.254/32', '01:23:45:67:89:0a')")
|
||||
@first_network_address = PostgresqlNetworkAddress.find(1)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_bit_strings (bit_string, bit_string_varying) VALUES (B'00010101', X'15')")
|
||||
@first_bit_string = PostgresqlBitString.find(1)
|
||||
|
||||
@connection.execute("INSERT INTO postgresql_oids (obj_id) VALUES (1234)")
|
||||
@first_oid = PostgresqlOid.find(1)
|
||||
end
|
||||
|
||||
def test_data_type_of_array_types
|
||||
assert_equal :string, @first_array.column_for_attribute(:commission_by_quarter).type
|
||||
assert_equal :string, @first_array.column_for_attribute(:nicknames).type
|
||||
end
|
||||
|
||||
def test_data_type_of_money_types
|
||||
assert_equal :decimal, @first_money.column_for_attribute(:wealth).type
|
||||
end
|
||||
|
||||
def test_data_type_of_number_types
|
||||
assert_equal :float, @first_number.column_for_attribute(:single).type
|
||||
assert_equal :float, @first_number.column_for_attribute(:double).type
|
||||
end
|
||||
|
||||
def test_data_type_of_time_types
|
||||
assert_equal :string, @first_time.column_for_attribute(:time_interval).type
|
||||
end
|
||||
|
||||
def test_data_type_of_network_address_types
|
||||
assert_equal :string, @first_network_address.column_for_attribute(:cidr_address).type
|
||||
assert_equal :string, @first_network_address.column_for_attribute(:inet_address).type
|
||||
assert_equal :string, @first_network_address.column_for_attribute(:mac_address).type
|
||||
end
|
||||
|
||||
def test_data_type_of_bit_string_types
|
||||
assert_equal :string, @first_bit_string.column_for_attribute(:bit_string).type
|
||||
assert_equal :string, @first_bit_string.column_for_attribute(:bit_string_varying).type
|
||||
end
|
||||
|
||||
def test_data_type_of_oid_types
|
||||
assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type
|
||||
end
|
||||
|
||||
def test_array_values
|
||||
assert_equal '{35000,21000,18000,17000}', @first_array.commission_by_quarter
|
||||
assert_equal '{foo,bar,baz}', @first_array.nicknames
|
||||
end
|
||||
|
||||
def test_money_values
|
||||
assert_equal 567.89, @first_money.wealth
|
||||
assert_equal -567.89, @second_money.wealth
|
||||
end
|
||||
|
||||
def test_number_values
|
||||
assert_equal 123.456, @first_number.single
|
||||
assert_equal 123456.789, @first_number.double
|
||||
end
|
||||
|
||||
def test_time_values
|
||||
assert_equal '-1 years -2 days', @first_time.time_interval
|
||||
end
|
||||
|
||||
def test_network_address_values
|
||||
assert_equal '192.168.0.0/24', @first_network_address.cidr_address
|
||||
assert_equal '172.16.1.254', @first_network_address.inet_address
|
||||
assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address
|
||||
end
|
||||
|
||||
def test_bit_string_values
|
||||
assert_equal '00010101', @first_bit_string.bit_string
|
||||
assert_equal '00010101', @first_bit_string.bit_string_varying
|
||||
end
|
||||
|
||||
def test_oid_values
|
||||
assert_equal 1234, @first_oid.obj_id
|
||||
end
|
||||
|
||||
def test_update_integer_array
|
||||
new_value = '{32800,95000,29350,17000}'
|
||||
assert @first_array.commission_by_quarter = new_value
|
||||
assert @first_array.save
|
||||
assert @first_array.reload
|
||||
assert_equal @first_array.commission_by_quarter, new_value
|
||||
assert @first_array.commission_by_quarter = new_value
|
||||
assert @first_array.save
|
||||
assert @first_array.reload
|
||||
assert_equal @first_array.commission_by_quarter, new_value
|
||||
end
|
||||
|
||||
def test_update_text_array
|
||||
new_value = '{robby,robert,rob,robbie}'
|
||||
assert @first_array.nicknames = new_value
|
||||
assert @first_array.save
|
||||
assert @first_array.reload
|
||||
assert_equal @first_array.nicknames, new_value
|
||||
assert @first_array.nicknames = new_value
|
||||
assert @first_array.save
|
||||
assert @first_array.reload
|
||||
assert_equal @first_array.nicknames, new_value
|
||||
end
|
||||
|
||||
def test_update_money
|
||||
new_value = BigDecimal.new('123.45')
|
||||
assert @first_money.wealth = new_value
|
||||
assert @first_money.save
|
||||
assert @first_money.reload
|
||||
assert_equal new_value, @first_money.wealth
|
||||
end
|
||||
|
||||
def test_update_number
|
||||
new_single = 789.012
|
||||
new_double = 789012.345
|
||||
assert @first_number.single = new_single
|
||||
assert @first_number.double = new_double
|
||||
assert @first_number.save
|
||||
assert @first_number.reload
|
||||
assert_equal @first_number.single, new_single
|
||||
assert_equal @first_number.double, new_double
|
||||
end
|
||||
|
||||
def test_update_time
|
||||
assert @first_time.time_interval = '2 years 3 minutes'
|
||||
assert @first_time.save
|
||||
assert @first_time.reload
|
||||
assert_equal @first_time.time_interval, '2 years 00:03:00'
|
||||
end
|
||||
|
||||
def test_update_network_address
|
||||
new_cidr_address = '10.1.2.3/32'
|
||||
new_inet_address = '10.0.0.0/8'
|
||||
new_mac_address = 'bc:de:f0:12:34:56'
|
||||
assert @first_network_address.cidr_address = new_cidr_address
|
||||
assert @first_network_address.inet_address = new_inet_address
|
||||
assert @first_network_address.mac_address = new_mac_address
|
||||
assert @first_network_address.save
|
||||
assert @first_network_address.reload
|
||||
assert_equal @first_network_address.cidr_address, new_cidr_address
|
||||
assert_equal @first_network_address.inet_address, new_inet_address
|
||||
assert_equal @first_network_address.mac_address, new_mac_address
|
||||
end
|
||||
|
||||
def test_update_bit_string
|
||||
new_bit_string = '11111111'
|
||||
new_bit_string_varying = 'FF'
|
||||
assert @first_bit_string.bit_string = new_bit_string
|
||||
assert @first_bit_string.bit_string_varying = new_bit_string_varying
|
||||
assert @first_bit_string.save
|
||||
assert @first_bit_string.reload
|
||||
assert_equal @first_bit_string.bit_string, new_bit_string
|
||||
assert_equal @first_bit_string.bit_string, @first_bit_string.bit_string_varying
|
||||
end
|
||||
|
||||
def test_update_oid
|
||||
new_value = 567890
|
||||
assert @first_oid.obj_id = new_value
|
||||
assert @first_oid.save
|
||||
assert @first_oid.reload
|
||||
assert_equal @first_oid.obj_id, new_value
|
||||
end
|
||||
end
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/task'
|
||||
|
||||
class DateTimeTest < ActiveRecord::TestCase
|
||||
def test_saves_both_date_and_time
|
||||
time_values = [1807, 2, 10, 15, 30, 45]
|
||||
now = DateTime.civil(*time_values)
|
||||
|
||||
task = Task.new
|
||||
task.starting = now
|
||||
task.save!
|
||||
|
||||
# check against Time.local_time, since some platforms will return a Time instead of a DateTime
|
||||
assert_equal Time.local_time(*time_values), Task.find(task.id).starting
|
||||
end
|
||||
|
||||
def test_assign_empty_date_time
|
||||
task = Task.new
|
||||
task.starting = ''
|
||||
task.ending = nil
|
||||
assert_nil task.starting
|
||||
assert_nil task.ending
|
||||
end
|
||||
|
||||
def test_assign_empty_date
|
||||
topic = Topic.new
|
||||
topic.last_read = ''
|
||||
assert_nil topic.last_read
|
||||
end
|
||||
|
||||
def test_assign_empty_time
|
||||
topic = Topic.new
|
||||
topic.bonus_time = ''
|
||||
assert_nil topic.bonus_time
|
||||
end
|
||||
end
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/default'
|
||||
|
||||
class DefaultTest < ActiveRecord::TestCase
|
||||
def test_default_timestamp
|
||||
default = Default.new
|
||||
assert_instance_of(Time, default.default_timestamp)
|
||||
assert_equal(:datetime, default.column_for_attribute(:default_timestamp).type)
|
||||
|
||||
# Variance should be small; increase if required -- e.g., if test db is on
|
||||
# remote host and clocks aren't synchronized.
|
||||
t1 = Time.new
|
||||
accepted_variance = 1.0
|
||||
assert_in_delta(t1.to_f, default.default_timestamp.to_f, accepted_variance)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,111 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/default'
|
||||
require 'models/entrant'
|
||||
|
||||
class DefaultTest < ActiveRecord::TestCase
|
||||
def test_nil_defaults_for_not_null_columns
|
||||
column_defaults =
|
||||
if current_adapter?(:MysqlAdapter) && (Mysql.client_version < 50051 || (50100..50122).include?(Mysql.client_version))
|
||||
{ 'id' => nil, 'name' => '', 'course_id' => nil }
|
||||
else
|
||||
{ 'id' => nil, 'name' => nil, 'course_id' => nil }
|
||||
end
|
||||
|
||||
column_defaults.each do |name, default|
|
||||
column = Entrant.columns_hash[name]
|
||||
assert !column.null, "#{name} column should be NOT NULL"
|
||||
assert_equal default, column.default, "#{name} column should be DEFAULT #{default.inspect}"
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter, :FirebirdAdapter, :OpenBaseAdapter, :OracleAdapter)
|
||||
def test_default_integers
|
||||
default = Default.new
|
||||
assert_instance_of Fixnum, default.positive_integer
|
||||
assert_equal 1, default.positive_integer
|
||||
assert_instance_of Fixnum, default.negative_integer
|
||||
assert_equal -1, default.negative_integer
|
||||
assert_instance_of BigDecimal, default.decimal_number
|
||||
assert_equal BigDecimal.new("2.78"), default.decimal_number
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
def test_multiline_default_text
|
||||
# older postgres versions represent the default with escapes ("\\012" for a newline)
|
||||
assert ( "--- []\n\n" == Default.columns_hash['multiline_default'].default ||
|
||||
"--- []\\012\\012" == Default.columns_hash['multiline_default'].default)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
class DefaultsTestWithoutTransactionalFixtures < ActiveRecord::TestCase
|
||||
# ActiveRecord::Base#create! (and #save and other related methods) will
|
||||
# open a new transaction. When in transactional fixtures mode, this will
|
||||
# cause ActiveRecord to create a new savepoint. However, since MySQL doesn't
|
||||
# support DDL transactions, creating a table will result in any created
|
||||
# savepoints to be automatically released. This in turn causes the savepoint
|
||||
# release code in AbstractAdapter#transaction to fail.
|
||||
#
|
||||
# We don't want that to happen, so we disable transactional fixtures here.
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
# MySQL 5 and higher is quirky with not null text/blob columns.
|
||||
# With MySQL Text/blob columns cannot have defaults. If the column is not
|
||||
# null MySQL will report that the column has a null default
|
||||
# but it behaves as though the column had a default of ''
|
||||
def test_mysql_text_not_null_defaults
|
||||
klass = Class.new(ActiveRecord::Base)
|
||||
klass.table_name = 'test_mysql_text_not_null_defaults'
|
||||
klass.connection.create_table klass.table_name do |t|
|
||||
t.column :non_null_text, :text, :null => false
|
||||
t.column :non_null_blob, :blob, :null => false
|
||||
t.column :null_text, :text, :null => true
|
||||
t.column :null_blob, :blob, :null => true
|
||||
end
|
||||
assert_equal '', klass.columns_hash['non_null_blob'].default
|
||||
assert_equal '', klass.columns_hash['non_null_text'].default
|
||||
|
||||
assert_equal nil, klass.columns_hash['null_blob'].default
|
||||
assert_equal nil, klass.columns_hash['null_text'].default
|
||||
|
||||
assert_nothing_raised do
|
||||
instance = klass.create!
|
||||
assert_equal '', instance.non_null_text
|
||||
assert_equal '', instance.non_null_blob
|
||||
assert_nil instance.null_text
|
||||
assert_nil instance.null_blob
|
||||
end
|
||||
ensure
|
||||
klass.connection.drop_table(klass.table_name) rescue nil
|
||||
end
|
||||
|
||||
# MySQL uses an implicit default 0 rather than NULL unless in strict mode.
|
||||
# We use an implicit NULL so schema.rb is compatible with other databases.
|
||||
def test_mysql_integer_not_null_defaults
|
||||
klass = Class.new(ActiveRecord::Base)
|
||||
klass.table_name = 'test_integer_not_null_default_zero'
|
||||
klass.connection.create_table klass.table_name do |t|
|
||||
t.column :zero, :integer, :null => false, :default => 0
|
||||
t.column :omit, :integer, :null => false
|
||||
end
|
||||
|
||||
assert_equal 0, klass.columns_hash['zero'].default
|
||||
assert !klass.columns_hash['zero'].null
|
||||
# 0 in MySQL 4, nil in 5.
|
||||
assert [0, nil].include?(klass.columns_hash['omit'].default)
|
||||
assert !klass.columns_hash['omit'].null
|
||||
|
||||
assert_raise(ActiveRecord::StatementInvalid) { klass.create! }
|
||||
|
||||
assert_nothing_raised do
|
||||
instance = klass.create!(:omit => 1)
|
||||
assert_equal 0, instance.zero
|
||||
assert_equal 1, instance.omit
|
||||
end
|
||||
ensure
|
||||
klass.connection.drop_table(klass.table_name) rescue nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/entrant'
|
||||
|
||||
class DeprecatedFinderTest < ActiveRecord::TestCase
|
||||
fixtures :entrants
|
||||
|
||||
def test_deprecated_find_all_was_removed
|
||||
assert_raise(NoMethodError) { Entrant.find_all }
|
||||
end
|
||||
|
||||
def test_deprecated_find_first_was_removed
|
||||
assert_raise(NoMethodError) { Entrant.find_first }
|
||||
end
|
||||
|
||||
def test_deprecated_find_on_conditions_was_removed
|
||||
assert_raise(NoMethodError) { Entrant.find_on_conditions }
|
||||
end
|
||||
|
||||
def test_count
|
||||
assert_equal(0, Entrant.count(:conditions => "id > 3"))
|
||||
assert_equal(1, Entrant.count(:conditions => ["id > ?", 2]))
|
||||
assert_equal(2, Entrant.count(:conditions => ["id > ?", 1]))
|
||||
end
|
||||
|
||||
def test_count_by_sql
|
||||
assert_equal(0, Entrant.count_by_sql("SELECT COUNT(*) FROM entrants WHERE id > 3"))
|
||||
assert_equal(1, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 2]))
|
||||
assert_equal(2, Entrant.count_by_sql(["SELECT COUNT(*) FROM entrants WHERE id > ?", 1]))
|
||||
end
|
||||
end
|
||||
316
vendor/rails/activerecord/test/cases/dirty_test.rb
vendored
316
vendor/rails/activerecord/test/cases/dirty_test.rb
vendored
|
|
@ -1,316 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/topic' # For booleans
|
||||
require 'models/pirate' # For timestamps
|
||||
require 'models/parrot'
|
||||
require 'models/person' # For optimistic locking
|
||||
|
||||
class Pirate # Just reopening it, not defining it
|
||||
attr_accessor :detected_changes_in_after_update # Boolean for if changes are detected
|
||||
attr_accessor :changes_detected_in_after_update # Actual changes
|
||||
|
||||
after_update :check_changes
|
||||
|
||||
private
|
||||
# after_save/update in sweepers, observers, and the model itself
|
||||
# can end up checking dirty status and acting on the results
|
||||
def check_changes
|
||||
if self.changed?
|
||||
self.detected_changes_in_after_update = true
|
||||
self.changes_detected_in_after_update = self.changes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class NumericData < ActiveRecord::Base
|
||||
self.table_name = 'numeric_data'
|
||||
end
|
||||
|
||||
class DirtyTest < ActiveRecord::TestCase
|
||||
def test_attribute_changes
|
||||
# New record - no changes.
|
||||
pirate = Pirate.new
|
||||
assert !pirate.catchphrase_changed?
|
||||
assert_nil pirate.catchphrase_change
|
||||
|
||||
# Change catchphrase.
|
||||
pirate.catchphrase = 'arrr'
|
||||
assert pirate.catchphrase_changed?
|
||||
assert_nil pirate.catchphrase_was
|
||||
assert_equal [nil, 'arrr'], pirate.catchphrase_change
|
||||
|
||||
# Saved - no changes.
|
||||
pirate.save!
|
||||
assert !pirate.catchphrase_changed?
|
||||
assert_nil pirate.catchphrase_change
|
||||
|
||||
# Same value - no changes.
|
||||
pirate.catchphrase = 'arrr'
|
||||
assert !pirate.catchphrase_changed?
|
||||
assert_nil pirate.catchphrase_change
|
||||
end
|
||||
|
||||
def test_aliased_attribute_changes
|
||||
# the actual attribute here is name, title is an
|
||||
# alias setup via alias_attribute
|
||||
parrot = Parrot.new
|
||||
assert !parrot.title_changed?
|
||||
assert_nil parrot.title_change
|
||||
|
||||
parrot.name = 'Sam'
|
||||
assert parrot.title_changed?
|
||||
assert_nil parrot.title_was
|
||||
assert_equal parrot.name_change, parrot.title_change
|
||||
end
|
||||
|
||||
def test_nullable_number_not_marked_as_changed_if_new_value_is_blank
|
||||
pirate = Pirate.new
|
||||
|
||||
["", nil].each do |value|
|
||||
pirate.parrot_id = value
|
||||
assert !pirate.parrot_id_changed?
|
||||
assert_nil pirate.parrot_id_change
|
||||
end
|
||||
end
|
||||
|
||||
def test_nullable_decimal_not_marked_as_changed_if_new_value_is_blank
|
||||
numeric_data = NumericData.new
|
||||
|
||||
["", nil].each do |value|
|
||||
numeric_data.bank_balance = value
|
||||
assert !numeric_data.bank_balance_changed?
|
||||
assert_nil numeric_data.bank_balance_change
|
||||
end
|
||||
end
|
||||
|
||||
def test_nullable_float_not_marked_as_changed_if_new_value_is_blank
|
||||
numeric_data = NumericData.new
|
||||
|
||||
["", nil].each do |value|
|
||||
numeric_data.temperature = value
|
||||
assert !numeric_data.temperature_changed?
|
||||
assert_nil numeric_data.temperature_change
|
||||
end
|
||||
end
|
||||
|
||||
def test_nullable_integer_zero_to_string_zero_not_marked_as_changed
|
||||
pirate = Pirate.new
|
||||
pirate.parrot_id = 0
|
||||
pirate.catchphrase = 'arrr'
|
||||
assert pirate.save!
|
||||
|
||||
assert !pirate.changed?
|
||||
|
||||
pirate.parrot_id = '0'
|
||||
assert !pirate.changed?
|
||||
end
|
||||
|
||||
def test_zero_to_blank_marked_as_changed
|
||||
pirate = Pirate.new
|
||||
pirate.catchphrase = "Yarrrr, me hearties"
|
||||
pirate.parrot_id = 1
|
||||
pirate.save
|
||||
|
||||
# check the change from 1 to ''
|
||||
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
||||
pirate.parrot_id = ''
|
||||
assert pirate.parrot_id_changed?
|
||||
assert_equal([1, nil], pirate.parrot_id_change)
|
||||
pirate.save
|
||||
|
||||
# check the change from nil to 0
|
||||
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
||||
pirate.parrot_id = 0
|
||||
assert pirate.parrot_id_changed?
|
||||
assert_equal([nil, 0], pirate.parrot_id_change)
|
||||
pirate.save
|
||||
|
||||
# check the change from 0 to ''
|
||||
pirate = Pirate.find_by_catchphrase("Yarrrr, me hearties")
|
||||
pirate.parrot_id = ''
|
||||
assert pirate.parrot_id_changed?
|
||||
assert_equal([0, nil], pirate.parrot_id_change)
|
||||
end
|
||||
|
||||
def test_object_should_be_changed_if_any_attribute_is_changed
|
||||
pirate = Pirate.new
|
||||
assert !pirate.changed?
|
||||
assert_equal [], pirate.changed
|
||||
assert_equal Hash.new, pirate.changes
|
||||
|
||||
pirate.catchphrase = 'arrr'
|
||||
assert pirate.changed?
|
||||
assert_nil pirate.catchphrase_was
|
||||
assert_equal %w(catchphrase), pirate.changed
|
||||
assert_equal({'catchphrase' => [nil, 'arrr']}, pirate.changes)
|
||||
|
||||
pirate.save
|
||||
assert !pirate.changed?
|
||||
assert_equal [], pirate.changed
|
||||
assert_equal Hash.new, pirate.changes
|
||||
end
|
||||
|
||||
def test_attribute_will_change!
|
||||
pirate = Pirate.create!(:catchphrase => 'arr')
|
||||
|
||||
pirate.catchphrase << ' matey'
|
||||
assert !pirate.catchphrase_changed?
|
||||
|
||||
assert pirate.catchphrase_will_change!
|
||||
assert pirate.catchphrase_changed?
|
||||
assert_equal ['arr matey', 'arr matey'], pirate.catchphrase_change
|
||||
|
||||
pirate.catchphrase << '!'
|
||||
assert pirate.catchphrase_changed?
|
||||
assert_equal ['arr matey', 'arr matey!'], pirate.catchphrase_change
|
||||
end
|
||||
|
||||
def test_association_assignment_changes_foreign_key
|
||||
pirate = Pirate.create!(:catchphrase => 'jarl')
|
||||
pirate.parrot = Parrot.create!(:name => 'Lorre')
|
||||
assert pirate.changed?
|
||||
assert_equal %w(parrot_id), pirate.changed
|
||||
end
|
||||
|
||||
def test_attribute_should_be_compared_with_type_cast
|
||||
topic = Topic.new
|
||||
assert topic.approved?
|
||||
assert !topic.approved_changed?
|
||||
|
||||
# Coming from web form.
|
||||
params = {:topic => {:approved => 1}}
|
||||
# In the controller.
|
||||
topic.attributes = params[:topic]
|
||||
assert topic.approved?
|
||||
assert !topic.approved_changed?
|
||||
end
|
||||
|
||||
def test_partial_update
|
||||
pirate = Pirate.new(:catchphrase => 'foo')
|
||||
old_updated_on = 1.hour.ago.beginning_of_day
|
||||
|
||||
with_partial_updates Pirate, false do
|
||||
assert_queries(2) { 2.times { pirate.save! } }
|
||||
Pirate.update_all({ :updated_on => old_updated_on }, :id => pirate.id)
|
||||
end
|
||||
|
||||
with_partial_updates Pirate, true do
|
||||
assert_queries(0) { 2.times { pirate.save! } }
|
||||
assert_equal old_updated_on, pirate.reload.updated_on
|
||||
|
||||
assert_queries(1) { pirate.catchphrase = 'bar'; pirate.save! }
|
||||
assert_not_equal old_updated_on, pirate.reload.updated_on
|
||||
end
|
||||
end
|
||||
|
||||
def test_partial_update_with_optimistic_locking
|
||||
person = Person.new(:first_name => 'foo')
|
||||
old_lock_version = 1
|
||||
|
||||
with_partial_updates Person, false do
|
||||
assert_queries(2) { 2.times { person.save! } }
|
||||
Person.update_all({ :first_name => 'baz' }, :id => person.id)
|
||||
end
|
||||
|
||||
with_partial_updates Person, true do
|
||||
assert_queries(0) { 2.times { person.save! } }
|
||||
assert_equal old_lock_version, person.reload.lock_version
|
||||
|
||||
assert_queries(1) { person.first_name = 'bar'; person.save! }
|
||||
assert_not_equal old_lock_version, person.reload.lock_version
|
||||
end
|
||||
end
|
||||
|
||||
def test_changed_attributes_should_be_preserved_if_save_failure
|
||||
pirate = Pirate.new
|
||||
pirate.parrot_id = 1
|
||||
assert !pirate.save
|
||||
check_pirate_after_save_failure(pirate)
|
||||
|
||||
pirate = Pirate.new
|
||||
pirate.parrot_id = 1
|
||||
assert_raise(ActiveRecord::RecordInvalid) { pirate.save! }
|
||||
check_pirate_after_save_failure(pirate)
|
||||
end
|
||||
|
||||
def test_reload_should_clear_changed_attributes
|
||||
pirate = Pirate.create!(:catchphrase => "shiver me timbers")
|
||||
pirate.catchphrase = "*hic*"
|
||||
assert pirate.changed?
|
||||
pirate.reload
|
||||
assert !pirate.changed?
|
||||
end
|
||||
|
||||
def test_reverted_changes_are_not_dirty
|
||||
phrase = "shiver me timbers"
|
||||
pirate = Pirate.create!(:catchphrase => phrase)
|
||||
pirate.catchphrase = "*hic*"
|
||||
assert pirate.changed?
|
||||
pirate.catchphrase = phrase
|
||||
assert !pirate.changed?
|
||||
end
|
||||
|
||||
def test_reverted_changes_are_not_dirty_after_multiple_changes
|
||||
phrase = "shiver me timbers"
|
||||
pirate = Pirate.create!(:catchphrase => phrase)
|
||||
10.times do |i|
|
||||
pirate.catchphrase = "*hic*" * i
|
||||
assert pirate.changed?
|
||||
end
|
||||
assert pirate.changed?
|
||||
pirate.catchphrase = phrase
|
||||
assert !pirate.changed?
|
||||
end
|
||||
|
||||
|
||||
def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
|
||||
pirate = Pirate.create!(:catchphrase => "Yar!")
|
||||
|
||||
pirate.parrot_id = 1
|
||||
assert pirate.changed?
|
||||
assert pirate.parrot_id_changed?
|
||||
assert !pirate.catchphrase_changed?
|
||||
|
||||
pirate.parrot_id = nil
|
||||
assert !pirate.changed?
|
||||
assert !pirate.parrot_id_changed?
|
||||
assert !pirate.catchphrase_changed?
|
||||
end
|
||||
|
||||
def test_save_should_store_serialized_attributes_even_with_partial_updates
|
||||
with_partial_updates(Topic) do
|
||||
topic = Topic.create!(:content => {:a => "a"})
|
||||
topic.content[:b] = "b"
|
||||
#assert topic.changed? # Known bug, will fail
|
||||
topic.save!
|
||||
assert_equal "b", topic.content[:b]
|
||||
topic.reload
|
||||
assert_equal "b", topic.content[:b]
|
||||
end
|
||||
end
|
||||
|
||||
def test_save_should_not_save_serialized_attribute_with_partial_updates_if_not_present
|
||||
with_partial_updates(Topic) do
|
||||
Topic.create!(:author_name => 'Bill', :content => {:a => "a"})
|
||||
topic = Topic.first(:select => 'id, author_name')
|
||||
topic.update_attribute :author_name, 'John'
|
||||
topic = Topic.first
|
||||
assert_not_nil topic.content
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def with_partial_updates(klass, on = true)
|
||||
old = klass.partial_updates?
|
||||
klass.partial_updates = on
|
||||
yield
|
||||
ensure
|
||||
klass.partial_updates = old
|
||||
end
|
||||
|
||||
def check_pirate_after_save_failure(pirate)
|
||||
assert pirate.changed?
|
||||
assert pirate.parrot_id_changed?
|
||||
assert_equal %w(parrot_id), pirate.changed
|
||||
assert_nil pirate.parrot_id_was
|
||||
end
|
||||
end
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
|
||||
class FinderRespondToTest < ActiveRecord::TestCase
|
||||
|
||||
fixtures :topics
|
||||
|
||||
def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method
|
||||
class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { }
|
||||
assert Topic.respond_to?(:method_added_for_finder_respond_to_test)
|
||||
ensure
|
||||
class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test)
|
||||
end
|
||||
|
||||
def test_should_preserve_normal_respond_to_behaviour_and_respond_to_standard_object_method
|
||||
assert Topic.respond_to?(:to_s)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_by_one_attribute_before_caching
|
||||
ensure_topic_method_is_not_cached(:find_by_title)
|
||||
assert Topic.respond_to?(:find_by_title)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_all_by_one_attribute
|
||||
ensure_topic_method_is_not_cached(:find_all_by_title)
|
||||
assert Topic.respond_to?(:find_all_by_title)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_all_by_two_attributes
|
||||
ensure_topic_method_is_not_cached(:find_all_by_title_and_author_name)
|
||||
assert Topic.respond_to?(:find_all_by_title_and_author_name)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_by_two_attributes
|
||||
ensure_topic_method_is_not_cached(:find_by_title_and_author_name)
|
||||
assert Topic.respond_to?(:find_by_title_and_author_name)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_or_initialize_from_one_attribute
|
||||
ensure_topic_method_is_not_cached(:find_or_initialize_by_title)
|
||||
assert Topic.respond_to?(:find_or_initialize_by_title)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_or_initialize_from_two_attributes
|
||||
ensure_topic_method_is_not_cached(:find_or_initialize_by_title_and_author_name)
|
||||
assert Topic.respond_to?(:find_or_initialize_by_title_and_author_name)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_or_create_from_one_attribute
|
||||
ensure_topic_method_is_not_cached(:find_or_create_by_title)
|
||||
assert Topic.respond_to?(:find_or_create_by_title)
|
||||
end
|
||||
|
||||
def test_should_respond_to_find_or_create_from_two_attributes
|
||||
ensure_topic_method_is_not_cached(:find_or_create_by_title_and_author_name)
|
||||
assert Topic.respond_to?(:find_or_create_by_title_and_author_name)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_find_by_one_missing_attribute
|
||||
assert !Topic.respond_to?(:find_by_undertitle)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_find_by_invalid_method_syntax
|
||||
assert !Topic.respond_to?(:fail_to_find_by_title)
|
||||
assert !Topic.respond_to?(:find_by_title?)
|
||||
assert !Topic.respond_to?(:fail_to_find_or_create_by_title)
|
||||
assert !Topic.respond_to?(:find_or_create_by_title?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ensure_topic_method_is_not_cached(method_id)
|
||||
class << Topic; self; end.send(:remove_method, method_id) if Topic.public_methods.any? { |m| m.to_s == method_id.to_s }
|
||||
end
|
||||
|
||||
end
|
||||
1070
vendor/rails/activerecord/test/cases/finder_test.rb
vendored
1070
vendor/rails/activerecord/test/cases/finder_test.rb
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,661 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/binary'
|
||||
require 'models/topic'
|
||||
require 'models/computer'
|
||||
require 'models/developer'
|
||||
require 'models/company'
|
||||
require 'models/task'
|
||||
require 'models/reply'
|
||||
require 'models/joke'
|
||||
require 'models/course'
|
||||
require 'models/category'
|
||||
require 'models/parrot'
|
||||
require 'models/pirate'
|
||||
require 'models/treasure'
|
||||
require 'models/matey'
|
||||
require 'models/ship'
|
||||
require 'models/book'
|
||||
|
||||
class FixturesTest < ActiveRecord::TestCase
|
||||
self.use_instantiated_fixtures = true
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
fixtures :topics, :developers, :accounts, :tasks, :categories, :funny_jokes, :binaries
|
||||
|
||||
FIXTURES = %w( accounts binaries companies customers
|
||||
developers developers_projects entrants
|
||||
movies projects subscribers topics tasks )
|
||||
MATCH_ATTRIBUTE_NAME = /[a-zA-Z][-_\w]*/
|
||||
|
||||
def test_clean_fixtures
|
||||
FIXTURES.each do |name|
|
||||
fixtures = nil
|
||||
assert_nothing_raised { fixtures = create_fixtures(name) }
|
||||
assert_kind_of(Fixtures, fixtures)
|
||||
fixtures.each { |name, fixture|
|
||||
fixture.each { |key, value|
|
||||
assert_match(MATCH_ATTRIBUTE_NAME, key)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def test_multiple_clean_fixtures
|
||||
fixtures_array = nil
|
||||
assert_nothing_raised { fixtures_array = create_fixtures(*FIXTURES) }
|
||||
assert_kind_of(Array, fixtures_array)
|
||||
fixtures_array.each { |fixtures| assert_kind_of(Fixtures, fixtures) }
|
||||
end
|
||||
|
||||
def test_attributes
|
||||
topics = create_fixtures("topics")
|
||||
assert_equal("The First Topic", topics["first"]["title"])
|
||||
assert_nil(topics["second"]["author_email_address"])
|
||||
end
|
||||
|
||||
def test_inserts
|
||||
topics = create_fixtures("topics")
|
||||
first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'David'")
|
||||
assert_equal("The First Topic", first_row["title"])
|
||||
|
||||
second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM topics WHERE author_name = 'Mary'")
|
||||
assert_nil(second_row["author_email_address"])
|
||||
end
|
||||
|
||||
if ActiveRecord::Base.connection.supports_migrations?
|
||||
def test_inserts_with_pre_and_suffix
|
||||
# Reset cache to make finds on the new table work
|
||||
Fixtures.reset_cache
|
||||
|
||||
ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
|
||||
t.column :title, :string
|
||||
t.column :author_name, :string
|
||||
t.column :author_email_address, :string
|
||||
t.column :written_on, :datetime
|
||||
t.column :bonus_time, :time
|
||||
t.column :last_read, :date
|
||||
t.column :content, :string
|
||||
t.column :approved, :boolean, :default => true
|
||||
t.column :replies_count, :integer, :default => 0
|
||||
t.column :parent_id, :integer
|
||||
t.column :type, :string, :limit => 50
|
||||
end
|
||||
|
||||
# Store existing prefix/suffix
|
||||
old_prefix = ActiveRecord::Base.table_name_prefix
|
||||
old_suffix = ActiveRecord::Base.table_name_suffix
|
||||
|
||||
# Set a prefix/suffix we can test against
|
||||
ActiveRecord::Base.table_name_prefix = 'prefix_'
|
||||
ActiveRecord::Base.table_name_suffix = '_suffix'
|
||||
|
||||
topics = create_fixtures("topics")
|
||||
|
||||
first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
|
||||
assert_equal("The First Topic", first_row["title"])
|
||||
|
||||
second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
|
||||
assert_nil(second_row["author_email_address"])
|
||||
|
||||
# This checks for a caching problem which causes a bug in the fixtures
|
||||
# class-level configuration helper.
|
||||
assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create"
|
||||
ensure
|
||||
# Restore prefix/suffix to its previous values
|
||||
ActiveRecord::Base.table_name_prefix = old_prefix
|
||||
ActiveRecord::Base.table_name_suffix = old_suffix
|
||||
|
||||
ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
def test_insert_with_datetime
|
||||
topics = create_fixtures("tasks")
|
||||
first = Task.find(1)
|
||||
assert first
|
||||
end
|
||||
|
||||
def test_logger_level_invariant
|
||||
level = ActiveRecord::Base.logger.level
|
||||
create_fixtures('topics')
|
||||
assert_equal level, ActiveRecord::Base.logger.level
|
||||
end
|
||||
|
||||
def test_instantiation
|
||||
topics = create_fixtures("topics")
|
||||
assert_kind_of Topic, topics["first"].find
|
||||
end
|
||||
|
||||
def test_complete_instantiation
|
||||
assert_equal 4, @topics.size
|
||||
assert_equal "The First Topic", @first.title
|
||||
end
|
||||
|
||||
def test_fixtures_from_root_yml_with_instantiation
|
||||
# assert_equal 2, @accounts.size
|
||||
assert_equal 50, @unknown.credit_limit
|
||||
end
|
||||
|
||||
def test_erb_in_fixtures
|
||||
assert_equal 11, @developers.size
|
||||
assert_equal "fixture_5", @dev_5.name
|
||||
end
|
||||
|
||||
def test_empty_yaml_fixture
|
||||
assert_not_nil Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/yml/accounts")
|
||||
end
|
||||
|
||||
def test_empty_yaml_fixture_with_a_comment_in_it
|
||||
assert_not_nil Fixtures.new( Account.connection, "companies", 'Company', FIXTURES_ROOT + "/naked/yml/companies")
|
||||
end
|
||||
|
||||
def test_dirty_dirty_yaml_file
|
||||
assert_raise(Fixture::FormatError) do
|
||||
Fixtures.new( Account.connection, "courses", 'Course', FIXTURES_ROOT + "/naked/yml/courses")
|
||||
end
|
||||
end
|
||||
|
||||
def test_empty_csv_fixtures
|
||||
assert_not_nil Fixtures.new( Account.connection, "accounts", 'Account', FIXTURES_ROOT + "/naked/csv/accounts")
|
||||
end
|
||||
|
||||
def test_omap_fixtures
|
||||
assert_nothing_raised do
|
||||
fixtures = Fixtures.new(Account.connection, 'categories', 'Category', FIXTURES_ROOT + "/categories_ordered")
|
||||
|
||||
i = 0
|
||||
fixtures.each do |name, fixture|
|
||||
assert_equal "fixture_no_#{i}", name
|
||||
assert_equal "Category #{i}", fixture['name']
|
||||
i += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_yml_file_in_subdirectory
|
||||
assert_equal(categories(:sub_special_1).name, "A special category in a subdir file")
|
||||
assert_equal(categories(:sub_special_1).class, SpecialCategory)
|
||||
end
|
||||
|
||||
def test_subsubdir_file_with_arbitrary_name
|
||||
assert_equal(categories(:sub_special_3).name, "A special category in an arbitrarily named subsubdir file")
|
||||
assert_equal(categories(:sub_special_3).class, SpecialCategory)
|
||||
end
|
||||
|
||||
def test_binary_in_fixtures
|
||||
assert_equal 1, @binaries.size
|
||||
data = File.open(ASSETS_ROOT + "/flowers.jpg", 'rb') { |f| f.read }
|
||||
data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
|
||||
data.freeze
|
||||
assert_equal data, @flowers.data
|
||||
end
|
||||
end
|
||||
|
||||
if Account.connection.respond_to?(:reset_pk_sequence!)
|
||||
class FixturesResetPkSequenceTest < ActiveRecord::TestCase
|
||||
fixtures :accounts
|
||||
fixtures :companies
|
||||
|
||||
def setup
|
||||
@instances = [Account.new(:credit_limit => 50), Company.new(:name => 'RoR Consulting')]
|
||||
Fixtures.reset_cache # make sure tables get reinitialized
|
||||
end
|
||||
|
||||
def test_resets_to_min_pk_with_specified_pk_and_sequence
|
||||
@instances.each do |instance|
|
||||
model = instance.class
|
||||
model.delete_all
|
||||
model.connection.reset_pk_sequence!(model.table_name, model.primary_key, model.sequence_name)
|
||||
|
||||
instance.save!
|
||||
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
|
||||
end
|
||||
end
|
||||
|
||||
def test_resets_to_min_pk_with_default_pk_and_sequence
|
||||
@instances.each do |instance|
|
||||
model = instance.class
|
||||
model.delete_all
|
||||
model.connection.reset_pk_sequence!(model.table_name)
|
||||
|
||||
instance.save!
|
||||
assert_equal 1, instance.id, "Sequence reset for #{model.table_name} failed."
|
||||
end
|
||||
end
|
||||
|
||||
def test_create_fixtures_resets_sequences_when_not_cached
|
||||
@instances.each do |instance|
|
||||
max_id = create_fixtures(instance.class.table_name).inject(0) do |max_id, (name, fixture)|
|
||||
fixture_id = fixture['id'].to_i
|
||||
fixture_id > max_id ? fixture_id : max_id
|
||||
end
|
||||
|
||||
# Clone the last fixture to check that it gets the next greatest id.
|
||||
instance.save!
|
||||
assert_equal max_id + 1, instance.id, "Sequence reset for #{instance.class.table_name} failed."
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
|
||||
self.use_instantiated_fixtures = false
|
||||
fixtures :topics, :developers, :accounts
|
||||
|
||||
def test_without_complete_instantiation
|
||||
assert_nil @first
|
||||
assert_nil @topics
|
||||
assert_nil @developers
|
||||
assert_nil @accounts
|
||||
end
|
||||
|
||||
def test_fixtures_from_root_yml_without_instantiation
|
||||
assert_nil @unknown
|
||||
end
|
||||
|
||||
def test_visibility_of_accessor_method
|
||||
assert_equal false, respond_to?(:topics, false), "should be private method"
|
||||
assert_equal true, respond_to?(:topics, true), "confirm to respond surely"
|
||||
end
|
||||
|
||||
def test_accessor_methods
|
||||
assert_equal "The First Topic", topics(:first).title
|
||||
assert_equal "Jamis", developers(:jamis).name
|
||||
assert_equal 50, accounts(:signals37).credit_limit
|
||||
end
|
||||
|
||||
def test_accessor_methods_with_multiple_args
|
||||
assert_equal 2, topics(:first, :second).size
|
||||
assert_raise(StandardError) { topics([:first, :second]) }
|
||||
end
|
||||
|
||||
def test_reloading_fixtures_through_accessor_methods
|
||||
assert_equal "The First Topic", topics(:first).title
|
||||
@loaded_fixtures['topics']['first'].expects(:find).returns(stub(:title => "Fresh Topic!"))
|
||||
assert_equal "Fresh Topic!", topics(:first, true).title
|
||||
end
|
||||
end
|
||||
|
||||
class FixturesWithoutInstanceInstantiationTest < ActiveRecord::TestCase
|
||||
self.use_instantiated_fixtures = true
|
||||
self.use_instantiated_fixtures = :no_instances
|
||||
|
||||
fixtures :topics, :developers, :accounts
|
||||
|
||||
def test_without_instance_instantiation
|
||||
assert_nil @first
|
||||
assert_not_nil @topics
|
||||
assert_not_nil @developers
|
||||
assert_not_nil @accounts
|
||||
end
|
||||
end
|
||||
|
||||
class TransactionalFixturesTest < ActiveRecord::TestCase
|
||||
self.use_instantiated_fixtures = true
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
fixtures :topics
|
||||
|
||||
def test_destroy
|
||||
assert_not_nil @first
|
||||
@first.destroy
|
||||
end
|
||||
|
||||
def test_destroy_just_kidding
|
||||
assert_not_nil @first
|
||||
end
|
||||
end
|
||||
|
||||
class MultipleFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :topics
|
||||
fixtures :developers, :accounts
|
||||
|
||||
def test_fixture_table_names
|
||||
assert_equal %w(topics developers accounts), fixture_table_names
|
||||
end
|
||||
end
|
||||
|
||||
class SetupTest < ActiveRecord::TestCase
|
||||
# fixtures :topics
|
||||
|
||||
def setup
|
||||
@first = true
|
||||
end
|
||||
|
||||
def test_nothing
|
||||
end
|
||||
end
|
||||
|
||||
class SetupSubclassTest < SetupTest
|
||||
def setup
|
||||
super
|
||||
@second = true
|
||||
end
|
||||
|
||||
def test_subclassing_should_preserve_setups
|
||||
assert @first
|
||||
assert @second
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class OverlappingFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :developers
|
||||
fixtures :developers, :accounts
|
||||
|
||||
def test_fixture_table_names
|
||||
assert_equal %w(topics developers accounts), fixture_table_names
|
||||
end
|
||||
end
|
||||
|
||||
class ForeignKeyFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :fk_test_has_pk, :fk_test_has_fk
|
||||
|
||||
# if foreign keys are implemented and fixtures
|
||||
# are not deleted in reverse order then this test
|
||||
# case will raise StatementInvalid
|
||||
|
||||
def test_number1
|
||||
assert true
|
||||
end
|
||||
|
||||
def test_number2
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
|
||||
set_fixture_class :funny_jokes => 'Joke'
|
||||
fixtures :funny_jokes
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_table_method
|
||||
assert_kind_of Joke, funny_jokes(:a_joke)
|
||||
end
|
||||
end
|
||||
|
||||
class FixtureNameIsNotTableNameFixturesTest < ActiveRecord::TestCase
|
||||
set_fixture_class :items => Book
|
||||
fixtures :items
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_named_accessor
|
||||
assert_kind_of Book, items(:dvd)
|
||||
end
|
||||
end
|
||||
|
||||
class FixtureNameIsNotTableNameMultipleFixturesTest < ActiveRecord::TestCase
|
||||
set_fixture_class :items => Book, :funny_jokes => Joke
|
||||
fixtures :items, :funny_jokes
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_named_accessor_of_differently_named_fixture
|
||||
assert_kind_of Book, items(:dvd)
|
||||
end
|
||||
|
||||
def test_named_accessor_of_same_named_fixture
|
||||
assert_kind_of Joke, funny_jokes(:a_joke)
|
||||
end
|
||||
end
|
||||
|
||||
class CustomConnectionFixturesTest < ActiveRecord::TestCase
|
||||
set_fixture_class :courses => Course
|
||||
fixtures :courses
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_connection
|
||||
assert_kind_of Course, courses(:ruby)
|
||||
assert_equal Course.connection, courses(:ruby).connection
|
||||
end
|
||||
end
|
||||
|
||||
class InvalidTableNameFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :funny_jokes
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our lack of set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_raises_error
|
||||
assert_raise FixtureClassNotFound do
|
||||
funny_jokes(:a_joke)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class CheckEscapedYamlFixturesTest < ActiveRecord::TestCase
|
||||
set_fixture_class :funny_jokes => 'Joke'
|
||||
fixtures :funny_jokes
|
||||
# Set to false to blow away fixtures cache and ensure our fixtures are loaded
|
||||
# and thus takes into account our set_fixture_class
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_proper_escaped_fixture
|
||||
assert_equal "The \\n Aristocrats\nAte the candy\n", funny_jokes(:another_joke).name
|
||||
end
|
||||
end
|
||||
|
||||
class DevelopersProject; end
|
||||
class ManyToManyFixturesWithClassDefined < ActiveRecord::TestCase
|
||||
fixtures :developers_projects
|
||||
|
||||
def test_this_should_run_cleanly
|
||||
assert true
|
||||
end
|
||||
end
|
||||
|
||||
class FixturesBrokenRollbackTest < ActiveRecord::TestCase
|
||||
def blank_setup; end
|
||||
alias_method :ar_setup_fixtures, :setup_fixtures
|
||||
alias_method :setup_fixtures, :blank_setup
|
||||
alias_method :setup, :blank_setup
|
||||
|
||||
def blank_teardown; end
|
||||
alias_method :ar_teardown_fixtures, :teardown_fixtures
|
||||
alias_method :teardown_fixtures, :blank_teardown
|
||||
alias_method :teardown, :blank_teardown
|
||||
|
||||
def test_no_rollback_in_teardown_unless_transaction_active
|
||||
assert_equal 0, ActiveRecord::Base.connection.open_transactions
|
||||
assert_raise(RuntimeError) { ar_setup_fixtures }
|
||||
assert_equal 0, ActiveRecord::Base.connection.open_transactions
|
||||
assert_nothing_raised { ar_teardown_fixtures }
|
||||
assert_equal 0, ActiveRecord::Base.connection.open_transactions
|
||||
end
|
||||
|
||||
private
|
||||
def load_fixtures
|
||||
raise 'argh'
|
||||
end
|
||||
end
|
||||
|
||||
class LoadAllFixturesTest < ActiveRecord::TestCase
|
||||
self.fixture_path = FIXTURES_ROOT + "/all"
|
||||
fixtures :all
|
||||
|
||||
def test_all_there
|
||||
assert_equal %w(developers people tasks), fixture_table_names.sort
|
||||
end
|
||||
end
|
||||
|
||||
class FasterFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :categories, :authors
|
||||
|
||||
def load_extra_fixture(name)
|
||||
fixture = create_fixtures(name)
|
||||
assert fixture.is_a?(Fixtures)
|
||||
@loaded_fixtures[fixture.table_name] = fixture
|
||||
end
|
||||
|
||||
def test_cache
|
||||
assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'categories')
|
||||
assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'authors')
|
||||
|
||||
assert_no_queries do
|
||||
create_fixtures('categories')
|
||||
create_fixtures('authors')
|
||||
end
|
||||
|
||||
load_extra_fixture('posts')
|
||||
assert Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
|
||||
self.class.setup_fixture_accessors('posts')
|
||||
assert_equal 'Welcome to the weblog', posts(:welcome).title
|
||||
end
|
||||
end
|
||||
|
||||
class FoxyFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers, :developers
|
||||
|
||||
def test_identifies_strings
|
||||
assert_equal(Fixtures.identify("foo"), Fixtures.identify("foo"))
|
||||
assert_not_equal(Fixtures.identify("foo"), Fixtures.identify("FOO"))
|
||||
end
|
||||
|
||||
def test_identifies_symbols
|
||||
assert_equal(Fixtures.identify(:foo), Fixtures.identify(:foo))
|
||||
end
|
||||
|
||||
def test_identifies_consistently
|
||||
assert_equal 207281424, Fixtures.identify(:ruby)
|
||||
assert_equal 1066363776, Fixtures.identify(:sapphire_2)
|
||||
end
|
||||
|
||||
TIMESTAMP_COLUMNS = %w(created_at created_on updated_at updated_on)
|
||||
|
||||
def test_populates_timestamp_columns
|
||||
TIMESTAMP_COLUMNS.each do |property|
|
||||
assert_not_nil(parrots(:george).send(property), "should set #{property}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_does_not_populate_timestamp_columns_if_model_has_set_record_timestamps_to_false
|
||||
TIMESTAMP_COLUMNS.each do |property|
|
||||
assert_nil(ships(:black_pearl).send(property), "should not set #{property}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_populates_all_columns_with_the_same_time
|
||||
last = nil
|
||||
|
||||
TIMESTAMP_COLUMNS.each do |property|
|
||||
current = parrots(:george).send(property)
|
||||
last ||= current
|
||||
|
||||
assert_equal(last, current)
|
||||
last = current
|
||||
end
|
||||
end
|
||||
|
||||
def test_only_populates_columns_that_exist
|
||||
assert_not_nil(pirates(:blackbeard).created_on)
|
||||
assert_not_nil(pirates(:blackbeard).updated_on)
|
||||
end
|
||||
|
||||
def test_preserves_existing_fixture_data
|
||||
assert_equal(2.weeks.ago.to_date, pirates(:redbeard).created_on.to_date)
|
||||
assert_equal(2.weeks.ago.to_date, pirates(:redbeard).updated_on.to_date)
|
||||
end
|
||||
|
||||
def test_generates_unique_ids
|
||||
assert_not_nil(parrots(:george).id)
|
||||
assert_not_equal(parrots(:george).id, parrots(:louis).id)
|
||||
end
|
||||
|
||||
def test_automatically_sets_primary_key
|
||||
assert_not_nil(ships(:black_pearl))
|
||||
end
|
||||
|
||||
def test_preserves_existing_primary_key
|
||||
assert_equal(2, ships(:interceptor).id)
|
||||
end
|
||||
|
||||
def test_resolves_belongs_to_symbols
|
||||
assert_equal(parrots(:george), pirates(:blackbeard).parrot)
|
||||
end
|
||||
|
||||
def test_ignores_belongs_to_symbols_if_association_and_foreign_key_are_named_the_same
|
||||
assert_equal(developers(:david), computers(:workstation).developer)
|
||||
end
|
||||
|
||||
def test_supports_join_tables
|
||||
assert(pirates(:blackbeard).parrots.include?(parrots(:george)))
|
||||
assert(pirates(:blackbeard).parrots.include?(parrots(:louis)))
|
||||
assert(parrots(:george).pirates.include?(pirates(:blackbeard)))
|
||||
end
|
||||
|
||||
def test_supports_inline_habtm
|
||||
assert(parrots(:george).treasures.include?(treasures(:diamond)))
|
||||
assert(parrots(:george).treasures.include?(treasures(:sapphire)))
|
||||
assert(!parrots(:george).treasures.include?(treasures(:ruby)))
|
||||
end
|
||||
|
||||
def test_supports_inline_habtm_with_specified_id
|
||||
assert(parrots(:polly).treasures.include?(treasures(:ruby)))
|
||||
assert(parrots(:polly).treasures.include?(treasures(:sapphire)))
|
||||
assert(!parrots(:polly).treasures.include?(treasures(:diamond)))
|
||||
end
|
||||
|
||||
def test_supports_yaml_arrays
|
||||
assert(parrots(:louis).treasures.include?(treasures(:diamond)))
|
||||
assert(parrots(:louis).treasures.include?(treasures(:sapphire)))
|
||||
end
|
||||
|
||||
def test_strips_DEFAULTS_key
|
||||
assert_raise(StandardError) { parrots(:DEFAULTS) }
|
||||
|
||||
# this lets us do YAML defaults and not have an extra fixture entry
|
||||
%w(sapphire ruby).each { |t| assert(parrots(:davey).treasures.include?(treasures(t))) }
|
||||
end
|
||||
|
||||
def test_supports_label_interpolation
|
||||
assert_equal("frederick", parrots(:frederick).name)
|
||||
end
|
||||
|
||||
def test_supports_polymorphic_belongs_to
|
||||
assert_equal(pirates(:redbeard), treasures(:sapphire).looter)
|
||||
assert_equal(parrots(:louis), treasures(:ruby).looter)
|
||||
end
|
||||
|
||||
def test_only_generates_a_pk_if_necessary
|
||||
m = Matey.find(:first)
|
||||
m.pirate = pirates(:blackbeard)
|
||||
m.target = pirates(:redbeard)
|
||||
end
|
||||
|
||||
def test_supports_sti
|
||||
assert_kind_of DeadParrot, parrots(:polly)
|
||||
assert_equal pirates(:blackbeard), parrots(:polly).killer
|
||||
end
|
||||
end
|
||||
|
||||
class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase
|
||||
fixtures :parrots
|
||||
|
||||
# This seemingly useless assertion catches a bug that caused the fixtures
|
||||
# setup code call nil[]
|
||||
def test_foo
|
||||
assert_equal parrots(:louis), Parrot.find_by_name("King Louis")
|
||||
end
|
||||
end
|
||||
|
||||
class FixtureLoadingTest < ActiveRecord::TestCase
|
||||
def test_logs_message_for_failed_dependency_load
|
||||
ActiveRecord::TestCase.expects(:require_dependency).with(:does_not_exist).raises(LoadError)
|
||||
ActiveRecord::Base.logger.expects(:warn)
|
||||
ActiveRecord::TestCase.try_to_load_dependency(:does_not_exist)
|
||||
end
|
||||
|
||||
def test_does_not_logs_message_for_successful_dependency_load
|
||||
ActiveRecord::TestCase.expects(:require_dependency).with(:works_out_fine)
|
||||
ActiveRecord::Base.logger.expects(:warn).never
|
||||
ActiveRecord::TestCase.try_to_load_dependency(:works_out_fine)
|
||||
end
|
||||
end
|
||||
68
vendor/rails/activerecord/test/cases/helper.rb
vendored
68
vendor/rails/activerecord/test/cases/helper.rb
vendored
|
|
@ -1,68 +0,0 @@
|
|||
$:.unshift(File.dirname(__FILE__) + '/../../lib')
|
||||
$:.unshift(File.dirname(__FILE__) + '/../../../activesupport/lib')
|
||||
|
||||
require 'config'
|
||||
|
||||
require 'rubygems'
|
||||
require 'test/unit'
|
||||
require 'stringio'
|
||||
|
||||
require 'active_record'
|
||||
require 'active_record/test_case'
|
||||
require 'active_record/fixtures'
|
||||
require 'connection'
|
||||
|
||||
require 'cases/repair_helper'
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
# Quote "type" if it's a reserved word for the current connection.
|
||||
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type')
|
||||
|
||||
def current_adapter?(*types)
|
||||
types.any? do |type|
|
||||
ActiveRecord::ConnectionAdapters.const_defined?(type) &&
|
||||
ActiveRecord::Base.connection.is_a?(ActiveRecord::ConnectionAdapters.const_get(type))
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.connection.class.class_eval do
|
||||
IGNORED_SQL = [/^PRAGMA/, /^SELECT currval/, /^SELECT CAST/, /^SELECT @@IDENTITY/, /^SELECT @@ROWCOUNT/, /^SAVEPOINT/, /^ROLLBACK TO SAVEPOINT/, /^RELEASE SAVEPOINT/, /SHOW FIELDS/]
|
||||
|
||||
def execute_with_query_record(sql, name = nil, &block)
|
||||
$queries_executed ||= []
|
||||
$queries_executed << sql unless IGNORED_SQL.any? { |r| sql =~ r }
|
||||
execute_without_query_record(sql, name, &block)
|
||||
end
|
||||
|
||||
alias_method_chain :execute, :query_record
|
||||
end
|
||||
|
||||
# Make with_scope public for tests
|
||||
class << ActiveRecord::Base
|
||||
public :with_scope, :with_exclusive_scope
|
||||
end
|
||||
|
||||
unless ENV['FIXTURE_DEBUG']
|
||||
module ActiveRecord::TestFixtures::ClassMethods
|
||||
def try_to_load_dependency_with_silence(*args)
|
||||
ActiveRecord::Base.logger.silence { try_to_load_dependency_without_silence(*args)}
|
||||
end
|
||||
|
||||
alias_method_chain :try_to_load_dependency, :silence
|
||||
end
|
||||
end
|
||||
|
||||
class ActiveSupport::TestCase
|
||||
include ActiveRecord::TestFixtures
|
||||
include ActiveRecord::Testing::RepairHelper
|
||||
|
||||
self.fixture_path = FIXTURES_ROOT
|
||||
self.use_instantiated_fixtures = false
|
||||
self.use_transactional_fixtures = true
|
||||
|
||||
def create_fixtures(*table_names, &block)
|
||||
Fixtures.create_fixtures(ActiveSupport::TestCase.fixture_path, table_names, {}, &block)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
|
||||
class ActiveRecordI18nTests < Test::Unit::TestCase
|
||||
|
||||
def setup
|
||||
I18n.backend = I18n::Backend::Simple.new
|
||||
end
|
||||
|
||||
def test_translated_model_attributes
|
||||
I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
|
||||
assert_equal 'topic title attribute', Topic.human_attribute_name('title')
|
||||
end
|
||||
|
||||
def test_translated_model_attributes_with_symbols
|
||||
I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
|
||||
assert_equal 'topic title attribute', Topic.human_attribute_name(:title)
|
||||
end
|
||||
|
||||
def test_translated_model_attributes_with_sti
|
||||
I18n.backend.store_translations 'en', :activerecord => {:attributes => {:reply => {:title => 'reply title attribute'} } }
|
||||
assert_equal 'reply title attribute', Reply.human_attribute_name('title')
|
||||
end
|
||||
|
||||
def test_translated_model_attributes_with_sti_fallback
|
||||
I18n.backend.store_translations 'en', :activerecord => {:attributes => {:topic => {:title => 'topic title attribute'} } }
|
||||
assert_equal 'topic title attribute', Reply.human_attribute_name('title')
|
||||
end
|
||||
|
||||
def test_translated_model_names
|
||||
I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
|
||||
assert_equal 'topic model', Topic.human_name
|
||||
end
|
||||
|
||||
def test_translated_model_names_with_sti
|
||||
I18n.backend.store_translations 'en', :activerecord => {:models => {:reply => 'reply model'} }
|
||||
assert_equal 'reply model', Reply.human_name
|
||||
end
|
||||
|
||||
def test_translated_model_names_with_sti_fallback
|
||||
I18n.backend.store_translations 'en', :activerecord => {:models => {:topic => 'topic model'} }
|
||||
assert_equal 'topic model', Reply.human_name
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1,262 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/company'
|
||||
require 'models/project'
|
||||
require 'models/subscriber'
|
||||
|
||||
class InheritanceTest < ActiveRecord::TestCase
|
||||
fixtures :companies, :projects, :subscribers, :accounts
|
||||
|
||||
def test_class_with_store_full_sti_class_returns_full_name
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
assert_equal 'Namespaced::Company', Namespaced::Company.sti_name
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
|
||||
def test_class_without_store_full_sti_class_returns_demodulized_name
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
ActiveRecord::Base.store_full_sti_class = false
|
||||
assert_equal 'Company', Namespaced::Company.sti_name
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
|
||||
def test_should_store_demodulized_class_name_with_store_full_sti_class_option_disabled
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
ActiveRecord::Base.store_full_sti_class = false
|
||||
item = Namespaced::Company.new
|
||||
assert_equal 'Company', item[:type]
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
|
||||
def test_should_store_full_class_name_with_store_full_sti_class_option_enabled
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
item = Namespaced::Company.new
|
||||
assert_equal 'Namespaced::Company', item[:type]
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
|
||||
def test_different_namespace_subclass_should_load_correctly_with_store_full_sti_class_option
|
||||
old = ActiveRecord::Base.store_full_sti_class
|
||||
ActiveRecord::Base.store_full_sti_class = true
|
||||
item = Namespaced::Company.create :name => "Wolverine 2"
|
||||
assert_not_nil Company.find(item.id)
|
||||
assert_not_nil Namespaced::Company.find(item.id)
|
||||
ensure
|
||||
ActiveRecord::Base.store_full_sti_class = old
|
||||
end
|
||||
|
||||
def test_company_descends_from_active_record
|
||||
assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
|
||||
assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
|
||||
assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
|
||||
assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
|
||||
end
|
||||
|
||||
def test_a_bad_type_column
|
||||
#SQLServer need to turn Identity Insert On before manually inserting into the Identity column
|
||||
if current_adapter?(:SybaseAdapter)
|
||||
Company.connection.execute "SET IDENTITY_INSERT companies ON"
|
||||
end
|
||||
Company.connection.insert "INSERT INTO companies (id, #{QUOTED_TYPE}, name) VALUES(100, 'bad_class!', 'Not happening')"
|
||||
|
||||
#We then need to turn it back Off before continuing.
|
||||
if current_adapter?(:SybaseAdapter)
|
||||
Company.connection.execute "SET IDENTITY_INSERT companies OFF"
|
||||
end
|
||||
assert_raise(ActiveRecord::SubclassNotFound) { Company.find(100) }
|
||||
end
|
||||
|
||||
def test_inheritance_find
|
||||
assert Company.find(1).kind_of?(Firm), "37signals should be a firm"
|
||||
assert Firm.find(1).kind_of?(Firm), "37signals should be a firm"
|
||||
assert Company.find(2).kind_of?(Client), "Summit should be a client"
|
||||
assert Client.find(2).kind_of?(Client), "Summit should be a client"
|
||||
end
|
||||
|
||||
def test_alt_inheritance_find
|
||||
switch_to_alt_inheritance_column
|
||||
test_inheritance_find
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_inheritance_find_all
|
||||
companies = Company.find(:all, :order => 'id')
|
||||
assert companies[0].kind_of?(Firm), "37signals should be a firm"
|
||||
assert companies[1].kind_of?(Client), "Summit should be a client"
|
||||
end
|
||||
|
||||
def test_alt_inheritance_find_all
|
||||
switch_to_alt_inheritance_column
|
||||
test_inheritance_find_all
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_inheritance_save
|
||||
firm = Firm.new
|
||||
firm.name = "Next Angle"
|
||||
firm.save
|
||||
|
||||
next_angle = Company.find(firm.id)
|
||||
assert next_angle.kind_of?(Firm), "Next Angle should be a firm"
|
||||
end
|
||||
|
||||
def test_alt_inheritance_save
|
||||
switch_to_alt_inheritance_column
|
||||
test_inheritance_save
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_inheritance_condition
|
||||
assert_equal 9, Company.count
|
||||
assert_equal 2, Firm.count
|
||||
assert_equal 3, Client.count
|
||||
end
|
||||
|
||||
def test_alt_inheritance_condition
|
||||
switch_to_alt_inheritance_column
|
||||
test_inheritance_condition
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_finding_incorrect_type_data
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Firm.find(2) }
|
||||
assert_nothing_raised { Firm.find(1) }
|
||||
end
|
||||
|
||||
def test_alt_finding_incorrect_type_data
|
||||
switch_to_alt_inheritance_column
|
||||
test_finding_incorrect_type_data
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_update_all_within_inheritance
|
||||
Client.update_all "name = 'I am a client'"
|
||||
assert_equal "I am a client", Client.find(:all).first.name
|
||||
assert_equal "37signals", Firm.find(:all).first.name
|
||||
end
|
||||
|
||||
def test_alt_update_all_within_inheritance
|
||||
switch_to_alt_inheritance_column
|
||||
test_update_all_within_inheritance
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_destroy_all_within_inheritance
|
||||
Client.destroy_all
|
||||
assert_equal 0, Client.count
|
||||
assert_equal 2, Firm.count
|
||||
end
|
||||
|
||||
def test_alt_destroy_all_within_inheritance
|
||||
switch_to_alt_inheritance_column
|
||||
test_destroy_all_within_inheritance
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_find_first_within_inheritance
|
||||
assert_kind_of Firm, Company.find(:first, :conditions => "name = '37signals'")
|
||||
assert_kind_of Firm, Firm.find(:first, :conditions => "name = '37signals'")
|
||||
assert_nil Client.find(:first, :conditions => "name = '37signals'")
|
||||
end
|
||||
|
||||
def test_alt_find_first_within_inheritance
|
||||
switch_to_alt_inheritance_column
|
||||
test_find_first_within_inheritance
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_complex_inheritance
|
||||
very_special_client = VerySpecialClient.create("name" => "veryspecial")
|
||||
assert_equal very_special_client, VerySpecialClient.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, SpecialClient.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, Company.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal very_special_client, Client.find(:first, :conditions => "name = 'veryspecial'")
|
||||
assert_equal 1, Client.find(:all, :conditions => "name = 'Summit'").size
|
||||
assert_equal very_special_client, Client.find(very_special_client.id)
|
||||
end
|
||||
|
||||
def test_alt_complex_inheritance
|
||||
switch_to_alt_inheritance_column
|
||||
test_complex_inheritance
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_eager_load_belongs_to_something_inherited
|
||||
account = Account.find(1, :include => :firm)
|
||||
assert_not_nil account.instance_variable_get("@firm"), "nil proves eager load failed"
|
||||
end
|
||||
|
||||
def test_eager_load_belongs_to_primary_key_quoting
|
||||
con = Account.connection
|
||||
assert_sql(/\(#{con.quote_table_name('companies')}.#{con.quote_column_name('id')} = 1\)/) do
|
||||
Account.find(1, :include => :firm)
|
||||
end
|
||||
end
|
||||
|
||||
def test_alt_eager_loading
|
||||
switch_to_alt_inheritance_column
|
||||
test_eager_load_belongs_to_something_inherited
|
||||
switch_to_default_inheritance_column
|
||||
end
|
||||
|
||||
def test_inheritance_without_mapping
|
||||
assert_kind_of SpecialSubscriber, SpecialSubscriber.find("webster132")
|
||||
assert_nothing_raised { s = SpecialSubscriber.new("name" => "And breaaaaathe!"); s.id = 'roger'; s.save }
|
||||
end
|
||||
|
||||
private
|
||||
def switch_to_alt_inheritance_column
|
||||
# we don't want misleading test results, so get rid of the values in the type column
|
||||
Company.find(:all, :order => 'id').each do |c|
|
||||
c['type'] = nil
|
||||
c.save
|
||||
end
|
||||
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
|
||||
Company.set_inheritance_column('ruby_type')
|
||||
end
|
||||
def switch_to_default_inheritance_column
|
||||
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
|
||||
Company.set_inheritance_column('type')
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class InheritanceComputeTypeTest < ActiveRecord::TestCase
|
||||
fixtures :companies
|
||||
|
||||
def setup
|
||||
ActiveSupport::Dependencies.log_activity = true
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Dependencies.log_activity = false
|
||||
self.class.const_remove :FirmOnTheFly rescue nil
|
||||
Firm.const_remove :FirmOnTheFly rescue nil
|
||||
end
|
||||
|
||||
def test_instantiation_doesnt_try_to_require_corresponding_file
|
||||
foo = Firm.find(:first).clone
|
||||
foo.ruby_type = foo.type = 'FirmOnTheFly'
|
||||
foo.save!
|
||||
|
||||
# Should fail without FirmOnTheFly in the type condition.
|
||||
assert_raise(ActiveRecord::RecordNotFound) { Firm.find(foo.id) }
|
||||
|
||||
# Nest FirmOnTheFly in the test case where Dependencies won't see it.
|
||||
self.class.const_set :FirmOnTheFly, Class.new(Firm)
|
||||
assert_raise(ActiveRecord::SubclassNotFound) { Firm.find(foo.id) }
|
||||
|
||||
# Nest FirmOnTheFly in Firm where Dependencies will see it.
|
||||
# This is analogous to nesting models in a migration.
|
||||
Firm.const_set :FirmOnTheFly, Class.new(Firm)
|
||||
|
||||
# And instantiate will find the existing constant rather than trying
|
||||
# to require firm_on_the_fly.
|
||||
assert_nothing_raised { assert_kind_of Firm::FirmOnTheFly, Firm.find(foo.id) }
|
||||
end
|
||||
end
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/topic'
|
||||
|
||||
class InvalidDateTest < Test::Unit::TestCase
|
||||
def test_assign_valid_dates
|
||||
valid_dates = [[2007, 11, 30], [1993, 2, 28], [2008, 2, 29]]
|
||||
|
||||
invalid_dates = [[2007, 11, 31], [1993, 2, 29], [2007, 2, 29]]
|
||||
|
||||
topic = Topic.new
|
||||
|
||||
valid_dates.each do |date_src|
|
||||
topic = Topic.new("last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s)
|
||||
assert_equal(topic.last_read, Date.new(*date_src))
|
||||
end
|
||||
|
||||
invalid_dates.each do |date_src|
|
||||
assert_nothing_raised do
|
||||
topic = Topic.new({"last_read(1i)" => date_src[0].to_s, "last_read(2i)" => date_src[1].to_s, "last_read(3i)" => date_src[2].to_s})
|
||||
assert_equal(topic.last_read, Time.local(*date_src).to_date, "The date should be modified according to the behaviour of the Time object")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,219 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/contact'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/tagging'
|
||||
require 'models/tag'
|
||||
require 'models/comment'
|
||||
|
||||
class JsonSerializationTest < ActiveRecord::TestCase
|
||||
class NamespacedContact < Contact
|
||||
column :name, :string
|
||||
end
|
||||
|
||||
def setup
|
||||
@contact = Contact.new(
|
||||
:name => 'Konata Izumi',
|
||||
:age => 16,
|
||||
:avatar => 'binarydata',
|
||||
:created_at => Time.utc(2006, 8, 1),
|
||||
:awesome => true,
|
||||
:preferences => { :shows => 'anime' }
|
||||
)
|
||||
end
|
||||
|
||||
def test_should_demodulize_root_in_json
|
||||
NamespacedContact.include_root_in_json = true
|
||||
@contact = NamespacedContact.new :name => 'whatever'
|
||||
json = @contact.to_json
|
||||
assert_match %r{^\{"namespaced_contact":\{}, json
|
||||
end
|
||||
|
||||
def test_should_include_root_in_json
|
||||
Contact.include_root_in_json = true
|
||||
json = @contact.to_json
|
||||
|
||||
assert_match %r{^\{"contact":\{}, json
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
ensure
|
||||
Contact.include_root_in_json = false
|
||||
end
|
||||
|
||||
def test_should_include_root_in_json
|
||||
Contact.include_root_in_json = true
|
||||
json = @contact.to_json(:root => 'json_contact')
|
||||
|
||||
assert_match %r{^\{"json_contact":\{}, json
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
ensure
|
||||
Contact.include_root_in_json = false
|
||||
end
|
||||
|
||||
def test_should_encode_all_encodable_attributes
|
||||
json = @contact.to_json
|
||||
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering_with_only
|
||||
json = @contact.to_json(:only => [:name, :age])
|
||||
|
||||
assert_match %r{"name":"Konata Izumi"}, json
|
||||
assert_match %r{"age":16}, json
|
||||
assert_no_match %r{"awesome":true}, json
|
||||
assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_no_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_should_allow_attribute_filtering_with_except
|
||||
json = @contact.to_json(:except => [:name, :age])
|
||||
|
||||
assert_no_match %r{"name":"Konata Izumi"}, json
|
||||
assert_no_match %r{"age":16}, json
|
||||
assert_match %r{"awesome":true}, json
|
||||
assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
|
||||
assert_match %r{"preferences":\{"shows":"anime"\}}, json
|
||||
end
|
||||
|
||||
def test_methods_are_called_on_object
|
||||
# Define methods on fixture.
|
||||
def @contact.label; "Has cheezburger"; end
|
||||
def @contact.favorite_quote; "Constraints are liberating"; end
|
||||
|
||||
# Single method.
|
||||
assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
|
||||
|
||||
# Both methods.
|
||||
methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
|
||||
assert_match %r{"label":"Has cheezburger"}, methods_json
|
||||
assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json
|
||||
end
|
||||
end
|
||||
|
||||
class DatabaseConnectedJsonEncodingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :posts, :comments, :tags, :taggings
|
||||
|
||||
def setup
|
||||
@david = authors(:david)
|
||||
@mary = authors(:mary)
|
||||
end
|
||||
|
||||
def test_includes_uses_association_name
|
||||
json = @david.to_json(:include => :posts)
|
||||
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"id":1}, json
|
||||
assert_match %r{"name":"David"}, json
|
||||
|
||||
assert_match %r{"author_id":1}, json
|
||||
assert_match %r{"title":"Welcome to the weblog"}, json
|
||||
assert_match %r{"body":"Such a lovely day"}, json
|
||||
|
||||
assert_match %r{"title":"So I was thinking"}, json
|
||||
assert_match %r{"body":"Like I hopefully always am"}, json
|
||||
end
|
||||
|
||||
def test_includes_uses_association_name_and_applies_attribute_filters
|
||||
json = @david.to_json(:include => { :posts => { :only => :title } })
|
||||
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"title":"Welcome to the weblog"}, json
|
||||
assert_no_match %r{"body":"Such a lovely day"}, json
|
||||
|
||||
assert_match %r{"title":"So I was thinking"}, json
|
||||
assert_no_match %r{"body":"Like I hopefully always am"}, json
|
||||
end
|
||||
|
||||
def test_includes_fetches_second_level_associations
|
||||
json = @david.to_json(:include => { :posts => { :include => { :comments => { :only => :body } } } })
|
||||
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"comments":\[}, json
|
||||
assert_match %r{\{"body":"Thank you again for the welcome"\}}, json
|
||||
assert_match %r{\{"body":"Don't think too hard"\}}, json
|
||||
assert_no_match %r{"post_id":}, json
|
||||
end
|
||||
|
||||
def test_includes_fetches_nth_level_associations
|
||||
json = @david.to_json(
|
||||
:include => {
|
||||
:posts => {
|
||||
:include => {
|
||||
:taggings => {
|
||||
:include => {
|
||||
:tag => { :only => :name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
assert_match %r{"name":"David"}, json
|
||||
assert_match %r{"posts":\[}, json
|
||||
|
||||
assert_match %r{"taggings":\[}, json
|
||||
assert_match %r{"tag":\{"name":"General"\}}, json
|
||||
end
|
||||
|
||||
def test_should_not_call_methods_on_associations_that_dont_respond
|
||||
def @david.favorite_quote; "Constraints are liberating"; end
|
||||
json = @david.to_json(:include => :posts, :methods => :favorite_quote)
|
||||
|
||||
assert !@david.posts.first.respond_to?(:favorite_quote)
|
||||
assert_match %r{"favorite_quote":"Constraints are liberating"}, json
|
||||
assert_equal %r{"favorite_quote":}.match(json).size, 1
|
||||
end
|
||||
|
||||
def test_should_allow_only_option_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
|
||||
assert_equal %([{"name":"David"},{"name":"Mary"}]), ActiveSupport::JSON.encode(authors, :only => :name)
|
||||
end
|
||||
|
||||
def test_should_allow_except_option_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
|
||||
assert_equal %([{"id":1},{"id":2}]), ActiveSupport::JSON.encode(authors, :except => [:name, :author_address_id, :author_address_extra_id])
|
||||
end
|
||||
|
||||
def test_should_allow_includes_for_list_of_authors
|
||||
authors = [@david, @mary]
|
||||
json = ActiveSupport::JSON.encode(authors,
|
||||
:only => :name,
|
||||
:include => {
|
||||
:posts => { :only => :id }
|
||||
}
|
||||
)
|
||||
|
||||
['"name":"David"', '"posts":[', '{"id":1}', '{"id":2}', '{"id":4}',
|
||||
'{"id":5}', '{"id":6}', '"name":"Mary"', '"posts":[{"id":7}]'].each do |fragment|
|
||||
assert json.include?(fragment), json
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_options_for_hash_of_authors
|
||||
authors_hash = {
|
||||
1 => @david,
|
||||
2 => @mary
|
||||
}
|
||||
|
||||
assert_equal %({"1":{"name":"David"}}), ActiveSupport::JSON.encode(authors_hash, :only => [1, :name])
|
||||
end
|
||||
end
|
||||
|
|
@ -1,193 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/developer'
|
||||
require 'models/reply'
|
||||
require 'models/minimalistic'
|
||||
|
||||
class Topic; def after_find() end end
|
||||
class Developer; def after_find() end end
|
||||
class SpecialDeveloper < Developer; end
|
||||
|
||||
class TopicManualObserver
|
||||
include Singleton
|
||||
|
||||
attr_reader :action, :object, :callbacks
|
||||
|
||||
def initialize
|
||||
Topic.add_observer(self)
|
||||
@callbacks = []
|
||||
end
|
||||
|
||||
def update(callback_method, object)
|
||||
@callbacks << { "callback_method" => callback_method, "object" => object }
|
||||
end
|
||||
|
||||
def has_been_notified?
|
||||
!@callbacks.empty?
|
||||
end
|
||||
end
|
||||
|
||||
class TopicaAuditor < ActiveRecord::Observer
|
||||
observe :topic
|
||||
|
||||
attr_reader :topic
|
||||
|
||||
def after_find(topic)
|
||||
@topic = topic
|
||||
end
|
||||
end
|
||||
|
||||
class TopicObserver < ActiveRecord::Observer
|
||||
attr_reader :topic
|
||||
|
||||
def after_find(topic)
|
||||
@topic = topic
|
||||
end
|
||||
end
|
||||
|
||||
class MinimalisticObserver < ActiveRecord::Observer
|
||||
attr_reader :minimalistic
|
||||
|
||||
def after_find(minimalistic)
|
||||
@minimalistic = minimalistic
|
||||
end
|
||||
end
|
||||
|
||||
class MultiObserver < ActiveRecord::Observer
|
||||
attr_reader :record
|
||||
|
||||
def self.observed_class() [ Topic, Developer ] end
|
||||
|
||||
cattr_reader :last_inherited
|
||||
@@last_inherited = nil
|
||||
|
||||
def observed_class_inherited_with_testing(subclass)
|
||||
observed_class_inherited_without_testing(subclass)
|
||||
@@last_inherited = subclass
|
||||
end
|
||||
|
||||
alias_method_chain :observed_class_inherited, :testing
|
||||
|
||||
def after_find(record)
|
||||
@record = record
|
||||
end
|
||||
end
|
||||
|
||||
class LifecycleTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :developers, :minimalistics
|
||||
|
||||
def test_before_destroy
|
||||
original_count = Topic.count
|
||||
(topic_to_be_destroyed = Topic.find(1)).destroy
|
||||
assert_equal original_count - (1 + topic_to_be_destroyed.replies.size), Topic.count
|
||||
end
|
||||
|
||||
def test_after_save
|
||||
ActiveRecord::Base.observers = :topic_manual_observer
|
||||
ActiveRecord::Base.instantiate_observers
|
||||
|
||||
topic = Topic.find(1)
|
||||
topic.title = "hello"
|
||||
topic.save
|
||||
|
||||
assert TopicManualObserver.instance.has_been_notified?
|
||||
assert_equal :after_save, TopicManualObserver.instance.callbacks.last["callback_method"]
|
||||
end
|
||||
|
||||
def test_observer_update_on_save
|
||||
ActiveRecord::Base.observers = TopicManualObserver
|
||||
ActiveRecord::Base.instantiate_observers
|
||||
|
||||
topic = Topic.find(1)
|
||||
assert TopicManualObserver.instance.has_been_notified?
|
||||
assert_equal :after_find, TopicManualObserver.instance.callbacks.first["callback_method"]
|
||||
end
|
||||
|
||||
def test_auto_observer
|
||||
topic_observer = TopicaAuditor.instance
|
||||
assert_nil TopicaAuditor.observed_class
|
||||
assert_equal [Topic], TopicaAuditor.instance.observed_classes.to_a
|
||||
|
||||
topic = Topic.find(1)
|
||||
assert_equal topic.title, topic_observer.topic.title
|
||||
end
|
||||
|
||||
def test_inferred_auto_observer
|
||||
topic_observer = TopicObserver.instance
|
||||
assert_equal Topic, TopicObserver.observed_class
|
||||
|
||||
topic = Topic.find(1)
|
||||
assert_equal topic.title, topic_observer.topic.title
|
||||
end
|
||||
|
||||
def test_observing_two_classes
|
||||
multi_observer = MultiObserver.instance
|
||||
|
||||
topic = Topic.find(1)
|
||||
assert_equal topic.title, multi_observer.record.title
|
||||
|
||||
developer = Developer.find(1)
|
||||
assert_equal developer.name, multi_observer.record.name
|
||||
end
|
||||
|
||||
def test_observing_subclasses
|
||||
multi_observer = MultiObserver.instance
|
||||
|
||||
developer = SpecialDeveloper.find(1)
|
||||
assert_equal developer.name, multi_observer.record.name
|
||||
|
||||
klass = Class.new(Developer)
|
||||
assert_equal klass, multi_observer.last_inherited
|
||||
|
||||
developer = klass.find(1)
|
||||
assert_equal developer.name, multi_observer.record.name
|
||||
end
|
||||
|
||||
def test_after_find_can_be_observed_when_its_not_defined_on_the_model
|
||||
observer = MinimalisticObserver.instance
|
||||
assert_equal Minimalistic, MinimalisticObserver.observed_class
|
||||
|
||||
minimalistic = Minimalistic.find(1)
|
||||
assert_equal minimalistic, observer.minimalistic
|
||||
end
|
||||
|
||||
def test_after_find_can_be_observed_when_its_defined_on_the_model
|
||||
observer = TopicObserver.instance
|
||||
assert_equal Topic, TopicObserver.observed_class
|
||||
|
||||
topic = Topic.find(1)
|
||||
assert_equal topic, observer.topic
|
||||
end
|
||||
|
||||
def test_after_find_is_not_created_if_its_not_used
|
||||
# use a fresh class so an observer can't have defined an
|
||||
# after_find on it
|
||||
model_class = Class.new(ActiveRecord::Base)
|
||||
observer_class = Class.new(ActiveRecord::Observer)
|
||||
observer_class.observe(model_class)
|
||||
|
||||
observer = observer_class.instance
|
||||
|
||||
assert !model_class.method_defined?(:after_find)
|
||||
end
|
||||
|
||||
def test_after_find_is_not_clobbered_if_it_already_exists
|
||||
# use a fresh observer class so we can instantiate it (Observer is
|
||||
# a Singleton)
|
||||
model_class = Class.new(ActiveRecord::Base) do
|
||||
def after_find; end
|
||||
end
|
||||
original_method = model_class.instance_method(:after_find)
|
||||
observer_class = Class.new(ActiveRecord::Observer) do
|
||||
def after_find; end
|
||||
end
|
||||
observer_class.observe(model_class)
|
||||
|
||||
observer = observer_class.instance
|
||||
assert_equal original_method, model_class.instance_method(:after_find)
|
||||
end
|
||||
|
||||
def test_invalid_observer
|
||||
assert_raise(ArgumentError) { Topic.observers = Object.new; Topic.instantiate_observers }
|
||||
end
|
||||
end
|
||||
350
vendor/rails/activerecord/test/cases/locking_test.rb
vendored
350
vendor/rails/activerecord/test/cases/locking_test.rb
vendored
|
|
@ -1,350 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/person'
|
||||
require 'models/reader'
|
||||
require 'models/legacy_thing'
|
||||
require 'models/reference'
|
||||
|
||||
class LockWithoutDefault < ActiveRecord::Base; end
|
||||
|
||||
class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
|
||||
set_table_name :lock_without_defaults_cust
|
||||
set_locking_column :custom_lock_version
|
||||
end
|
||||
|
||||
class ReadonlyFirstNamePerson < Person
|
||||
attr_readonly :first_name
|
||||
end
|
||||
|
||||
class OptimisticLockingTest < ActiveRecord::TestCase
|
||||
fixtures :people, :legacy_things, :references
|
||||
|
||||
# need to disable transactional fixtures, because otherwise the sqlite3
|
||||
# adapter (at least) chokes when we try and change the schema in the middle
|
||||
# of a test (see test_increment_counter_*).
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def test_lock_existing
|
||||
p1 = Person.find(1)
|
||||
p2 = Person.find(1)
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p1.first_name = 'stu'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
# See Lighthouse ticket #1966
|
||||
def test_lock_destroy
|
||||
p1 = Person.find(1)
|
||||
p2 = Person.find(1)
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p1.first_name = 'stu'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
assert_raises(ActiveRecord::StaleObjectError) { p2.destroy }
|
||||
|
||||
assert p1.destroy
|
||||
assert p1.frozen?
|
||||
assert p1.destroyed?
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Person.find(1) }
|
||||
end
|
||||
|
||||
def test_lock_repeating
|
||||
p1 = Person.find(1)
|
||||
p2 = Person.find(1)
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p1.first_name = 'stu'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
p2.first_name = 'sue2'
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
def test_lock_new
|
||||
p1 = Person.new(:first_name => 'anika')
|
||||
assert_equal 0, p1.lock_version
|
||||
|
||||
p1.first_name = 'anika2'
|
||||
p1.save!
|
||||
p2 = Person.find(p1.id)
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p1.first_name = 'anika3'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal 0, p2.lock_version
|
||||
|
||||
p2.first_name = 'sue'
|
||||
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
|
||||
end
|
||||
|
||||
def test_lock_new_with_nil
|
||||
p1 = Person.new(:first_name => 'anika')
|
||||
p1.save!
|
||||
p1.lock_version = nil # simulate bad fixture or column with no default
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
end
|
||||
|
||||
|
||||
def test_lock_column_name_existing
|
||||
t1 = LegacyThing.find(1)
|
||||
t2 = LegacyThing.find(1)
|
||||
assert_equal 0, t1.version
|
||||
assert_equal 0, t2.version
|
||||
|
||||
t1.tps_report_number = 700
|
||||
t1.save!
|
||||
assert_equal 1, t1.version
|
||||
assert_equal 0, t2.version
|
||||
|
||||
t2.tps_report_number = 800
|
||||
assert_raise(ActiveRecord::StaleObjectError) { t2.save! }
|
||||
end
|
||||
|
||||
def test_lock_column_is_mass_assignable
|
||||
p1 = Person.create(:first_name => 'bianca')
|
||||
assert_equal 0, p1.lock_version
|
||||
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
||||
|
||||
p1.first_name = 'bianca2'
|
||||
p1.save!
|
||||
assert_equal 1, p1.lock_version
|
||||
assert_equal p1.lock_version, Person.new(p1.attributes).lock_version
|
||||
end
|
||||
|
||||
def test_lock_without_default_sets_version_to_zero
|
||||
t1 = LockWithoutDefault.new
|
||||
assert_equal 0, t1.lock_version
|
||||
end
|
||||
|
||||
def test_lock_with_custom_column_without_default_sets_version_to_zero
|
||||
t1 = LockWithCustomColumnWithoutDefault.new
|
||||
assert_equal 0, t1.custom_lock_version
|
||||
end
|
||||
|
||||
def test_readonly_attributes
|
||||
assert_equal Set.new([ 'first_name' ]), ReadonlyFirstNamePerson.readonly_attributes
|
||||
|
||||
p = ReadonlyFirstNamePerson.create(:first_name => "unchangeable name")
|
||||
p.reload
|
||||
assert_equal "unchangeable name", p.first_name
|
||||
|
||||
p.update_attributes(:first_name => "changed name")
|
||||
p.reload
|
||||
assert_equal "unchangeable name", p.first_name
|
||||
end
|
||||
|
||||
{ :lock_version => Person, :custom_lock_version => LegacyThing }.each do |name, model|
|
||||
define_method("test_increment_counter_updates_#{name}") do
|
||||
counter_test model, 1 do |id|
|
||||
model.increment_counter :test_count, id
|
||||
end
|
||||
end
|
||||
|
||||
define_method("test_decrement_counter_updates_#{name}") do
|
||||
counter_test model, -1 do |id|
|
||||
model.decrement_counter :test_count, id
|
||||
end
|
||||
end
|
||||
|
||||
define_method("test_update_counters_updates_#{name}") do
|
||||
counter_test model, 1 do |id|
|
||||
model.update_counters id, :test_count => 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# See Lighthouse ticket #1966
|
||||
def test_destroy_dependents
|
||||
# Establish dependent relationship between People and LegacyThing
|
||||
add_counter_column_to(Person, 'legacy_things_count')
|
||||
LegacyThing.connection.add_column LegacyThing.table_name, 'person_id', :integer
|
||||
LegacyThing.reset_column_information
|
||||
LegacyThing.class_eval do
|
||||
belongs_to :person, :counter_cache => true
|
||||
end
|
||||
Person.class_eval do
|
||||
has_many :legacy_things, :dependent => :destroy
|
||||
end
|
||||
|
||||
# Make sure that counter incrementing doesn't cause problems
|
||||
p1 = Person.new(:first_name => 'fjord')
|
||||
p1.save!
|
||||
t = LegacyThing.new(:person => p1)
|
||||
t.save!
|
||||
p1.reload
|
||||
assert_equal 1, p1.legacy_things_count
|
||||
assert p1.destroy
|
||||
assert_equal true, p1.frozen?
|
||||
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
|
||||
assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
|
||||
end
|
||||
|
||||
def test_quote_table_name
|
||||
ref = references(:michael_magician)
|
||||
ref.favourite = !ref.favourite
|
||||
assert ref.save
|
||||
end
|
||||
|
||||
# Useful for partial updates, don't only update the lock_version if there
|
||||
# is nothing else being updated.
|
||||
def test_update_without_attributes_does_not_only_update_lock_version
|
||||
assert_nothing_raised do
|
||||
p1 = Person.new(:first_name => 'anika')
|
||||
p1.send(:update_with_lock, [])
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_counter_column_to(model, col='test_count')
|
||||
model.connection.add_column model.table_name, col, :integer, :null => false, :default => 0
|
||||
model.reset_column_information
|
||||
# OpenBase does not set a value to existing rows when adding a not null default column
|
||||
model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
|
||||
end
|
||||
|
||||
def remove_counter_column_from(model)
|
||||
model.connection.remove_column model.table_name, :test_count
|
||||
model.reset_column_information
|
||||
end
|
||||
|
||||
def counter_test(model, expected_count)
|
||||
add_counter_column_to(model)
|
||||
object = model.find(:first)
|
||||
assert_equal 0, object.test_count
|
||||
assert_equal 0, object.send(model.locking_column)
|
||||
yield object.id
|
||||
object.reload
|
||||
assert_equal expected_count, object.test_count
|
||||
assert_equal 1, object.send(model.locking_column)
|
||||
ensure
|
||||
remove_counter_column_from(model)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# TODO: test against the generated SQL since testing locking behavior itself
|
||||
# is so cumbersome. Will deadlock Ruby threads if the underlying db.execute
|
||||
# blocks, so separate script called by Kernel#system is needed.
|
||||
# (See exec vs. async_exec in the PostgreSQL adapter.)
|
||||
|
||||
# TODO: The Sybase, and OpenBase adapters currently have no support for pessimistic locking
|
||||
|
||||
unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
|
||||
class PessimisticLockingTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
fixtures :people, :readers
|
||||
|
||||
def setup
|
||||
# Avoid introspection queries during tests.
|
||||
Person.columns; Reader.columns
|
||||
end
|
||||
|
||||
# Test typical find.
|
||||
def test_sane_find_with_lock
|
||||
assert_nothing_raised do
|
||||
Person.transaction do
|
||||
Person.find 1, :lock => true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Test scoped lock.
|
||||
def test_sane_find_with_scoped_lock
|
||||
assert_nothing_raised do
|
||||
Person.transaction do
|
||||
Person.with_scope(:find => { :lock => true }) do
|
||||
Person.find 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PostgreSQL protests SELECT ... FOR UPDATE on an outer join.
|
||||
unless current_adapter?(:PostgreSQLAdapter)
|
||||
# Test locked eager find.
|
||||
def test_eager_find_with_lock
|
||||
assert_nothing_raised do
|
||||
Person.transaction do
|
||||
Person.find 1, :include => :readers, :lock => true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Locking a record reloads it.
|
||||
def test_sane_lock_method
|
||||
assert_nothing_raised do
|
||||
Person.transaction do
|
||||
person = Person.find 1
|
||||
old, person.first_name = person.first_name, 'fooman'
|
||||
person.lock!
|
||||
assert_equal old, person.first_name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
|
||||
use_concurrent_connections
|
||||
|
||||
def test_no_locks_no_wait
|
||||
first, second = duel { Person.find 1 }
|
||||
assert first.end > second.end
|
||||
end
|
||||
|
||||
def test_second_lock_waits
|
||||
assert [0.2, 1, 5].any? { |zzz|
|
||||
first, second = duel(zzz) { Person.find 1, :lock => true }
|
||||
second.end > first.end
|
||||
}
|
||||
end
|
||||
|
||||
protected
|
||||
def duel(zzz = 5)
|
||||
t0, t1, t2, t3 = nil, nil, nil, nil
|
||||
|
||||
a = Thread.new do
|
||||
t0 = Time.now
|
||||
Person.transaction do
|
||||
yield
|
||||
sleep zzz # block thread 2 for zzz seconds
|
||||
end
|
||||
t1 = Time.now
|
||||
end
|
||||
|
||||
b = Thread.new do
|
||||
sleep zzz / 2.0 # ensure thread 1 tx starts first
|
||||
t2 = Time.now
|
||||
Person.transaction { yield }
|
||||
t3 = Time.now
|
||||
end
|
||||
|
||||
a.join
|
||||
b.join
|
||||
|
||||
assert t1 > t0 + zzz
|
||||
assert t2 > t0
|
||||
assert t3 > t2
|
||||
[t0.to_f..t1.to_f, t2.to_f..t3.to_f]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,704 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/comment'
|
||||
require 'models/category'
|
||||
|
||||
class MethodScopingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :developers, :projects, :comments, :posts, :developers_projects
|
||||
|
||||
def test_set_conditions
|
||||
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
|
||||
assert_equal 'just a test...', Developer.send(:current_scoped_methods)[:find][:conditions]
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
assert_nothing_raised { Developer.find(1) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_first
|
||||
Developer.with_scope(:find => { :conditions => "salary = 100000" }) do
|
||||
assert_equal Developer.find(10), Developer.find(:first, :order => 'name')
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_last
|
||||
highest_salary = Developer.find(:first, :order => "salary DESC")
|
||||
|
||||
Developer.with_scope(:find => { :order => "salary" }) do
|
||||
assert_equal highest_salary, Developer.last
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_last_preserves_scope
|
||||
lowest_salary = Developer.find(:first, :order => "salary ASC")
|
||||
highest_salary = Developer.find(:first, :order => "salary DESC")
|
||||
|
||||
Developer.with_scope(:find => { :order => "salary" }) do
|
||||
assert_equal highest_salary, Developer.last
|
||||
assert_equal lowest_salary, Developer.first
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_combines_conditions
|
||||
Developer.with_scope(:find => { :conditions => "salary = 9000" }) do
|
||||
assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => "name = 'Jamis'")
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_sanitizes_conditions
|
||||
Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
|
||||
assert_equal developers(:poor_jamis), Developer.find(:first)
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_combines_and_sanitizes_conditions
|
||||
Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
|
||||
assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_all
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
assert_equal [developers(:david)], Developer.find(:all)
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_select
|
||||
Developer.with_scope(:find => { :select => "id, name" }) do
|
||||
developer = Developer.find(:first, :conditions => "name = 'David'")
|
||||
assert_equal "David", developer.name
|
||||
assert !developer.has_attribute?(:salary)
|
||||
end
|
||||
end
|
||||
|
||||
def test_options_select_replaces_scope_select
|
||||
Developer.with_scope(:find => { :select => "id, name" }) do
|
||||
developer = Developer.find(:first, :select => 'id, salary', :conditions => "name = 'David'")
|
||||
assert_equal 80000, developer.salary
|
||||
assert !developer.has_attribute?(:name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_count
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
assert_equal 1, Developer.count
|
||||
end
|
||||
|
||||
Developer.with_scope(:find => { :conditions => 'salary = 100000' }) do
|
||||
assert_equal 8, Developer.count
|
||||
assert_equal 1, Developer.count(:conditions => "name LIKE 'fixture_1%'")
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_include
|
||||
# with the include, will retrieve only developers for the given project
|
||||
scoped_developers = Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.find(:all, :conditions => 'projects.id = 2')
|
||||
end
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
end
|
||||
|
||||
def test_scoped_find_joins
|
||||
scoped_developers = Developer.with_scope(:find => { :joins => 'JOIN developers_projects ON id = developer_id' } ) do
|
||||
Developer.find(:all, :conditions => 'developers_projects.project_id = 2')
|
||||
end
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
assert_equal developers(:david).attributes, scoped_developers.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_using_new_style_joins
|
||||
scoped_developers = Developer.with_scope(:find => { :joins => :projects }) do
|
||||
Developer.find(:all, :conditions => 'projects.id = 2')
|
||||
end
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
assert_equal developers(:david).attributes, scoped_developers.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_old_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id ' }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_new_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => :comments, :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_new_and_old_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_string_array_style_and_string_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => ["INNER JOIN posts ON posts.author_id = authors.id"]}) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => 'INNER JOIN comments ON posts.id = comments.post_id', :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_string_array_style_and_hash_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => :posts}) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => ['INNER JOIN comments ON posts.id = comments.post_id'], :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_merges_joins_and_eliminates_duplicate_string_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON posts.author_id = authors.id'}) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => ["INNER JOIN posts ON posts.author_id = authors.id", "INNER JOIN comments ON posts.id = comments.post_id"], :conditions => 'comments.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_find_strips_spaces_from_string_joins_and_eliminates_duplicate_string_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => ' INNER JOIN posts ON posts.author_id = authors.id '}) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => ['INNER JOIN posts ON posts.author_id = authors.id'], :conditions => 'posts.id = 1')
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_scoped_count_include
|
||||
# with the include, will retrieve only developers for the given project
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_create
|
||||
new_comment = nil
|
||||
|
||||
VerySpecialComment.with_scope(:create => { :post_id => 1 }) do
|
||||
assert_equal({ :post_id => 1 }, VerySpecialComment.send(:current_scoped_methods)[:create])
|
||||
new_comment = VerySpecialComment.create :body => "Wonderful world"
|
||||
end
|
||||
|
||||
assert Post.find(1).comments.include?(new_comment)
|
||||
end
|
||||
|
||||
def test_immutable_scope
|
||||
options = { :conditions => "name = 'David'" }
|
||||
Developer.with_scope(:find => options) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
options[:conditions] = "name != 'David'"
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
|
||||
scope = { :find => { :conditions => "name = 'David'" }}
|
||||
Developer.with_scope(scope) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
scope[:find][:conditions] = "name != 'David'"
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_with_duck_typing
|
||||
scoping = Struct.new(:method_scoping).new(:find => { :conditions => ["name = ?", 'David'] })
|
||||
Developer.with_scope(scoping) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
scoped_methods = Developer.instance_eval('current_scoped_methods')
|
||||
|
||||
begin
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
raise "an exception"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
|
||||
end
|
||||
end
|
||||
|
||||
class NestedScopingTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :developers, :projects, :comments, :posts
|
||||
|
||||
def test_merge_options
|
||||
Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
|
||||
Developer.with_scope(:find => { :limit => 10 }) do
|
||||
merged_option = Developer.instance_eval('current_scoped_methods')[:find]
|
||||
assert_equal({ :conditions => 'salary = 80000', :limit => 10 }, merged_option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merge_inner_scope_has_priority
|
||||
Developer.with_scope(:find => { :limit => 5 }) do
|
||||
Developer.with_scope(:find => { :limit => 10 }) do
|
||||
merged_option = Developer.instance_eval('current_scoped_methods')[:find]
|
||||
assert_equal({ :limit => 10 }, merged_option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_replace_options
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
Developer.with_exclusive_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.instance_eval('current_scoped_methods'))
|
||||
assert_equal({:find => { :conditions => "name = 'Jamis'" }}, Developer.send(:scoped_methods)[-1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_append_conditions
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
|
||||
appended_condition = Developer.instance_eval('current_scoped_methods')[:find][:conditions]
|
||||
assert_equal("(name = 'David') AND (salary = 80000)", appended_condition)
|
||||
assert_equal(1, Developer.count)
|
||||
end
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
|
||||
assert_equal(0, Developer.count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merge_and_append_options
|
||||
Developer.with_scope(:find => { :conditions => 'salary = 80000', :limit => 10 }) do
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
merged_option = Developer.instance_eval('current_scoped_methods')[:find]
|
||||
assert_equal({ :conditions => "(salary = 80000) AND (name = 'David')", :limit => 10 }, merged_option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
assert_nothing_raised { Developer.find(1) }
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
assert_equal('Jamis', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_include
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.with_scope(:find => { :conditions => "projects.id = 2" }) do
|
||||
assert_nothing_raised { Developer.find(1) }
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_merged_include
|
||||
# :include's remain unique and don't "double up" when merging
|
||||
Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
# the nested scope doesn't remove the first :include
|
||||
Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => [] }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
# mixing array and symbol include's will merge correctly
|
||||
Developer.with_scope(:find => { :include => [:projects], :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_replace_include
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.with_exclusive_scope(:find => { :include => [] }) do
|
||||
assert_equal 0, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_three_level_nested_exclusive_scoped_find
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
assert_equal('Jamis', Developer.find(:first).name)
|
||||
|
||||
Developer.with_exclusive_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
|
||||
Developer.with_exclusive_scope(:find => { :conditions => "name = 'Maiha'" }) do
|
||||
assert_equal(nil, Developer.find(:first))
|
||||
end
|
||||
|
||||
# ensure that scoping is restored
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
|
||||
# ensure that scoping is restored
|
||||
assert_equal('Jamis', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
def test_merged_scoped_find
|
||||
poor_jamis = developers(:poor_jamis)
|
||||
Developer.with_scope(:find => { :conditions => "salary < 100000" }) do
|
||||
Developer.with_scope(:find => { :offset => 1, :order => 'id asc' }) do
|
||||
assert_sql /ORDER BY id asc / do
|
||||
assert_equal(poor_jamis, Developer.find(:first, :order => 'id asc'))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merged_scoped_find_sanitizes_conditions
|
||||
Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
|
||||
Developer.with_scope(:find => { :conditions => ['salary = ?', 9000] }) do
|
||||
assert_raise(ActiveRecord::RecordNotFound) { developers(:poor_jamis) }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_combines_and_sanitizes_conditions
|
||||
Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
|
||||
Developer.with_exclusive_scope(:find => { :conditions => ['salary = ?', 9000] }) do
|
||||
assert_equal developers(:poor_jamis), Developer.find(:first)
|
||||
assert_equal developers(:poor_jamis), Developer.find(:first, :conditions => ['name = ?', 'Jamis'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merged_scoped_find_combines_and_sanitizes_conditions
|
||||
Developer.with_scope(:find => { :conditions => ["name = ?", 'David'] }) do
|
||||
Developer.with_scope(:find => { :conditions => ['salary > ?', 9000] }) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_create
|
||||
comment = nil
|
||||
Comment.with_scope(:create => { :post_id => 1}) do
|
||||
Comment.with_scope(:create => { :post_id => 2}) do
|
||||
assert_equal({ :post_id => 2 }, Comment.send(:current_scoped_methods)[:create])
|
||||
comment = Comment.create :body => "Hey guys, nested scopes are broken. Please fix!"
|
||||
end
|
||||
end
|
||||
assert_equal 2, comment.post_id
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope_for_create
|
||||
comment = nil
|
||||
Comment.with_scope(:create => { :body => "Hey guys, nested scopes are broken. Please fix!" }) do
|
||||
Comment.with_exclusive_scope(:create => { :post_id => 1 }) do
|
||||
assert_equal({ :post_id => 1 }, Comment.send(:current_scoped_methods)[:create])
|
||||
comment = Comment.create :body => "Hey guys"
|
||||
end
|
||||
end
|
||||
assert_equal 1, comment.post_id
|
||||
assert_equal 'Hey guys', comment.body
|
||||
end
|
||||
|
||||
def test_merged_scoped_find_on_blank_conditions
|
||||
[nil, " ", [], {}].each do |blank|
|
||||
Developer.with_scope(:find => {:conditions => blank}) do
|
||||
Developer.with_scope(:find => {:conditions => blank}) do
|
||||
assert_nothing_raised { Developer.find(:first) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_merged_scoped_find_on_blank_bind_conditions
|
||||
[ [""], ["",{}] ].each do |blank|
|
||||
Developer.with_scope(:find => {:conditions => blank}) do
|
||||
Developer.with_scope(:find => {:conditions => blank}) do
|
||||
assert_nothing_raised { Developer.find(:first) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_immutable_nested_scope
|
||||
options1 = { :conditions => "name = 'Jamis'" }
|
||||
options2 = { :conditions => "name = 'David'" }
|
||||
Developer.with_scope(:find => options1) do
|
||||
Developer.with_exclusive_scope(:find => options2) do
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
options1[:conditions] = options2[:conditions] = nil
|
||||
assert_equal %w(David), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_immutable_merged_scope
|
||||
options1 = { :conditions => "name = 'Jamis'" }
|
||||
options2 = { :conditions => "salary > 10000" }
|
||||
Developer.with_scope(:find => options1) do
|
||||
Developer.with_scope(:find => options2) do
|
||||
assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
|
||||
options1[:conditions] = options2[:conditions] = nil
|
||||
assert_equal %w(Jamis), Developer.find(:all).map { |d| d.name }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_ensure_that_method_scoping_is_correctly_restored
|
||||
Developer.with_scope(:find => { :conditions => "name = 'David'" }) do
|
||||
scoped_methods = Developer.instance_eval('current_scoped_methods')
|
||||
begin
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Maiha'" }) do
|
||||
raise "an exception"
|
||||
end
|
||||
rescue
|
||||
end
|
||||
assert_equal scoped_methods, Developer.instance_eval('current_scoped_methods')
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_merges_old_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => 'INNER JOIN posts ON authors.id = posts.author_id' }) do
|
||||
Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
|
||||
end
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_merges_new_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
|
||||
Author.with_scope(:find => { :joins => :comments }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :conditions => 'comments.id = 1')
|
||||
end
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_merges_new_and_old_style_joins
|
||||
scoped_authors = Author.with_scope(:find => { :joins => :posts }) do
|
||||
Author.with_scope(:find => { :joins => 'INNER JOIN comments ON posts.id = comments.post_id' }) do
|
||||
Author.find(:all, :select => 'DISTINCT authors.*', :joins => '', :conditions => 'comments.id = 1')
|
||||
end
|
||||
end
|
||||
assert scoped_authors.include?(authors(:david))
|
||||
assert !scoped_authors.include?(authors(:mary))
|
||||
assert_equal 1, scoped_authors.size
|
||||
assert_equal authors(:david).attributes, scoped_authors.first.attributes
|
||||
end
|
||||
end
|
||||
|
||||
class HasManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a comment...', Comment.what_are_you
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_scoped
|
||||
assert_equal 4, Comment.search_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.search_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Comment.find_all_by_type('Comment').size
|
||||
assert_equal 2, @welcome.comments.find_all_by_type('Comment').size
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
Comment.with_scope(:find => { :conditions => '1=1' }) do
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class HasAndBelongsToManyScopingTest< ActiveRecord::TestCase
|
||||
fixtures :posts, :categories, :categories_posts
|
||||
|
||||
def setup
|
||||
@welcome = Post.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a category...', Category.what_are_you
|
||||
assert_equal 'a category...', @welcome.categories.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Category.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 0, @welcome.categories.find_all_by_type('SpecialCategory').size
|
||||
assert_equal 2, @welcome.categories.find_all_by_type('Category').size
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
Category.with_scope(:find => { :conditions => '1=1' }) do
|
||||
assert_equal 'a comment...', @welcome.comments.what_are_you
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultScopingTest < ActiveRecord::TestCase
|
||||
fixtures :developers
|
||||
|
||||
def test_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_string
|
||||
assert_equal Developer.find_all_by_name('David').map(&:id).sort, DeveloperCalledDavid.all.map(&:id).sort
|
||||
assert_equal nil, DeveloperCalledDavid.create!.name
|
||||
end
|
||||
|
||||
def test_default_scope_with_conditions_hash
|
||||
assert_equal Developer.find_all_by_name('Jamis').map(&:id).sort, DeveloperCalledJamis.all.map(&:id).sort
|
||||
assert_equal 'Jamis', DeveloperCalledJamis.create!.name
|
||||
end
|
||||
|
||||
def test_default_scoping_with_threads
|
||||
scope = [{ :create => {}, :find => { :order => 'salary DESC' } }]
|
||||
|
||||
2.times do
|
||||
Thread.new { assert_equal scope, DeveloperOrderedBySalary.send(:scoped_methods) }.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_default_scoping_with_inheritance
|
||||
scope = [{ :create => {}, :find => { :order => 'salary DESC' } }]
|
||||
|
||||
# Inherit a class having a default scope and define a new default scope
|
||||
klass = Class.new(DeveloperOrderedBySalary)
|
||||
klass.send :default_scope, {}
|
||||
|
||||
# Scopes added on children should append to parent scope
|
||||
expected_klass_scope = [{ :create => {}, :find => { :order => 'salary DESC' }}, { :create => {}, :find => {} }]
|
||||
assert_equal expected_klass_scope, klass.send(:scoped_methods)
|
||||
|
||||
# Parent should still have the original scope
|
||||
assert_equal scope, DeveloperOrderedBySalary.send(:scoped_methods)
|
||||
end
|
||||
|
||||
def test_method_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.all_ordered_by_name.collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_scope
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.with_scope(:find => { :order => 'name DESC'}) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_named_scope_overwrites_default
|
||||
expected = Developer.find(:all, :order => 'name DESC').collect { |dev| dev.name }
|
||||
received = DeveloperOrderedBySalary.by_name.find(:all).collect { |dev| dev.name }
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_nested_exclusive_scope
|
||||
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.with_exclusive_scope(:find => { :limit => 100 }) do
|
||||
DeveloperOrderedBySalary.find(:all).collect { |dev| dev.salary }
|
||||
end
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_overwriting_default_scope
|
||||
expected = Developer.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
received = DeveloperOrderedBySalary.find(:all, :order => 'salary').collect { |dev| dev.salary }
|
||||
assert_equal expected, received
|
||||
end
|
||||
end
|
||||
|
||||
=begin
|
||||
# We disabled the scoping for has_one and belongs_to as we can't think of a proper use case
|
||||
|
||||
class BelongsToScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@greetings = Comment.find(1)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_method
|
||||
assert_equal 'a post...', Post.what_are_you
|
||||
assert_equal 'a post...', @greetings.post.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 4, Post.find_all_by_type('Post').size
|
||||
assert_equal 1, @greetings.post.find_all_by_type('Post').size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
class HasOneScopingTest< ActiveRecord::TestCase
|
||||
fixtures :comments, :posts
|
||||
|
||||
def setup
|
||||
@sti_comments = Post.find(4)
|
||||
end
|
||||
|
||||
def test_forwarding_of_static_methods
|
||||
assert_equal 'a comment...', Comment.what_are_you
|
||||
assert_equal 'a very special comment...', @sti_comments.very_special_comment.what_are_you
|
||||
end
|
||||
|
||||
def test_forwarding_to_dynamic_finders
|
||||
assert_equal 1, Comment.find_all_by_type('VerySpecialComment').size
|
||||
assert_equal 1, @sti_comments.very_special_comment.find_all_by_type('VerySpecialComment').size
|
||||
assert_equal 0, @sti_comments.very_special_comment.find_all_by_type('Comment').size
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
=end
|
||||
1642
vendor/rails/activerecord/test/cases/migration_test.rb
vendored
1642
vendor/rails/activerecord/test/cases/migration_test.rb
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,124 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/course'
|
||||
|
||||
class FirebirdMigrationTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
# using Course connection for tests -- need a db that doesn't already have a BOOLEAN domain
|
||||
@connection = Course.connection
|
||||
@fireruby_connection = @connection.instance_variable_get(:@connection)
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.drop_table :foo rescue nil
|
||||
@connection.execute("DROP DOMAIN D_BOOLEAN") rescue nil
|
||||
end
|
||||
|
||||
def test_create_table_with_custom_sequence_name
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :sequence => 'foo_custom_seq') do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert sequence_exists?('foo_custom_seq')
|
||||
|
||||
assert_nothing_raised { @connection.drop_table(:foo, :sequence => 'foo_custom_seq') }
|
||||
assert !sequence_exists?('foo_custom_seq')
|
||||
ensure
|
||||
FireRuby::Generator.new('foo_custom_seq', @fireruby_connection).drop rescue nil
|
||||
end
|
||||
|
||||
def test_create_table_without_sequence
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :sequence => false) do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert_nothing_raised { @connection.drop_table :foo }
|
||||
|
||||
assert_nothing_raised do
|
||||
@connection.create_table(:foo, :id => false) do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
end
|
||||
assert !sequence_exists?('foo_seq')
|
||||
assert_nothing_raised { @connection.drop_table :foo }
|
||||
end
|
||||
|
||||
def test_create_table_with_boolean_column
|
||||
assert !boolean_domain_exists?
|
||||
assert_nothing_raised do
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :bar, :string
|
||||
f.column :baz, :boolean
|
||||
end
|
||||
end
|
||||
assert boolean_domain_exists?
|
||||
end
|
||||
|
||||
def test_add_boolean_column
|
||||
assert !boolean_domain_exists?
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :bar, :string
|
||||
end
|
||||
|
||||
assert_nothing_raised { @connection.add_column :foo, :baz, :boolean }
|
||||
assert boolean_domain_exists?
|
||||
assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "baz" }.type
|
||||
end
|
||||
|
||||
def test_change_column_to_boolean
|
||||
assert !boolean_domain_exists?
|
||||
# Manually create table with a SMALLINT column, which can be changed to a BOOLEAN
|
||||
@connection.execute "CREATE TABLE foo (bar SMALLINT)"
|
||||
assert_equal :integer, @connection.columns(:foo).find { |c| c.name == "bar" }.type
|
||||
|
||||
assert_nothing_raised { @connection.change_column :foo, :bar, :boolean }
|
||||
assert boolean_domain_exists?
|
||||
assert_equal :boolean, @connection.columns(:foo).find { |c| c.name == "bar" }.type
|
||||
end
|
||||
|
||||
def test_rename_table_with_data_and_index
|
||||
@connection.create_table :foo do |f|
|
||||
f.column :baz, :string, :limit => 50
|
||||
end
|
||||
100.times { |i| @connection.execute "INSERT INTO foo VALUES (GEN_ID(foo_seq, 1), 'record #{i+1}')" }
|
||||
@connection.add_index :foo, :baz
|
||||
|
||||
assert_nothing_raised { @connection.rename_table :foo, :bar }
|
||||
assert !@connection.tables.include?("foo")
|
||||
assert @connection.tables.include?("bar")
|
||||
assert_equal "index_bar_on_baz", @connection.indexes("bar").first.name
|
||||
assert_equal 100, FireRuby::Generator.new("bar_seq", @fireruby_connection).last
|
||||
assert_equal 100, @connection.select_one("SELECT COUNT(*) FROM bar")["count"]
|
||||
ensure
|
||||
@connection.drop_table :bar rescue nil
|
||||
end
|
||||
|
||||
def test_renaming_table_with_fk_constraint_raises_error
|
||||
@connection.create_table :parent do |p|
|
||||
p.column :name, :string
|
||||
end
|
||||
@connection.create_table :child do |c|
|
||||
c.column :parent_id, :integer
|
||||
end
|
||||
@connection.execute "ALTER TABLE child ADD CONSTRAINT fk_child_parent FOREIGN KEY(parent_id) REFERENCES parent(id)"
|
||||
assert_raise(ActiveRecord::ActiveRecordError) { @connection.rename_table :child, :descendant }
|
||||
ensure
|
||||
@connection.drop_table :child rescue nil
|
||||
@connection.drop_table :descendant rescue nil
|
||||
@connection.drop_table :parent rescue nil
|
||||
end
|
||||
|
||||
private
|
||||
def boolean_domain_exists?
|
||||
!@connection.select_one("SELECT 1 FROM rdb$fields WHERE rdb$field_name = 'D_BOOLEAN'").nil?
|
||||
end
|
||||
|
||||
def sequence_exists?(sequence_name)
|
||||
FireRuby::Generator.exists?(sequence_name, @fireruby_connection)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class Mixin < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Let us control what Time.now returns for the TouchTest suite
|
||||
class Time
|
||||
@@forced_now_time = nil
|
||||
cattr_accessor :forced_now_time
|
||||
|
||||
class << self
|
||||
def now_with_forcing
|
||||
if @@forced_now_time
|
||||
@@forced_now_time
|
||||
else
|
||||
now_without_forcing
|
||||
end
|
||||
end
|
||||
alias_method_chain :now, :forcing
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
class TouchTest < ActiveRecord::TestCase
|
||||
fixtures :mixins
|
||||
|
||||
def setup
|
||||
Time.forced_now_time = Time.now
|
||||
end
|
||||
|
||||
def teardown
|
||||
Time.forced_now_time = nil
|
||||
end
|
||||
|
||||
def test_time_mocking
|
||||
five_minutes_ago = 5.minutes.ago
|
||||
Time.forced_now_time = five_minutes_ago
|
||||
assert_equal five_minutes_ago, Time.now
|
||||
|
||||
Time.forced_now_time = nil
|
||||
assert_not_equal five_minutes_ago, Time.now
|
||||
end
|
||||
|
||||
def test_update
|
||||
stamped = Mixin.new
|
||||
|
||||
assert_nil stamped.updated_at
|
||||
assert_nil stamped.created_at
|
||||
stamped.save
|
||||
assert_equal Time.now, stamped.updated_at
|
||||
assert_equal Time.now, stamped.created_at
|
||||
end
|
||||
|
||||
def test_create
|
||||
obj = Mixin.create
|
||||
assert_equal Time.now, obj.updated_at
|
||||
assert_equal Time.now, obj.created_at
|
||||
end
|
||||
|
||||
def test_many_updates
|
||||
stamped = Mixin.new
|
||||
|
||||
assert_nil stamped.updated_at
|
||||
assert_nil stamped.created_at
|
||||
stamped.save
|
||||
assert_equal Time.now, stamped.created_at
|
||||
assert_equal Time.now, stamped.updated_at
|
||||
|
||||
old_updated_at = stamped.updated_at
|
||||
|
||||
Time.forced_now_time = 5.minutes.from_now
|
||||
stamped.lft_will_change!
|
||||
stamped.save
|
||||
|
||||
assert_equal Time.now, stamped.updated_at
|
||||
assert_equal old_updated_at, stamped.created_at
|
||||
end
|
||||
|
||||
def test_create_turned_off
|
||||
Mixin.record_timestamps = false
|
||||
|
||||
mixin = Mixin.new
|
||||
|
||||
assert_nil mixin.updated_at
|
||||
mixin.save
|
||||
assert_nil mixin.updated_at
|
||||
|
||||
# Make sure Mixin.record_timestamps gets reset, even if this test fails,
|
||||
# so that other tests do not fail because Mixin.record_timestamps == false
|
||||
rescue Exception => e
|
||||
raise e
|
||||
ensure
|
||||
Mixin.record_timestamps = true
|
||||
end
|
||||
|
||||
end
|
||||
109
vendor/rails/activerecord/test/cases/modules_test.rb
vendored
109
vendor/rails/activerecord/test/cases/modules_test.rb
vendored
|
|
@ -1,109 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/company_in_module'
|
||||
|
||||
class ModulesTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :projects, :developers
|
||||
|
||||
def setup
|
||||
# need to make sure Object::Firm and Object::Client are not defined,
|
||||
# so that constantize will not be able to cheat when having to load namespaced classes
|
||||
@undefined_consts = {}
|
||||
|
||||
[:Firm, :Client].each do |const|
|
||||
@undefined_consts.merge! const => Object.send(:remove_const, const) if Object.const_defined?(const)
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
# reinstate the constants that we undefined in the setup
|
||||
@undefined_consts.each do |constant, value|
|
||||
Object.send :const_set, constant, value unless value.nil?
|
||||
end
|
||||
end
|
||||
|
||||
def test_module_spanning_associations
|
||||
firm = MyApplication::Business::Firm.find(:first)
|
||||
assert !firm.clients.empty?, "Firm should have clients"
|
||||
assert_nil firm.class.table_name.match('::'), "Firm shouldn't have the module appear in its table name"
|
||||
end
|
||||
|
||||
def test_module_spanning_has_and_belongs_to_many_associations
|
||||
project = MyApplication::Business::Project.find(:first)
|
||||
project.developers << MyApplication::Business::Developer.create("name" => "John")
|
||||
assert "John", project.developers.last.name
|
||||
end
|
||||
|
||||
def test_associations_spanning_cross_modules
|
||||
account = MyApplication::Billing::Account.find(:first, :order => 'id')
|
||||
assert_kind_of MyApplication::Business::Firm, account.firm
|
||||
assert_kind_of MyApplication::Billing::Firm, account.qualified_billing_firm
|
||||
assert_kind_of MyApplication::Billing::Firm, account.unqualified_billing_firm
|
||||
assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_qualified_billing_firm
|
||||
assert_kind_of MyApplication::Billing::Nested::Firm, account.nested_unqualified_billing_firm
|
||||
end
|
||||
|
||||
def test_find_account_and_include_company
|
||||
account = MyApplication::Billing::Account.find(1, :include => :firm)
|
||||
assert_kind_of MyApplication::Business::Firm, account.instance_variable_get('@firm')
|
||||
assert_kind_of MyApplication::Business::Firm, account.firm
|
||||
end
|
||||
|
||||
def test_table_name
|
||||
assert_equal 'accounts', MyApplication::Billing::Account.table_name, 'table_name for ActiveRecord model in module'
|
||||
assert_equal 'companies', MyApplication::Business::Client.table_name, 'table_name for ActiveRecord model subclass'
|
||||
assert_equal 'company_contacts', MyApplication::Business::Client::Contact.table_name, 'table_name for ActiveRecord model enclosed by another ActiveRecord model'
|
||||
end
|
||||
|
||||
def test_assign_ids
|
||||
firm = MyApplication::Business::Firm.first
|
||||
|
||||
assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do
|
||||
firm.client_ids = [MyApplication::Business::Client.first.id]
|
||||
end
|
||||
end
|
||||
|
||||
# need to add an eager loading condition to force the eager loading model into
|
||||
# the old join model, to test that. See http://dev.rubyonrails.org/ticket/9640
|
||||
def test_eager_loading_in_modules
|
||||
clients = []
|
||||
|
||||
assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do
|
||||
clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL')
|
||||
clients << MyApplication::Business::Client.find(3, :include => {:firm => :account})
|
||||
end
|
||||
|
||||
clients.each do |client|
|
||||
assert_no_queries do
|
||||
assert_not_nil(client.firm.account)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_module_table_name_prefix
|
||||
assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
|
||||
assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
|
||||
assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
|
||||
end
|
||||
|
||||
def test_module_table_name_prefix_with_global_prefix
|
||||
classes = [ MyApplication::Business::Company,
|
||||
MyApplication::Business::Firm,
|
||||
MyApplication::Business::Client,
|
||||
MyApplication::Business::Client::Contact,
|
||||
MyApplication::Business::Developer,
|
||||
MyApplication::Business::Project,
|
||||
MyApplication::Business::Prefixed::Company,
|
||||
MyApplication::Business::Prefixed::Nested::Company,
|
||||
MyApplication::Billing::Account ]
|
||||
|
||||
ActiveRecord::Base.table_name_prefix = 'global_'
|
||||
classes.each(&:reset_table_name)
|
||||
assert_equal 'global_companies', MyApplication::Business::Company.table_name, 'inferred table_name for ActiveRecord model in module without table_name_prefix'
|
||||
assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Company.table_name, 'inferred table_name for ActiveRecord model in module with table_name_prefix'
|
||||
assert_equal 'prefixed_companies', MyApplication::Business::Prefixed::Nested::Company.table_name, 'table_name for ActiveRecord model in nested module with a parent table_name_prefix'
|
||||
assert_equal 'companies', MyApplication::Business::Prefixed::Firm.table_name, 'explicit table_name for ActiveRecord model in module with table_name_prefix should not be prefixed'
|
||||
ensure
|
||||
ActiveRecord::Base.table_name_prefix = ''
|
||||
classes.each(&:reset_table_name)
|
||||
end
|
||||
end
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/entrant'
|
||||
|
||||
# So we can test whether Course.connection survives a reload.
|
||||
require_dependency 'models/course'
|
||||
|
||||
class MultipleDbTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@courses = create_fixtures("courses") { Course.retrieve_connection }
|
||||
@entrants = create_fixtures("entrants")
|
||||
end
|
||||
|
||||
def test_connected
|
||||
assert_not_nil Entrant.connection
|
||||
assert_not_nil Course.connection
|
||||
end
|
||||
|
||||
def test_proper_connection
|
||||
assert_not_equal(Entrant.connection, Course.connection)
|
||||
assert_equal(Entrant.connection, Entrant.retrieve_connection)
|
||||
assert_equal(Course.connection, Course.retrieve_connection)
|
||||
assert_equal(ActiveRecord::Base.connection, Entrant.connection)
|
||||
end
|
||||
|
||||
def test_find
|
||||
c1 = Course.find(1)
|
||||
assert_equal "Ruby Development", c1.name
|
||||
c2 = Course.find(2)
|
||||
assert_equal "Java Development", c2.name
|
||||
e1 = Entrant.find(1)
|
||||
assert_equal "Ruby Developer", e1.name
|
||||
e2 = Entrant.find(2)
|
||||
assert_equal "Ruby Guru", e2.name
|
||||
e3 = Entrant.find(3)
|
||||
assert_equal "Java Lover", e3.name
|
||||
end
|
||||
|
||||
def test_associations
|
||||
c1 = Course.find(1)
|
||||
assert_equal 2, c1.entrants.count
|
||||
e1 = Entrant.find(1)
|
||||
assert_equal e1.course.id, c1.id
|
||||
c2 = Course.find(2)
|
||||
assert_equal 1, c2.entrants.count
|
||||
e3 = Entrant.find(3)
|
||||
assert_equal e3.course.id, c2.id
|
||||
end
|
||||
|
||||
def test_course_connection_should_survive_dependency_reload
|
||||
assert Course.connection
|
||||
|
||||
ActiveSupport::Dependencies.clear
|
||||
Object.send(:remove_const, :Course)
|
||||
require_dependency 'models/course'
|
||||
|
||||
assert Course.connection
|
||||
end
|
||||
|
||||
def test_transactions_across_databases
|
||||
c1 = Course.find(1)
|
||||
e1 = Entrant.find(1)
|
||||
|
||||
begin
|
||||
Course.transaction do
|
||||
Entrant.transaction do
|
||||
c1.name = "Typo"
|
||||
e1.name = "Typo"
|
||||
c1.save
|
||||
e1.save
|
||||
raise "No I messed up."
|
||||
end
|
||||
end
|
||||
rescue
|
||||
# Yup caught it
|
||||
end
|
||||
|
||||
assert_equal "Typo", c1.name
|
||||
assert_equal "Typo", e1.name
|
||||
|
||||
assert_equal "Ruby Development", Course.find(1).name
|
||||
assert_equal "Ruby Developer", Entrant.find(1).name
|
||||
end
|
||||
end
|
||||
|
|
@ -1,366 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/topic'
|
||||
require 'models/comment'
|
||||
require 'models/reply'
|
||||
require 'models/author'
|
||||
require 'models/developer'
|
||||
|
||||
class NamedScopeTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :authors, :topics, :comments, :author_addresses
|
||||
|
||||
def test_named_scope_with_STI
|
||||
assert_equal 5,Post.with_type_self.count
|
||||
assert_equal 1,SpecialPost.with_type_self.count
|
||||
end
|
||||
|
||||
def test_implements_enumerable
|
||||
assert !Topic.find(:all).empty?
|
||||
|
||||
assert_equal Topic.find(:all), Topic.base
|
||||
assert_equal Topic.find(:all), Topic.base.to_a
|
||||
assert_equal Topic.find(:first), Topic.base.first
|
||||
assert_equal Topic.find(:all), Topic.base.map { |i| i }
|
||||
end
|
||||
|
||||
def test_found_items_are_cached
|
||||
Topic.columns
|
||||
all_posts = Topic.base
|
||||
|
||||
assert_queries(1) do
|
||||
all_posts.collect
|
||||
all_posts.collect
|
||||
end
|
||||
end
|
||||
|
||||
def test_reload_expires_cache_of_found_items
|
||||
all_posts = Topic.base
|
||||
all_posts.inspect
|
||||
|
||||
new_post = Topic.create!
|
||||
assert !all_posts.include?(new_post)
|
||||
assert all_posts.reload.include?(new_post)
|
||||
end
|
||||
|
||||
def test_delegates_finds_and_calculations_to_the_base_class
|
||||
assert !Topic.find(:all).empty?
|
||||
|
||||
assert_equal Topic.find(:all), Topic.base.find(:all)
|
||||
assert_equal Topic.find(:first), Topic.base.find(:first)
|
||||
assert_equal Topic.count, Topic.base.count
|
||||
assert_equal Topic.average(:replies_count), Topic.base.average(:replies_count)
|
||||
end
|
||||
|
||||
def test_scope_should_respond_to_own_methods_and_methods_of_the_proxy
|
||||
assert Topic.approved.respond_to?(:proxy_found)
|
||||
assert Topic.approved.respond_to?(:count)
|
||||
assert Topic.approved.respond_to?(:length)
|
||||
end
|
||||
|
||||
def test_respond_to_respects_include_private_parameter
|
||||
assert !Topic.approved.respond_to?(:load_found)
|
||||
assert Topic.approved.respond_to?(:load_found, true)
|
||||
end
|
||||
|
||||
def test_subclasses_inherit_scopes
|
||||
assert Topic.scopes.include?(:base)
|
||||
|
||||
assert Reply.scopes.include?(:base)
|
||||
assert_equal Reply.find(:all), Reply.base
|
||||
end
|
||||
|
||||
def test_scopes_with_options_limit_finds_to_those_matching_the_criteria_specified
|
||||
assert !Topic.find(:all, :conditions => {:approved => true}).empty?
|
||||
|
||||
assert_equal Topic.find(:all, :conditions => {:approved => true}), Topic.approved
|
||||
assert_equal Topic.count(:conditions => {:approved => true}), Topic.approved.count
|
||||
end
|
||||
|
||||
def test_scopes_with_string_name_can_be_composed
|
||||
# NOTE that scopes defined with a string as a name worked on their own
|
||||
# but when called on another scope the other scope was completely replaced
|
||||
assert_equal Topic.replied.approved, Topic.replied.approved_as_string
|
||||
end
|
||||
|
||||
def test_scopes_can_be_specified_with_deep_hash_conditions
|
||||
assert_equal Topic.replied.approved, Topic.replied.approved_as_hash_condition
|
||||
end
|
||||
|
||||
def test_scopes_are_composable
|
||||
assert_equal (approved = Topic.find(:all, :conditions => {:approved => true})), Topic.approved
|
||||
assert_equal (replied = Topic.find(:all, :conditions => 'replies_count > 0')), Topic.replied
|
||||
assert !(approved == replied)
|
||||
assert !(approved & replied).empty?
|
||||
|
||||
assert_equal approved & replied, Topic.approved.replied
|
||||
end
|
||||
|
||||
def test_procedural_scopes
|
||||
topics_written_before_the_third = Topic.find(:all, :conditions => ['written_on < ?', topics(:third).written_on])
|
||||
topics_written_before_the_second = Topic.find(:all, :conditions => ['written_on < ?', topics(:second).written_on])
|
||||
assert_not_equal topics_written_before_the_second, topics_written_before_the_third
|
||||
|
||||
assert_equal topics_written_before_the_third, Topic.written_before(topics(:third).written_on)
|
||||
assert_equal topics_written_before_the_second, Topic.written_before(topics(:second).written_on)
|
||||
end
|
||||
|
||||
def test_procedural_scopes_returning_nil
|
||||
all_topics = Topic.find(:all)
|
||||
|
||||
assert_equal all_topics, Topic.written_before(nil)
|
||||
end
|
||||
|
||||
def test_scopes_with_joins
|
||||
address = author_addresses(:david_address)
|
||||
posts_with_authors_at_address = Post.find(
|
||||
:all, :joins => 'JOIN authors ON authors.id = posts.author_id',
|
||||
:conditions => [ 'authors.author_address_id = ?', address.id ]
|
||||
)
|
||||
assert_equal posts_with_authors_at_address, Post.with_authors_at_address(address)
|
||||
end
|
||||
|
||||
def test_scopes_with_joins_respects_custom_select
|
||||
address = author_addresses(:david_address)
|
||||
posts_with_authors_at_address_titles = Post.find(:all,
|
||||
:select => 'title',
|
||||
:joins => 'JOIN authors ON authors.id = posts.author_id',
|
||||
:conditions => [ 'authors.author_address_id = ?', address.id ]
|
||||
)
|
||||
assert_equal posts_with_authors_at_address_titles, Post.with_authors_at_address(address).find(:all, :select => 'title')
|
||||
end
|
||||
|
||||
def test_extensions
|
||||
assert_equal 1, Topic.anonymous_extension.one
|
||||
assert_equal 2, Topic.named_extension.two
|
||||
end
|
||||
|
||||
def test_multiple_extensions
|
||||
assert_equal 2, Topic.multiple_extensions.extension_two
|
||||
assert_equal 1, Topic.multiple_extensions.extension_one
|
||||
end
|
||||
|
||||
def test_has_many_associations_have_access_to_named_scopes
|
||||
assert_not_equal Post.containing_the_letter_a, authors(:david).posts
|
||||
assert !Post.containing_the_letter_a.empty?
|
||||
|
||||
assert_equal authors(:david).posts & Post.containing_the_letter_a, authors(:david).posts.containing_the_letter_a
|
||||
end
|
||||
|
||||
def test_has_many_through_associations_have_access_to_named_scopes
|
||||
assert_not_equal Comment.containing_the_letter_e, authors(:david).comments
|
||||
assert !Comment.containing_the_letter_e.empty?
|
||||
|
||||
assert_equal authors(:david).comments & Comment.containing_the_letter_e, authors(:david).comments.containing_the_letter_e
|
||||
end
|
||||
|
||||
def test_named_scopes_honor_current_scopes_from_when_defined
|
||||
assert !Post.ranked_by_comments.limit(5).empty?
|
||||
assert !authors(:david).posts.ranked_by_comments.limit(5).empty?
|
||||
assert_not_equal Post.ranked_by_comments.limit(5), authors(:david).posts.ranked_by_comments.limit(5)
|
||||
assert_not_equal Post.top(5), authors(:david).posts.top(5)
|
||||
assert_equal authors(:david).posts.ranked_by_comments.limit(5), authors(:david).posts.top(5)
|
||||
assert_equal Post.ranked_by_comments.limit(5), Post.top(5)
|
||||
end
|
||||
|
||||
def test_active_records_have_scope_named__all__
|
||||
assert !Topic.find(:all).empty?
|
||||
|
||||
assert_equal Topic.find(:all), Topic.base
|
||||
end
|
||||
|
||||
def test_active_records_have_scope_named__scoped__
|
||||
assert !Topic.find(:all, scope = {:conditions => "content LIKE '%Have%'"}).empty?
|
||||
|
||||
assert_equal Topic.find(:all, scope), Topic.scoped(scope)
|
||||
end
|
||||
|
||||
def test_proxy_options
|
||||
expected_proxy_options = { :conditions => { :approved => true } }
|
||||
assert_equal expected_proxy_options, Topic.approved.proxy_options
|
||||
end
|
||||
|
||||
def test_first_and_last_should_support_find_options
|
||||
assert_equal Topic.base.first(:order => 'title'), Topic.base.find(:first, :order => 'title')
|
||||
assert_equal Topic.base.last(:order => 'title'), Topic.base.find(:last, :order => 'title')
|
||||
end
|
||||
|
||||
def test_first_and_last_should_allow_integers_for_limit
|
||||
assert_equal Topic.base.first(2), Topic.base.to_a.first(2)
|
||||
assert_equal Topic.base.last(2), Topic.base.to_a.last(2)
|
||||
end
|
||||
|
||||
def test_first_and_last_should_not_use_query_when_results_are_loaded
|
||||
topics = Topic.base
|
||||
topics.reload # force load
|
||||
assert_no_queries do
|
||||
topics.first
|
||||
topics.last
|
||||
end
|
||||
end
|
||||
|
||||
def test_first_and_last_find_options_should_use_query_when_results_are_loaded
|
||||
topics = Topic.base
|
||||
topics.reload # force load
|
||||
assert_queries(2) do
|
||||
topics.first(:order => 'title')
|
||||
topics.last(:order => 'title')
|
||||
end
|
||||
end
|
||||
|
||||
def test_empty_should_not_load_results
|
||||
topics = Topic.base
|
||||
assert_queries(2) do
|
||||
topics.empty? # use count query
|
||||
topics.collect # force load
|
||||
topics.empty? # use loaded (no query)
|
||||
end
|
||||
end
|
||||
|
||||
def test_any_should_not_load_results
|
||||
topics = Topic.base
|
||||
assert_queries(2) do
|
||||
topics.any? # use count query
|
||||
topics.collect # force load
|
||||
topics.any? # use loaded (no query)
|
||||
end
|
||||
end
|
||||
|
||||
def test_any_should_call_proxy_found_if_using_a_block
|
||||
topics = Topic.base
|
||||
assert_queries(1) do
|
||||
topics.expects(:empty?).never
|
||||
topics.any? { true }
|
||||
end
|
||||
end
|
||||
|
||||
def test_any_should_not_fire_query_if_named_scope_loaded
|
||||
topics = Topic.base
|
||||
topics.collect # force load
|
||||
assert_no_queries { assert topics.any? }
|
||||
end
|
||||
|
||||
def test_should_build_with_proxy_options
|
||||
topic = Topic.approved.build({})
|
||||
assert topic.approved
|
||||
end
|
||||
|
||||
def test_should_build_new_with_proxy_options
|
||||
topic = Topic.approved.new
|
||||
assert topic.approved
|
||||
end
|
||||
|
||||
def test_should_create_with_proxy_options
|
||||
topic = Topic.approved.create({})
|
||||
assert topic.approved
|
||||
end
|
||||
|
||||
def test_should_create_with_bang_with_proxy_options
|
||||
topic = Topic.approved.create!({})
|
||||
assert topic.approved
|
||||
end
|
||||
|
||||
def test_should_build_with_proxy_options_chained
|
||||
topic = Topic.approved.by_lifo.build({})
|
||||
assert topic.approved
|
||||
assert_equal 'lifo', topic.author_name
|
||||
end
|
||||
|
||||
def test_find_all_should_behave_like_select
|
||||
assert_equal Topic.base.select(&:approved), Topic.base.find_all(&:approved)
|
||||
end
|
||||
|
||||
def test_rand_should_select_a_random_object_from_proxy
|
||||
assert Topic.approved.sample.is_a?(Topic)
|
||||
end
|
||||
|
||||
def test_should_use_where_in_query_for_named_scope
|
||||
assert_equal Developer.find_all_by_name('Jamis').to_set, Developer.find_all_by_id(Developer.jamises).to_set
|
||||
end
|
||||
|
||||
def test_size_should_use_count_when_results_are_not_loaded
|
||||
topics = Topic.base
|
||||
assert_queries(1) do
|
||||
assert_sql(/COUNT/i) { topics.size }
|
||||
end
|
||||
end
|
||||
|
||||
def test_size_should_use_length_when_results_are_loaded
|
||||
topics = Topic.base
|
||||
topics.reload # force load
|
||||
assert_no_queries do
|
||||
topics.size # use loaded (no query)
|
||||
end
|
||||
end
|
||||
|
||||
def test_chaining_with_duplicate_joins
|
||||
join = "INNER JOIN comments ON comments.post_id = posts.id"
|
||||
post = Post.find(1)
|
||||
assert_equal post.comments.size, Post.scoped(:joins => join).scoped(:joins => join, :conditions => "posts.id = #{post.id}").size
|
||||
end
|
||||
|
||||
def test_chaining_should_use_latest_conditions_when_creating
|
||||
post = Topic.rejected.new
|
||||
assert !post.approved?
|
||||
|
||||
post = Topic.rejected.approved.new
|
||||
assert post.approved?
|
||||
|
||||
post = Topic.approved.rejected.new
|
||||
assert !post.approved?
|
||||
|
||||
post = Topic.approved.rejected.approved.new
|
||||
assert post.approved?
|
||||
end
|
||||
|
||||
def test_chaining_should_use_latest_conditions_when_searching
|
||||
# Normal hash conditions
|
||||
assert_equal Topic.all(:conditions => {:approved => true}), Topic.rejected.approved.all
|
||||
assert_equal Topic.all(:conditions => {:approved => false}), Topic.approved.rejected.all
|
||||
|
||||
# Nested hash conditions with same keys
|
||||
assert_equal [posts(:sti_comments)], Post.with_special_comments.with_very_special_comments.all
|
||||
|
||||
# Nested hash conditions with different keys
|
||||
assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).all.uniq
|
||||
end
|
||||
|
||||
def test_named_scopes_batch_finders
|
||||
assert_equal 3, Topic.approved.count
|
||||
|
||||
assert_queries(4) do
|
||||
Topic.approved.find_each(:batch_size => 1) {|t| assert t.approved? }
|
||||
end
|
||||
|
||||
assert_queries(2) do
|
||||
Topic.approved.find_in_batches(:batch_size => 2) do |group|
|
||||
group.each {|t| assert t.approved? }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_table_names_for_chaining_scopes_with_and_without_table_name_included
|
||||
assert_nothing_raised do
|
||||
Comment.for_first_post.for_first_author.all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicScopeMatchTest < ActiveRecord::TestCase
|
||||
def test_scoped_by_no_match
|
||||
assert_nil ActiveRecord::DynamicScopeMatch.match("not_scoped_at_all")
|
||||
end
|
||||
|
||||
def test_scoped_by
|
||||
match = ActiveRecord::DynamicScopeMatch.match("scoped_by_age_and_sex_and_location")
|
||||
assert_not_nil match
|
||||
assert match.scope?
|
||||
assert_equal %w(age sex location), match.attribute_names
|
||||
end
|
||||
end
|
||||
|
||||
class DynamicScopeTest < ActiveRecord::TestCase
|
||||
def test_dynamic_scope
|
||||
assert_equal Post.scoped_by_author_id(1).find(1), Post.find(1)
|
||||
assert_equal Post.scoped_by_author_id_and_title(1, "Welcome to the weblog").first, Post.find(:first, :conditions => { :author_id => 1, :title => "Welcome to the weblog"})
|
||||
end
|
||||
end
|
||||
|
|
@ -1,848 +0,0 @@
|
|||
require "cases/helper"
|
||||
require "models/pirate"
|
||||
require "models/ship"
|
||||
require "models/ship_part"
|
||||
require "models/bird"
|
||||
require "models/parrot"
|
||||
require "models/treasure"
|
||||
require "models/man"
|
||||
require "models/interest"
|
||||
require "models/owner"
|
||||
require "models/pet"
|
||||
|
||||
module AssertRaiseWithMessage
|
||||
def assert_raise_with_message(expected_exception, expected_message)
|
||||
begin
|
||||
error_raised = false
|
||||
yield
|
||||
rescue expected_exception => error
|
||||
error_raised = true
|
||||
actual_message = error.message
|
||||
end
|
||||
assert error_raised
|
||||
assert_equal expected_message, actual_message
|
||||
end
|
||||
end
|
||||
|
||||
class TestNestedAttributesInGeneral < ActiveRecord::TestCase
|
||||
include AssertRaiseWithMessage
|
||||
|
||||
def teardown
|
||||
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
end
|
||||
|
||||
def test_base_should_have_an_empty_nested_attributes_options
|
||||
assert_equal Hash.new, ActiveRecord::Base.nested_attributes_options
|
||||
end
|
||||
|
||||
def test_should_add_a_proc_to_nested_attributes_options
|
||||
assert_equal ActiveRecord::NestedAttributes::ClassMethods::REJECT_ALL_BLANK_PROC,
|
||||
Pirate.nested_attributes_options[:birds_with_reject_all_blank][:reject_if]
|
||||
|
||||
[:parrots, :birds].each do |name|
|
||||
assert_instance_of Proc, Pirate.nested_attributes_options[name][:reject_if]
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
|
||||
pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
|
||||
pirate.save!
|
||||
|
||||
assert pirate.birds_with_reject_all_blank.empty?
|
||||
end
|
||||
|
||||
def test_should_build_a_new_record_if_reject_all_blank_does_not_return_false
|
||||
pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
pirate.birds_with_reject_all_blank_attributes = [{:name => 'Tweetie', :color => ''}]
|
||||
pirate.save!
|
||||
|
||||
assert_equal 1, pirate.birds_with_reject_all_blank.count
|
||||
end
|
||||
|
||||
def test_should_raise_an_ArgumentError_for_non_existing_associations
|
||||
assert_raise_with_message ArgumentError, "No association found for name `honesty'. Has it been defined yet?" do
|
||||
Pirate.accepts_nested_attributes_for :honesty
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_disable_allow_destroy_by_default
|
||||
Pirate.accepts_nested_attributes_for :ship
|
||||
|
||||
pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
ship = pirate.create_ship(:name => 'Nights Dirty Lightning')
|
||||
|
||||
assert_no_difference('Ship.count') do
|
||||
pirate.update_attributes(:ship_attributes => { '_destroy' => true })
|
||||
end
|
||||
end
|
||||
|
||||
def test_a_model_should_respond_to_underscore_destroy_and_return_if_it_is_marked_for_destruction
|
||||
ship = Ship.create!(:name => 'Nights Dirty Lightning')
|
||||
assert !ship._destroy
|
||||
ship.mark_for_destruction
|
||||
assert ship._destroy
|
||||
end
|
||||
|
||||
def test_reject_if_method_without_arguments
|
||||
Pirate.accepts_nested_attributes_for :ship, :reject_if => :new_record?
|
||||
|
||||
pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
|
||||
pirate.ship_attributes = { :name => 'Black Pearl' }
|
||||
assert_no_difference('Ship.count') { pirate.save! }
|
||||
end
|
||||
|
||||
def test_reject_if_method_with_arguments
|
||||
Pirate.accepts_nested_attributes_for :ship, :reject_if => :reject_empty_ships_on_create
|
||||
|
||||
pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
|
||||
pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
|
||||
assert_no_difference('Ship.count') { pirate.save! }
|
||||
|
||||
# pirate.reject_empty_ships_on_create returns false for saved records
|
||||
pirate.ship_attributes = { :name => 'Red Pearl', :_reject_me_if_new => true }
|
||||
assert_difference('Ship.count') { pirate.save! }
|
||||
end
|
||||
|
||||
def test_reject_if_with_indifferent_keys
|
||||
Pirate.accepts_nested_attributes_for :ship, :reject_if => proc {|attributes| attributes[:name].blank? }
|
||||
|
||||
pirate = Pirate.new(:catchphrase => "Stop wastin' me time")
|
||||
pirate.ship_attributes = { :name => 'Hello Pearl' }
|
||||
assert_difference('Ship.count') { pirate.save! }
|
||||
end
|
||||
end
|
||||
|
||||
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
|
||||
include AssertRaiseWithMessage
|
||||
|
||||
def setup
|
||||
@pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
|
||||
end
|
||||
|
||||
def test_should_raise_argument_error_if_trying_to_build_polymorphic_belongs_to
|
||||
assert_raise_with_message ArgumentError, "Cannot build association looter. Are you trying to build a polymorphic one-to-one association?" do
|
||||
Treasure.new(:name => 'pearl', :looter_attributes => {:catchphrase => "Arrr"})
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_define_an_attribute_writer_method_for_the_association
|
||||
assert_respond_to @pirate, :ship_attributes=
|
||||
end
|
||||
|
||||
def test_should_build_a_new_record_if_there_is_no_id
|
||||
@ship.destroy
|
||||
@pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
|
||||
|
||||
assert @pirate.ship.new_record?
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
|
||||
@ship.destroy
|
||||
@pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
|
||||
|
||||
assert_nil @pirate.ship
|
||||
end
|
||||
|
||||
def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
|
||||
@ship.destroy
|
||||
@pirate.reload.ship_attributes = {}
|
||||
|
||||
assert_nil @pirate.ship
|
||||
end
|
||||
|
||||
def test_should_replace_an_existing_record_if_there_is_no_id
|
||||
@pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger' }
|
||||
|
||||
assert @pirate.ship.new_record?
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
assert_equal 'Nights Dirty Lightning', @ship.name
|
||||
end
|
||||
|
||||
def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
|
||||
@pirate.reload.ship_attributes = { :name => 'Davy Jones Gold Dagger', :_destroy => '1' }
|
||||
|
||||
assert_equal @ship, @pirate.ship
|
||||
assert_equal 'Nights Dirty Lightning', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_modify_an_existing_record_if_there_is_a_matching_id
|
||||
@pirate.reload.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
|
||||
|
||||
assert_equal @ship, @pirate.ship
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
||||
@pirate.reload.ship_attributes = { 'id' => @ship.id, 'name' => 'Davy Jones Gold Dagger' }
|
||||
|
||||
assert_equal @ship, @pirate.ship
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
|
||||
@ship.stubs(:id).returns('ABC1X')
|
||||
@pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
|
||||
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
|
||||
@pirate.ship.destroy
|
||||
[1, '1', true, 'true'].each do |truth|
|
||||
@pirate.reload.create_ship(:name => 'Mister Pablo')
|
||||
assert_difference('Ship.count', -1) do
|
||||
@pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => truth })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
|
||||
[nil, '0', 0, 'false', false].each do |not_truth|
|
||||
assert_no_difference('Ship.count') do
|
||||
@pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => not_truth })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
|
||||
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
|
||||
|
||||
assert_no_difference('Ship.count') do
|
||||
@pirate.update_attribute(:ship_attributes, { :id => @pirate.ship.id, :_destroy => '1' })
|
||||
end
|
||||
|
||||
Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
end
|
||||
|
||||
def test_should_also_work_with_a_HashWithIndifferentAccess
|
||||
@pirate.ship_attributes = HashWithIndifferentAccess.new(:id => @ship.id, :name => 'Davy Jones Gold Dagger')
|
||||
|
||||
assert !@pirate.ship.new_record?
|
||||
assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_work_with_update_attributes_as_well
|
||||
@pirate.update_attributes({ :catchphrase => 'Arr', :ship_attributes => { :id => @ship.id, :name => 'Mister Pablo' } })
|
||||
@pirate.reload
|
||||
|
||||
assert_equal 'Arr', @pirate.catchphrase
|
||||
assert_equal 'Mister Pablo', @pirate.ship.name
|
||||
end
|
||||
|
||||
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
|
||||
assert_no_difference('Ship.count') do
|
||||
@pirate.attributes = { :ship_attributes => { :id => @ship.id, :_destroy => '1' } }
|
||||
end
|
||||
assert_difference('Ship.count', -1) do
|
||||
@pirate.save
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_automatically_enable_autosave_on_the_association
|
||||
assert Pirate.reflect_on_association(:ship).options[:autosave]
|
||||
end
|
||||
|
||||
def test_should_accept_update_only_option
|
||||
@pirate.update_attribute(:update_only_ship_attributes, { :id => @pirate.ship.id, :name => 'Mayflower' })
|
||||
end
|
||||
|
||||
def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
|
||||
@ship.delete
|
||||
assert_difference('Ship.count', 1) do
|
||||
@pirate.reload.update_attribute(:update_only_ship_attributes, { :name => 'Mayflower' })
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
|
||||
@ship.delete
|
||||
@ship = @pirate.create_update_only_ship(:name => 'Nights Dirty Lightning')
|
||||
|
||||
assert_no_difference('Ship.count') do
|
||||
@pirate.update_attributes(:update_only_ship_attributes => { :name => 'Mayflower' })
|
||||
end
|
||||
assert_equal 'Mayflower', @ship.reload.name
|
||||
end
|
||||
end
|
||||
|
||||
class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
|
||||
include AssertRaiseWithMessage
|
||||
|
||||
def setup
|
||||
@ship = Ship.new(:name => 'Nights Dirty Lightning')
|
||||
@pirate = @ship.build_pirate(:catchphrase => 'Aye')
|
||||
@ship.save!
|
||||
end
|
||||
|
||||
def test_should_define_an_attribute_writer_method_for_the_association
|
||||
assert_respond_to @ship, :pirate_attributes=
|
||||
end
|
||||
|
||||
def test_should_build_a_new_record_if_there_is_no_id
|
||||
@pirate.destroy
|
||||
@ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
|
||||
|
||||
assert @ship.pirate.new_record?
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_not_build_a_new_record_if_there_is_no_id_and_destroy_is_truthy
|
||||
@pirate.destroy
|
||||
@ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
|
||||
|
||||
assert_nil @ship.pirate
|
||||
end
|
||||
|
||||
def test_should_not_build_a_new_record_if_a_reject_if_proc_returns_false
|
||||
@pirate.destroy
|
||||
@ship.reload.pirate_attributes = {}
|
||||
|
||||
assert_nil @ship.pirate
|
||||
end
|
||||
|
||||
def test_should_replace_an_existing_record_if_there_is_no_id
|
||||
@ship.reload.pirate_attributes = { :catchphrase => 'Arr' }
|
||||
|
||||
assert @ship.pirate.new_record?
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
assert_equal 'Aye', @pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_not_replace_an_existing_record_if_there_is_no_id_and_destroy_is_truthy
|
||||
@ship.reload.pirate_attributes = { :catchphrase => 'Arr', :_destroy => '1' }
|
||||
|
||||
assert_equal @pirate, @ship.pirate
|
||||
assert_equal 'Aye', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_modify_an_existing_record_if_there_is_a_matching_id
|
||||
@ship.reload.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
|
||||
|
||||
assert_equal @pirate, @ship.pirate
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_associate_with_record_if_parent_record_is_not_saved
|
||||
@ship.destroy
|
||||
@pirate = Pirate.create(:catchphrase => 'Arr')
|
||||
@ship = Ship.new(:name => 'Nights Dirty Lightning', :pirate_attributes => { :id => @pirate.id, :catchphrase => @pirate.catchphrase})
|
||||
|
||||
assert_equal @ship.name, 'Nights Dirty Lightning'
|
||||
assert_equal @pirate, @ship.pirate
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_string_keys_and_update_the_associated_model
|
||||
@ship.reload.pirate_attributes = { 'id' => @pirate.id, 'catchphrase' => 'Arr' }
|
||||
|
||||
assert_equal @pirate, @ship.pirate
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
|
||||
@pirate.stubs(:id).returns('ABC1X')
|
||||
@ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
|
||||
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
|
||||
@ship.pirate.destroy
|
||||
[1, '1', true, 'true'].each do |truth|
|
||||
@ship.reload.create_pirate(:catchphrase => 'Arr')
|
||||
assert_difference('Pirate.count', -1) do
|
||||
@ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => truth })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_an_existing_record_if_destroy_is_not_truthy
|
||||
[nil, '0', 0, 'false', false].each do |not_truth|
|
||||
assert_no_difference('Pirate.count') do
|
||||
@ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => not_truth })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false
|
||||
Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? }
|
||||
|
||||
assert_no_difference('Pirate.count') do
|
||||
@ship.update_attribute(:pirate_attributes, { :id => @ship.pirate.id, :_destroy => '1' })
|
||||
end
|
||||
|
||||
Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
end
|
||||
|
||||
def test_should_work_with_update_attributes_as_well
|
||||
@ship.update_attributes({ :name => 'Mister Pablo', :pirate_attributes => { :catchphrase => 'Arr' } })
|
||||
@ship.reload
|
||||
|
||||
assert_equal 'Mister Pablo', @ship.name
|
||||
assert_equal 'Arr', @ship.pirate.catchphrase
|
||||
end
|
||||
|
||||
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
|
||||
assert_no_difference('Pirate.count') do
|
||||
@ship.attributes = { :pirate_attributes => { :id => @ship.pirate.id, '_destroy' => true } }
|
||||
end
|
||||
assert_difference('Pirate.count', -1) { @ship.save }
|
||||
end
|
||||
|
||||
def test_should_automatically_enable_autosave_on_the_association
|
||||
assert Ship.reflect_on_association(:pirate).options[:autosave]
|
||||
end
|
||||
|
||||
def test_should_create_new_model_when_nothing_is_there_and_update_only_is_true
|
||||
@pirate.delete
|
||||
assert_difference('Pirate.count', 1) do
|
||||
@ship.reload.update_attribute(:update_only_pirate_attributes, { :catchphrase => 'Arr' })
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_update_existing_when_update_only_is_true_and_no_id_is_given
|
||||
@pirate.delete
|
||||
@pirate = @ship.create_update_only_pirate(:catchphrase => 'Aye')
|
||||
|
||||
assert_no_difference('Pirate.count') do
|
||||
@ship.update_attributes(:update_only_pirate_attributes => { :catchphrase => 'Arr' })
|
||||
end
|
||||
assert_equal 'Arr', @pirate.reload.catchphrase
|
||||
end
|
||||
end
|
||||
|
||||
module NestedAttributesOnACollectionAssociationTests
|
||||
include AssertRaiseWithMessage
|
||||
|
||||
def test_should_define_an_attribute_writer_method_for_the_association
|
||||
assert_respond_to @pirate, association_setter
|
||||
end
|
||||
|
||||
def test_should_save_only_one_association_on_create
|
||||
pirate = Pirate.create!({
|
||||
:catchphrase => 'Arr',
|
||||
association_getter => { 'foo' => { :name => 'Grace OMalley' } }
|
||||
})
|
||||
|
||||
assert_equal 1, pirate.reload.send(@association_name).count
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_string_keys_and_assign_the_attributes_to_the_associated_models
|
||||
@alternate_params[association_getter].stringify_keys!
|
||||
@pirate.update_attributes @alternate_params
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
|
||||
end
|
||||
|
||||
def test_should_assign_existing_children_if_parent_is_new
|
||||
@pirate = Pirate.new({:catchphrase => "Don' botharr talkin' like one, savvy?"}.merge(@alternate_params))
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@pirate.send(@association_name)[0].name, @pirate.send(@association_name)[1].name]
|
||||
end
|
||||
|
||||
def test_should_take_an_array_and_assign_the_attributes_to_the_associated_models
|
||||
@pirate.send(association_setter, @alternate_params[association_getter].values)
|
||||
@pirate.save
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.reload.name, @child_2.reload.name]
|
||||
end
|
||||
|
||||
def test_should_also_work_with_a_HashWithIndifferentAccess
|
||||
@pirate.send(association_setter, HashWithIndifferentAccess.new('foo' => HashWithIndifferentAccess.new(:id => @child_1.id, :name => 'Grace OMalley')))
|
||||
@pirate.save
|
||||
assert_equal 'Grace OMalley', @child_1.reload.name
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_and_assign_the_attributes_to_the_associated_models
|
||||
@pirate.attributes = @alternate_params
|
||||
assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
|
||||
assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
|
||||
end
|
||||
|
||||
def test_should_not_load_association_when_updating_existing_records
|
||||
@pirate.reload
|
||||
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
|
||||
assert ! @pirate.send(@association_name).loaded?
|
||||
|
||||
@pirate.save
|
||||
assert ! @pirate.send(@association_name).loaded?
|
||||
assert_equal 'Grace OMalley', @child_1.reload.name
|
||||
end
|
||||
|
||||
def test_should_not_overwrite_unsaved_updates_when_loading_association
|
||||
@pirate.reload
|
||||
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
|
||||
assert_equal 'Grace OMalley', @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.name
|
||||
end
|
||||
|
||||
def test_should_preserve_order_when_not_overwriting_unsaved_updates
|
||||
@pirate.reload
|
||||
@pirate.send(association_setter, [{ :id => @child_1.id, :name => 'Grace OMalley' }])
|
||||
assert_equal @child_1.id, @pirate.send(@association_name).send(:load_target).first.id
|
||||
end
|
||||
|
||||
def test_should_refresh_saved_records_when_not_overwriting_unsaved_updates
|
||||
@pirate.reload
|
||||
record = @pirate.class.reflect_on_association(@association_name).klass.new(:name => 'Grace OMalley')
|
||||
@pirate.send(@association_name) << record
|
||||
record.save!
|
||||
@pirate.send(@association_name).last.update_attributes!(:name => 'Polly')
|
||||
assert_equal 'Polly', @pirate.send(@association_name).send(:load_target).last.name
|
||||
end
|
||||
|
||||
def test_should_not_remove_scheduled_destroys_when_loading_association
|
||||
@pirate.reload
|
||||
@pirate.send(association_setter, [{ :id => @child_1.id, :_destroy => '1' }])
|
||||
assert @pirate.send(@association_name).send(:load_target).find { |r| r.id == @child_1.id }.marked_for_destruction?
|
||||
end
|
||||
|
||||
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
|
||||
@child_1.stubs(:id).returns('ABC1X')
|
||||
@child_2.stubs(:id).returns('ABC2X')
|
||||
|
||||
@pirate.attributes = {
|
||||
association_getter => [
|
||||
{ :id => @child_1.id, :name => 'Grace OMalley' },
|
||||
{ :id => @child_2.id, :name => 'Privateers Greed' }
|
||||
]
|
||||
}
|
||||
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
|
||||
end
|
||||
|
||||
def test_should_not_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
|
||||
assert_nothing_raised ActiveRecord::RecordNotFound do
|
||||
@pirate.attributes = { association_getter => [{ :id => 1234567890 }] }
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
|
||||
@pirate.send(@association_name).destroy_all
|
||||
@pirate.reload.attributes = {
|
||||
association_getter => { 'foo' => { :name => 'Grace OMalley' }, 'bar' => { :name => 'Privateers Greed' }}
|
||||
}
|
||||
|
||||
assert @pirate.send(@association_name).first.new_record?
|
||||
assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
|
||||
|
||||
assert @pirate.send(@association_name).last.new_record?
|
||||
assert_equal 'Privateers Greed', @pirate.send(@association_name).last.name
|
||||
end
|
||||
|
||||
def test_should_not_assign_destroy_key_to_a_record
|
||||
assert_nothing_raised ActiveRecord::UnknownAttributeError do
|
||||
@pirate.send(association_setter, { 'foo' => { '_destroy' => '0' }})
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_ignore_new_associated_records_with_truthy_destroy_attribute
|
||||
@pirate.send(@association_name).destroy_all
|
||||
@pirate.reload.attributes = {
|
||||
association_getter => {
|
||||
'foo' => { :name => 'Grace OMalley' },
|
||||
'bar' => { :name => 'Privateers Greed', '_destroy' => '1' }
|
||||
}
|
||||
}
|
||||
|
||||
assert_equal 1, @pirate.send(@association_name).length
|
||||
assert_equal 'Grace OMalley', @pirate.send(@association_name).first.name
|
||||
end
|
||||
|
||||
def test_should_ignore_new_associated_records_if_a_reject_if_proc_returns_false
|
||||
@alternate_params[association_getter]['baz'] = {}
|
||||
assert_no_difference("@pirate.send(@association_name).count") do
|
||||
@pirate.attributes = @alternate_params
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_sort_the_hash_by_the_keys_before_building_new_associated_models
|
||||
attributes = ActiveSupport::OrderedHash.new
|
||||
attributes['123726353'] = { :name => 'Grace OMalley' }
|
||||
attributes['2'] = { :name => 'Privateers Greed' } # 2 is lower then 123726353
|
||||
@pirate.send(association_setter, attributes)
|
||||
|
||||
assert_equal ['Posideons Killer', 'Killer bandita Dionne', 'Privateers Greed', 'Grace OMalley'].to_set, @pirate.send(@association_name).map(&:name).to_set
|
||||
end
|
||||
|
||||
def test_should_raise_an_argument_error_if_something_else_than_a_hash_is_passed
|
||||
assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, {}) }
|
||||
assert_nothing_raised(ArgumentError) { @pirate.send(association_setter, ActiveSupport::OrderedHash.new) }
|
||||
|
||||
assert_raise_with_message ArgumentError, 'Hash or Array expected, got String ("foo")' do
|
||||
@pirate.send(association_setter, "foo")
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_work_with_update_attributes_as_well
|
||||
@pirate.update_attributes(:catchphrase => 'Arr',
|
||||
association_getter => { 'foo' => { :id => @child_1.id, :name => 'Grace OMalley' }})
|
||||
|
||||
assert_equal 'Grace OMalley', @child_1.reload.name
|
||||
end
|
||||
|
||||
def test_should_update_existing_records_and_add_new_ones_that_have_no_id
|
||||
@alternate_params[association_getter]['baz'] = { :name => 'Buccaneers Servant' }
|
||||
assert_difference('@pirate.send(@association_name).count', +1) do
|
||||
@pirate.update_attributes @alternate_params
|
||||
end
|
||||
assert_equal ['Grace OMalley', 'Privateers Greed', 'Buccaneers Servant'].to_set, @pirate.reload.send(@association_name).map(&:name).to_set
|
||||
end
|
||||
|
||||
def test_should_be_possible_to_destroy_a_record
|
||||
['1', 1, 'true', true].each do |true_variable|
|
||||
record = @pirate.reload.send(@association_name).create!(:name => 'Grace OMalley')
|
||||
@pirate.send(association_setter,
|
||||
@alternate_params[association_getter].merge('baz' => { :id => record.id, '_destroy' => true_variable })
|
||||
)
|
||||
|
||||
assert_difference('@pirate.send(@association_name).count', -1) do
|
||||
@pirate.save
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_the_associated_model_with_a_non_truthy_argument
|
||||
[nil, '', '0', 0, 'false', false].each do |false_variable|
|
||||
@alternate_params[association_getter]['foo']['_destroy'] = false_variable
|
||||
assert_no_difference('@pirate.send(@association_name).count') do
|
||||
@pirate.update_attributes(@alternate_params)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_not_destroy_the_associated_model_until_the_parent_is_saved
|
||||
assert_no_difference('@pirate.send(@association_name).count') do
|
||||
@pirate.send(association_setter, @alternate_params[association_getter].merge('baz' => { :id => @child_1.id, '_destroy' => true }))
|
||||
end
|
||||
assert_difference('@pirate.send(@association_name).count', -1) { @pirate.save }
|
||||
end
|
||||
|
||||
def test_should_automatically_enable_autosave_on_the_association
|
||||
assert Pirate.reflect_on_association(@association_name).options[:autosave]
|
||||
end
|
||||
|
||||
def test_validate_presence_of_parent_works_with_inverse_of
|
||||
Man.accepts_nested_attributes_for(:interests)
|
||||
assert_equal :man, Man.reflect_on_association(:interests).options[:inverse_of]
|
||||
assert_equal :interests, Interest.reflect_on_association(:man).options[:inverse_of]
|
||||
|
||||
repair_validations(Interest) do
|
||||
Interest.validates_presence_of(:man)
|
||||
assert_difference 'Man.count' do
|
||||
assert_difference 'Interest.count', 2 do
|
||||
man = Man.create!(:name => 'John',
|
||||
:interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
|
||||
assert_equal 2, man.interests.count
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_validate_presence_of_parent_fails_without_inverse_of
|
||||
Man.accepts_nested_attributes_for(:interests)
|
||||
Man.reflect_on_association(:interests).options.delete(:inverse_of)
|
||||
Interest.reflect_on_association(:man).options.delete(:inverse_of)
|
||||
|
||||
repair_validations(Interest) do
|
||||
Interest.validates_presence_of(:man)
|
||||
assert_no_difference ['Man.count', 'Interest.count'] do
|
||||
man = Man.create(:name => 'John',
|
||||
:interests_attributes => [{:topic=>'Cars'}, {:topic=>'Sports'}])
|
||||
assert !man.errors[:'interests.man'].empty?
|
||||
end
|
||||
end
|
||||
# restore :inverse_of
|
||||
Man.reflect_on_association(:interests).options[:inverse_of] = :man
|
||||
Interest.reflect_on_association(:man).options[:inverse_of] = :interests
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def association_setter
|
||||
@association_setter ||= "#{@association_name}_attributes=".to_sym
|
||||
end
|
||||
|
||||
def association_getter
|
||||
@association_getter ||= "#{@association_name}_attributes".to_sym
|
||||
end
|
||||
end
|
||||
|
||||
class TestNestedAttributesOnAHasManyAssociation < ActiveRecord::TestCase
|
||||
def setup
|
||||
@association_type = :has_many
|
||||
@association_name = :birds
|
||||
|
||||
@pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
@pirate.birds.create!(:name => 'Posideons Killer')
|
||||
@pirate.birds.create!(:name => 'Killer bandita Dionne')
|
||||
|
||||
@child_1, @child_2 = @pirate.birds
|
||||
|
||||
@alternate_params = {
|
||||
:birds_attributes => {
|
||||
'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
|
||||
'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
include NestedAttributesOnACollectionAssociationTests
|
||||
end
|
||||
|
||||
class TestNestedAttributesOnAHasAndBelongsToManyAssociation < ActiveRecord::TestCase
|
||||
def setup
|
||||
@association_type = :has_and_belongs_to_many
|
||||
@association_name = :parrots
|
||||
|
||||
@pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
@pirate.parrots.create!(:name => 'Posideons Killer')
|
||||
@pirate.parrots.create!(:name => 'Killer bandita Dionne')
|
||||
|
||||
@child_1, @child_2 = @pirate.parrots
|
||||
|
||||
@alternate_params = {
|
||||
:parrots_attributes => {
|
||||
'foo' => { :id => @child_1.id, :name => 'Grace OMalley' },
|
||||
'bar' => { :id => @child_2.id, :name => 'Privateers Greed' }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
include NestedAttributesOnACollectionAssociationTests
|
||||
end
|
||||
|
||||
class TestNestedAttributesLimit < ActiveRecord::TestCase
|
||||
def setup
|
||||
Pirate.accepts_nested_attributes_for :parrots, :limit => 2
|
||||
|
||||
@pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
|
||||
end
|
||||
|
||||
def teardown
|
||||
Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? }
|
||||
end
|
||||
|
||||
def test_limit_with_less_records
|
||||
@pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Big Big Love' } } }
|
||||
assert_difference('Parrot.count') { @pirate.save! }
|
||||
end
|
||||
|
||||
def test_limit_with_number_exact_records
|
||||
@pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' }, 'bar' => { :name => 'Blown Away' } } }
|
||||
assert_difference('Parrot.count', 2) { @pirate.save! }
|
||||
end
|
||||
|
||||
def test_limit_with_exceeding_records
|
||||
assert_raises(ActiveRecord::NestedAttributes::TooManyRecords) do
|
||||
@pirate.attributes = { :parrots_attributes => { 'foo' => { :name => 'Lovely Day' },
|
||||
'bar' => { :name => 'Blown Away' },
|
||||
'car' => { :name => 'The Happening' }} }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TestNestedAttributesWithNonStandardPrimaryKeys < ActiveRecord::TestCase
|
||||
fixtures :owners, :pets
|
||||
|
||||
def setup
|
||||
Owner.accepts_nested_attributes_for :pets
|
||||
|
||||
@owner = owners(:ashley)
|
||||
@pet1, @pet2 = pets(:chew), pets(:mochi)
|
||||
|
||||
@params = {
|
||||
:pets_attributes => {
|
||||
'0' => { :id => @pet1.id, :name => 'Foo' },
|
||||
'1' => { :id => @pet2.id, :name => 'Bar' }
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def test_should_update_existing_records_with_non_standard_primary_key
|
||||
@owner.update_attributes(@params)
|
||||
assert_equal ['Foo', 'Bar'], @owner.pets.map(&:name)
|
||||
end
|
||||
end
|
||||
|
||||
class TestHasOneAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@pirate = Pirate.create!(:catchphrase => "My baby takes tha mornin' train!")
|
||||
@ship = @pirate.create_ship(:name => "The good ship Dollypop")
|
||||
@part = @ship.parts.create!(:name => "Mast")
|
||||
@trinket = @part.trinkets.create!(:name => "Necklace")
|
||||
end
|
||||
|
||||
test "when great-grandchild changed in memory, saving parent should save great-grandchild" do
|
||||
@trinket.name = "changed"
|
||||
@pirate.save
|
||||
assert_equal "changed", @trinket.reload.name
|
||||
end
|
||||
|
||||
test "when great-grandchild changed via attributes, saving parent should save great-grandchild" do
|
||||
@pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}}
|
||||
@pirate.save
|
||||
assert_equal "changed", @trinket.reload.name
|
||||
end
|
||||
|
||||
test "when great-grandchild marked_for_destruction via attributes, saving parent should destroy great-grandchild" do
|
||||
@pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}}
|
||||
assert_difference('@part.trinkets.count', -1) { @pirate.save }
|
||||
end
|
||||
|
||||
test "when great-grandchild added via attributes, saving parent should create great-grandchild" do
|
||||
@pirate.attributes = {:ship_attributes => {:id => @ship.id, :parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}}
|
||||
assert_difference('@part.trinkets.count', 1) { @pirate.save }
|
||||
end
|
||||
|
||||
test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
|
||||
@trinket.name = "changed"
|
||||
Ship.create!(:pirate => @pirate, :name => "The Black Rock")
|
||||
ShipPart.create!(:ship => @ship, :name => "Stern")
|
||||
assert_no_queries { @pirate.valid? }
|
||||
end
|
||||
end
|
||||
|
||||
class TestHasManyAutosaveAssoictaionWhichItselfHasAutosaveAssociations < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@ship = Ship.create!(:name => "The good ship Dollypop")
|
||||
@part = @ship.parts.create!(:name => "Mast")
|
||||
@trinket = @part.trinkets.create!(:name => "Necklace")
|
||||
end
|
||||
|
||||
test "if association is not loaded and association record is saved and then in memory record attributes should be saved" do
|
||||
@ship.parts_attributes=[{:id => @part.id,:name =>'Deck'}]
|
||||
assert_equal 1, @ship.parts.proxy_target.size
|
||||
assert_equal 'Deck', @ship.parts[0].name
|
||||
end
|
||||
|
||||
test "when grandchild changed in memory, saving parent should save grandchild" do
|
||||
@trinket.name = "changed"
|
||||
@ship.save
|
||||
assert_equal "changed", @trinket.reload.name
|
||||
end
|
||||
|
||||
test "when grandchild changed via attributes, saving parent should save grandchild" do
|
||||
@ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :name => "changed"}]}]}
|
||||
@ship.save
|
||||
assert_equal "changed", @trinket.reload.name
|
||||
end
|
||||
|
||||
test "when grandchild marked_for_destruction via attributes, saving parent should destroy grandchild" do
|
||||
@ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:id => @trinket.id, :_destroy => true}]}]}
|
||||
assert_difference('@part.trinkets.count', -1) { @ship.save }
|
||||
end
|
||||
|
||||
test "when grandchild added via attributes, saving parent should create grandchild" do
|
||||
@ship.attributes = {:parts_attributes => [{:id => @part.id, :trinkets_attributes => [{:name => "created"}]}]}
|
||||
assert_difference('@part.trinkets.count', 1) { @ship.save }
|
||||
end
|
||||
|
||||
test "when extra records exist for associations, validate (which calls nested_records_changed_for_autosave?) should not load them up" do
|
||||
@trinket.name = "changed"
|
||||
Ship.create!(:name => "The Black Rock")
|
||||
ShipPart.create!(:ship => @ship, :name => "Stern")
|
||||
assert_no_queries { @ship.valid? }
|
||||
end
|
||||
end
|
||||
119
vendor/rails/activerecord/test/cases/pk_test.rb
vendored
119
vendor/rails/activerecord/test/cases/pk_test.rb
vendored
|
|
@ -1,119 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/subscriber'
|
||||
require 'models/movie'
|
||||
require 'models/keyboard'
|
||||
require 'models/mixed_case_monkey'
|
||||
|
||||
class PrimaryKeysTest < ActiveRecord::TestCase
|
||||
fixtures :topics, :subscribers, :movies, :mixed_case_monkeys
|
||||
|
||||
def test_integer_key
|
||||
topic = Topic.find(1)
|
||||
assert_equal(topics(:first).author_name, topic.author_name)
|
||||
topic = Topic.find(2)
|
||||
assert_equal(topics(:second).author_name, topic.author_name)
|
||||
|
||||
topic = Topic.new
|
||||
topic.title = "New Topic"
|
||||
assert_equal(nil, topic.id)
|
||||
assert_nothing_raised { topic.save! }
|
||||
id = topic.id
|
||||
|
||||
topicReloaded = Topic.find(id)
|
||||
assert_equal("New Topic", topicReloaded.title)
|
||||
end
|
||||
|
||||
def test_customized_primary_key_auto_assigns_on_save
|
||||
Keyboard.delete_all
|
||||
keyboard = Keyboard.new(:name => 'HHKB')
|
||||
assert_nothing_raised { keyboard.save! }
|
||||
assert_equal keyboard.id, Keyboard.find_by_name('HHKB').id
|
||||
end
|
||||
|
||||
def test_customized_primary_key_can_be_get_before_saving
|
||||
keyboard = Keyboard.new
|
||||
assert_nil keyboard.id
|
||||
assert_nothing_raised { assert_nil keyboard.key_number }
|
||||
end
|
||||
|
||||
def test_customized_string_primary_key_settable_before_save
|
||||
subscriber = Subscriber.new
|
||||
assert_nothing_raised { subscriber.id = 'webster123' }
|
||||
assert_equal 'webster123', subscriber.id
|
||||
assert_equal 'webster123', subscriber.nick
|
||||
end
|
||||
|
||||
def test_string_key
|
||||
subscriber = Subscriber.find(subscribers(:first).nick)
|
||||
assert_equal(subscribers(:first).name, subscriber.name)
|
||||
subscriber = Subscriber.find(subscribers(:second).nick)
|
||||
assert_equal(subscribers(:second).name, subscriber.name)
|
||||
|
||||
subscriber = Subscriber.new
|
||||
subscriber.id = "jdoe"
|
||||
assert_equal("jdoe", subscriber.id)
|
||||
subscriber.name = "John Doe"
|
||||
assert_nothing_raised { subscriber.save! }
|
||||
assert_equal("jdoe", subscriber.id)
|
||||
|
||||
subscriberReloaded = Subscriber.find("jdoe")
|
||||
assert_equal("John Doe", subscriberReloaded.name)
|
||||
end
|
||||
|
||||
def test_find_with_more_than_one_string_key
|
||||
assert_equal 2, Subscriber.find(subscribers(:first).nick, subscribers(:second).nick).length
|
||||
end
|
||||
|
||||
def test_primary_key_prefix
|
||||
ActiveRecord::Base.primary_key_prefix_type = :table_name
|
||||
Topic.reset_primary_key
|
||||
assert_equal "topicid", Topic.primary_key
|
||||
|
||||
ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
|
||||
Topic.reset_primary_key
|
||||
assert_equal "topic_id", Topic.primary_key
|
||||
|
||||
ActiveRecord::Base.primary_key_prefix_type = nil
|
||||
Topic.reset_primary_key
|
||||
assert_equal "id", Topic.primary_key
|
||||
end
|
||||
|
||||
def test_delete_should_quote_pkey
|
||||
assert_nothing_raised { MixedCaseMonkey.delete(1) }
|
||||
end
|
||||
def test_update_counters_should_quote_pkey_and_quote_counter_columns
|
||||
assert_nothing_raised { MixedCaseMonkey.update_counters(1, :fleaCount => 99) }
|
||||
end
|
||||
def test_find_with_one_id_should_quote_pkey
|
||||
assert_nothing_raised { MixedCaseMonkey.find(1) }
|
||||
end
|
||||
def test_find_with_multiple_ids_should_quote_pkey
|
||||
assert_nothing_raised { MixedCaseMonkey.find([1,2]) }
|
||||
end
|
||||
def test_instance_update_should_quote_pkey
|
||||
assert_nothing_raised { MixedCaseMonkey.find(1).save }
|
||||
end
|
||||
def test_instance_destroy_should_quote_pkey
|
||||
assert_nothing_raised { MixedCaseMonkey.find(1).destroy }
|
||||
end
|
||||
|
||||
def test_supports_primary_key
|
||||
assert_nothing_raised NoMethodError do
|
||||
ActiveRecord::Base.connection.supports_primary_key?
|
||||
end
|
||||
end
|
||||
|
||||
def test_primary_key_returns_value_if_it_exists
|
||||
if ActiveRecord::Base.connection.supports_primary_key?
|
||||
assert_equal 'id', ActiveRecord::Base.connection.primary_key('developers')
|
||||
end
|
||||
end
|
||||
|
||||
def test_primary_key_returns_nil_if_it_does_not_exist
|
||||
if ActiveRecord::Base.connection.supports_primary_key?
|
||||
assert_nil ActiveRecord::Base.connection.primary_key('developers_projects')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,103 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class PooledConnectionsTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
super
|
||||
@connection = ActiveRecord::Base.remove_connection
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveRecord::Base.clear_all_connections!
|
||||
ActiveRecord::Base.establish_connection(@connection)
|
||||
super
|
||||
end
|
||||
|
||||
def checkout_connections
|
||||
ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
|
||||
@connections = []
|
||||
@timed_out = 0
|
||||
|
||||
4.times do
|
||||
Thread.new do
|
||||
begin
|
||||
@connections << ActiveRecord::Base.connection_pool.checkout
|
||||
rescue ActiveRecord::ConnectionTimeoutError
|
||||
@timed_out += 1
|
||||
end
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
# Will deadlock due to lack of Monitor timeouts in 1.9
|
||||
if RUBY_VERSION < '1.9'
|
||||
def test_pooled_connection_checkout
|
||||
checkout_connections
|
||||
assert_equal @connections.length, 2
|
||||
assert_equal @timed_out, 2
|
||||
end
|
||||
end
|
||||
|
||||
def checkout_checkin_connections(pool_size, threads)
|
||||
ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
|
||||
@connection_count = 0
|
||||
@timed_out = 0
|
||||
threads.times do
|
||||
Thread.new do
|
||||
begin
|
||||
conn = ActiveRecord::Base.connection_pool.checkout
|
||||
sleep 0.1
|
||||
ActiveRecord::Base.connection_pool.checkin conn
|
||||
@connection_count += 1
|
||||
rescue ActiveRecord::ConnectionTimeoutError
|
||||
@timed_out += 1
|
||||
end
|
||||
end.join
|
||||
end
|
||||
end
|
||||
|
||||
def test_pooled_connection_checkin_one
|
||||
checkout_checkin_connections 1, 2
|
||||
assert_equal 2, @connection_count
|
||||
assert_equal 0, @timed_out
|
||||
end
|
||||
|
||||
def test_pooled_connection_checkin_two
|
||||
checkout_checkin_connections 2, 3
|
||||
assert_equal 3, @connection_count
|
||||
assert_equal 0, @timed_out
|
||||
end
|
||||
|
||||
def test_pooled_connection_checkout_existing_first
|
||||
ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1}))
|
||||
conn_pool = ActiveRecord::Base.connection_pool
|
||||
conn = conn_pool.checkout
|
||||
conn_pool.checkin(conn)
|
||||
conn = conn_pool.checkout
|
||||
assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn
|
||||
conn_pool.checkin(conn)
|
||||
end
|
||||
|
||||
def test_not_connected_defined_connection_returns_false
|
||||
ActiveRecord::Base.establish_connection(@connection)
|
||||
assert ! ActiveRecord::Base.connected?
|
||||
end
|
||||
|
||||
def test_undefined_connection_returns_false
|
||||
old_handler = ActiveRecord::Base.connection_handler
|
||||
ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
|
||||
assert_equal false, ActiveRecord::Base.connected?
|
||||
ensure
|
||||
ActiveRecord::Base.connection_handler = old_handler
|
||||
end
|
||||
end unless %w(FrontBase).include? ActiveRecord::Base.connection.adapter_name
|
||||
|
||||
class AllowConcurrencyDeprecatedTest < ActiveRecord::TestCase
|
||||
def test_allow_concurrency_is_deprecated
|
||||
assert_deprecated('ActiveRecord::Base.allow_concurrency') do
|
||||
ActiveRecord::Base.allow_concurrency
|
||||
end
|
||||
assert_deprecated('ActiveRecord::Base.allow_concurrency=') do
|
||||
ActiveRecord::Base.allow_concurrency = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,129 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/task'
|
||||
require 'models/course'
|
||||
require 'models/category'
|
||||
require 'models/post'
|
||||
|
||||
|
||||
class QueryCacheTest < ActiveRecord::TestCase
|
||||
fixtures :tasks, :topics, :categories, :posts, :categories_posts
|
||||
|
||||
def test_find_queries
|
||||
assert_queries(2) { Task.find(1); Task.find(1) }
|
||||
end
|
||||
|
||||
def test_find_queries_with_cache
|
||||
Task.cache do
|
||||
assert_queries(1) { Task.find(1); Task.find(1) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_count_queries_with_cache
|
||||
Task.cache do
|
||||
assert_queries(1) { Task.count; Task.count }
|
||||
end
|
||||
end
|
||||
|
||||
def test_query_cache_dups_results_correctly
|
||||
Task.cache do
|
||||
now = Time.now.utc
|
||||
task = Task.find 1
|
||||
assert_not_equal now, task.starting
|
||||
task.starting = now
|
||||
task.reload
|
||||
assert_not_equal now, task.starting
|
||||
end
|
||||
end
|
||||
|
||||
def test_cache_is_flat
|
||||
Task.cache do
|
||||
Topic.columns # don't count this query
|
||||
assert_queries(1) { Topic.find(1); Topic.find(1); }
|
||||
end
|
||||
|
||||
ActiveRecord::Base.cache do
|
||||
assert_queries(1) { Task.find(1); Task.find(1) }
|
||||
end
|
||||
end
|
||||
|
||||
def test_cache_does_not_wrap_string_results_in_arrays
|
||||
require 'sqlite3/version' if current_adapter?(:SQLite3Adapter)
|
||||
|
||||
Task.cache do
|
||||
if current_adapter?(:SQLite3Adapter) && SQLite3::Version::VERSION > '1.2.5'
|
||||
assert_instance_of Fixnum, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
||||
else
|
||||
assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class QueryCacheExpiryTest < ActiveRecord::TestCase
|
||||
fixtures :tasks, :posts, :categories, :categories_posts
|
||||
|
||||
def test_find
|
||||
Task.connection.expects(:clear_query_cache).times(1)
|
||||
|
||||
assert !Task.connection.query_cache_enabled
|
||||
Task.cache do
|
||||
assert Task.connection.query_cache_enabled
|
||||
Task.find(1)
|
||||
|
||||
Task.uncached do
|
||||
assert !Task.connection.query_cache_enabled
|
||||
Task.find(1)
|
||||
end
|
||||
|
||||
assert Task.connection.query_cache_enabled
|
||||
end
|
||||
assert !Task.connection.query_cache_enabled
|
||||
end
|
||||
|
||||
def test_update
|
||||
Task.connection.expects(:clear_query_cache).times(2)
|
||||
|
||||
Task.cache do
|
||||
task = Task.find(1)
|
||||
task.starting = Time.now.utc
|
||||
task.save!
|
||||
end
|
||||
end
|
||||
|
||||
def test_destroy
|
||||
Task.connection.expects(:clear_query_cache).times(2)
|
||||
|
||||
Task.cache do
|
||||
Task.find(1).destroy
|
||||
end
|
||||
end
|
||||
|
||||
def test_insert
|
||||
ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
|
||||
|
||||
Task.cache do
|
||||
Task.create!
|
||||
end
|
||||
end
|
||||
|
||||
def test_cache_is_expired_by_habtm_update
|
||||
ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
|
||||
ActiveRecord::Base.cache do
|
||||
c = Category.find(:first)
|
||||
p = Post.find(:first)
|
||||
p.categories << c
|
||||
end
|
||||
end
|
||||
|
||||
def test_cache_is_expired_by_habtm_delete
|
||||
ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
|
||||
ActiveRecord::Base.cache do
|
||||
c = Category.find(1)
|
||||
p = Post.find(1)
|
||||
assert p.categories.any?
|
||||
p.categories.delete_all
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/post'
|
||||
require 'models/comment'
|
||||
require 'models/developer'
|
||||
require 'models/project'
|
||||
require 'models/reader'
|
||||
require 'models/person'
|
||||
|
||||
# Dummy class methods to test implicit association scoping.
|
||||
def Comment.foo() find :first end
|
||||
def Project.foo() find :first end
|
||||
|
||||
|
||||
class ReadOnlyTest < ActiveRecord::TestCase
|
||||
fixtures :posts, :comments, :developers, :projects, :developers_projects
|
||||
|
||||
def test_cant_save_readonly_record
|
||||
dev = Developer.find(1)
|
||||
assert !dev.readonly?
|
||||
|
||||
dev.readonly!
|
||||
assert dev.readonly?
|
||||
|
||||
assert_nothing_raised do
|
||||
dev.name = 'Luscious forbidden fruit.'
|
||||
assert !dev.save
|
||||
dev.name = 'Forbidden.'
|
||||
end
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save }
|
||||
assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! }
|
||||
end
|
||||
|
||||
|
||||
def test_find_with_readonly_option
|
||||
Developer.find(:all).each { |d| assert !d.readonly? }
|
||||
Developer.find(:all, :readonly => false).each { |d| assert !d.readonly? }
|
||||
Developer.find(:all, :readonly => true).each { |d| assert d.readonly? }
|
||||
end
|
||||
|
||||
|
||||
def test_find_with_joins_option_implies_readonly
|
||||
# Blank joins don't count.
|
||||
Developer.find(:all, :joins => ' ').each { |d| assert !d.readonly? }
|
||||
Developer.find(:all, :joins => ' ', :readonly => false).each { |d| assert !d.readonly? }
|
||||
|
||||
# Others do.
|
||||
Developer.find(:all, :joins => ', projects').each { |d| assert d.readonly? }
|
||||
Developer.find(:all, :joins => ', projects', :readonly => false).each { |d| assert !d.readonly? }
|
||||
end
|
||||
|
||||
|
||||
def test_habtm_find_readonly
|
||||
dev = Developer.find(1)
|
||||
assert !dev.projects.empty?
|
||||
assert dev.projects.all?(&:readonly?)
|
||||
assert dev.projects.find(:all).all?(&:readonly?)
|
||||
assert dev.projects.find(:all, :readonly => true).all?(&:readonly?)
|
||||
end
|
||||
|
||||
def test_has_many_find_readonly
|
||||
post = Post.find(1)
|
||||
assert !post.comments.empty?
|
||||
assert !post.comments.any?(&:readonly?)
|
||||
assert !post.comments.find(:all).any?(&:readonly?)
|
||||
assert post.comments.find(:all, :readonly => true).all?(&:readonly?)
|
||||
end
|
||||
|
||||
def test_has_many_with_through_is_not_implicitly_marked_readonly
|
||||
assert people = Post.find(1).people
|
||||
assert !people.any?(&:readonly?)
|
||||
end
|
||||
|
||||
def test_readonly_scoping
|
||||
Post.with_scope(:find => { :conditions => '1=1' }) do
|
||||
assert !Post.find(1).readonly?
|
||||
assert Post.find(1, :readonly => true).readonly?
|
||||
assert !Post.find(1, :readonly => false).readonly?
|
||||
end
|
||||
|
||||
Post.with_scope(:find => { :joins => ' ' }) do
|
||||
assert !Post.find(1).readonly?
|
||||
assert Post.find(1, :readonly => true).readonly?
|
||||
assert !Post.find(1, :readonly => false).readonly?
|
||||
end
|
||||
|
||||
# Oracle barfs on this because the join includes unqualified and
|
||||
# conflicting column names
|
||||
unless current_adapter?(:OracleAdapter)
|
||||
Post.with_scope(:find => { :joins => ', developers' }) do
|
||||
assert Post.find(1).readonly?
|
||||
assert Post.find(1, :readonly => true).readonly?
|
||||
assert !Post.find(1, :readonly => false).readonly?
|
||||
end
|
||||
end
|
||||
|
||||
Post.with_scope(:find => { :readonly => true }) do
|
||||
assert Post.find(1).readonly?
|
||||
assert Post.find(1, :readonly => true).readonly?
|
||||
assert !Post.find(1, :readonly => false).readonly?
|
||||
end
|
||||
end
|
||||
|
||||
def test_association_collection_method_missing_scoping_not_readonly
|
||||
assert !Developer.find(1).projects.foo.readonly?
|
||||
assert !Post.find(1).comments.foo.readonly?
|
||||
end
|
||||
end
|
||||
|
|
@ -1,234 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/customer'
|
||||
require 'models/company'
|
||||
require 'models/company_in_module'
|
||||
require 'models/subscriber'
|
||||
require 'models/ship'
|
||||
require 'models/pirate'
|
||||
|
||||
class ReflectionTest < ActiveRecord::TestCase
|
||||
include ActiveRecord::Reflection
|
||||
|
||||
fixtures :topics, :customers, :companies, :subscribers
|
||||
|
||||
def setup
|
||||
@first = Topic.find(1)
|
||||
end
|
||||
|
||||
def test_column_null_not_null
|
||||
subscriber = Subscriber.find(:first)
|
||||
assert subscriber.column_for_attribute("name").null
|
||||
assert !subscriber.column_for_attribute("nick").null
|
||||
end
|
||||
|
||||
def test_read_attribute_names
|
||||
assert_equal(
|
||||
%w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type ).sort,
|
||||
@first.attribute_names
|
||||
)
|
||||
end
|
||||
|
||||
def test_columns
|
||||
assert_equal 14, Topic.columns.length
|
||||
end
|
||||
|
||||
def test_columns_are_returned_in_the_order_they_were_declared
|
||||
column_names = Topic.columns.map { |column| column.name }
|
||||
assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group), column_names
|
||||
end
|
||||
|
||||
def test_content_columns
|
||||
content_columns = Topic.content_columns
|
||||
content_column_names = content_columns.map {|column| column.name}
|
||||
assert_equal 10, content_columns.length
|
||||
assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title).sort, content_column_names.sort
|
||||
end
|
||||
|
||||
def test_column_string_type_and_limit
|
||||
assert_equal :string, @first.column_for_attribute("title").type
|
||||
assert_equal 255, @first.column_for_attribute("title").limit
|
||||
end
|
||||
|
||||
def test_column_null_not_null
|
||||
subscriber = Subscriber.find(:first)
|
||||
assert subscriber.column_for_attribute("name").null
|
||||
assert !subscriber.column_for_attribute("nick").null
|
||||
end
|
||||
|
||||
def test_human_name_for_column
|
||||
assert_equal "Author name", @first.column_for_attribute("author_name").human_name
|
||||
end
|
||||
|
||||
def test_integer_columns
|
||||
assert_equal :integer, @first.column_for_attribute("id").type
|
||||
end
|
||||
|
||||
def test_reflection_klass_for_nested_class_name
|
||||
reflection = MacroReflection.new(nil, nil, { :class_name => 'MyApplication::Business::Company' }, nil)
|
||||
assert_nothing_raised do
|
||||
assert_equal MyApplication::Business::Company, reflection.klass
|
||||
end
|
||||
end
|
||||
|
||||
def test_aggregation_reflection
|
||||
reflection_for_address = AggregateReflection.new(
|
||||
:composed_of, :address, { :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ] }, Customer
|
||||
)
|
||||
|
||||
reflection_for_balance = AggregateReflection.new(
|
||||
:composed_of, :balance, { :class_name => "Money", :mapping => %w(balance amount) }, Customer
|
||||
)
|
||||
|
||||
reflection_for_gps_location = AggregateReflection.new(
|
||||
:composed_of, :gps_location, { }, Customer
|
||||
)
|
||||
|
||||
assert Customer.reflect_on_all_aggregations.include?(reflection_for_gps_location)
|
||||
assert Customer.reflect_on_all_aggregations.include?(reflection_for_balance)
|
||||
assert Customer.reflect_on_all_aggregations.include?(reflection_for_address)
|
||||
|
||||
assert_equal reflection_for_address, Customer.reflect_on_aggregation(:address)
|
||||
|
||||
assert_equal Address, Customer.reflect_on_aggregation(:address).klass
|
||||
|
||||
assert_equal Money, Customer.reflect_on_aggregation(:balance).klass
|
||||
end
|
||||
|
||||
def test_reflect_on_all_autosave_associations
|
||||
expected = Pirate.reflect_on_all_associations.select { |r| r.options[:autosave] }
|
||||
received = Pirate.reflect_on_all_autosave_associations
|
||||
|
||||
assert !received.empty?
|
||||
assert_not_equal Pirate.reflect_on_all_associations.length, received.length
|
||||
assert_equal expected, received
|
||||
end
|
||||
|
||||
def test_has_many_reflection
|
||||
reflection_for_clients = AssociationReflection.new(:has_many, :clients, { :order => "id", :dependent => :destroy }, Firm)
|
||||
|
||||
assert_equal reflection_for_clients, Firm.reflect_on_association(:clients)
|
||||
|
||||
assert_equal Client, Firm.reflect_on_association(:clients).klass
|
||||
assert_equal 'companies', Firm.reflect_on_association(:clients).table_name
|
||||
|
||||
assert_equal Client, Firm.reflect_on_association(:clients_of_firm).klass
|
||||
assert_equal 'companies', Firm.reflect_on_association(:clients_of_firm).table_name
|
||||
end
|
||||
|
||||
def test_has_one_reflection
|
||||
reflection_for_account = AssociationReflection.new(:has_one, :account, { :foreign_key => "firm_id", :dependent => :destroy }, Firm)
|
||||
assert_equal reflection_for_account, Firm.reflect_on_association(:account)
|
||||
|
||||
assert_equal Account, Firm.reflect_on_association(:account).klass
|
||||
assert_equal 'accounts', Firm.reflect_on_association(:account).table_name
|
||||
end
|
||||
|
||||
def test_belongs_to_inferred_foreign_key_from_assoc_name
|
||||
Company.belongs_to :foo
|
||||
assert_equal "foo_id", Company.reflect_on_association(:foo).primary_key_name
|
||||
Company.belongs_to :bar, :class_name => "Xyzzy"
|
||||
assert_equal "bar_id", Company.reflect_on_association(:bar).primary_key_name
|
||||
Company.belongs_to :baz, :class_name => "Xyzzy", :foreign_key => "xyzzy_id"
|
||||
assert_equal "xyzzy_id", Company.reflect_on_association(:baz).primary_key_name
|
||||
end
|
||||
|
||||
def test_association_reflection_in_modules
|
||||
assert_reflection MyApplication::Business::Firm,
|
||||
:clients_of_firm,
|
||||
:klass => MyApplication::Business::Client,
|
||||
:class_name => 'Client',
|
||||
:table_name => 'companies'
|
||||
|
||||
assert_reflection MyApplication::Billing::Account,
|
||||
:firm,
|
||||
:klass => MyApplication::Business::Firm,
|
||||
:class_name => 'MyApplication::Business::Firm',
|
||||
:table_name => 'companies'
|
||||
|
||||
assert_reflection MyApplication::Billing::Account,
|
||||
:qualified_billing_firm,
|
||||
:klass => MyApplication::Billing::Firm,
|
||||
:class_name => 'MyApplication::Billing::Firm',
|
||||
:table_name => 'companies'
|
||||
|
||||
assert_reflection MyApplication::Billing::Account,
|
||||
:unqualified_billing_firm,
|
||||
:klass => MyApplication::Billing::Firm,
|
||||
:class_name => 'Firm',
|
||||
:table_name => 'companies'
|
||||
|
||||
assert_reflection MyApplication::Billing::Account,
|
||||
:nested_qualified_billing_firm,
|
||||
:klass => MyApplication::Billing::Nested::Firm,
|
||||
:class_name => 'MyApplication::Billing::Nested::Firm',
|
||||
:table_name => 'companies'
|
||||
|
||||
assert_reflection MyApplication::Billing::Account,
|
||||
:nested_unqualified_billing_firm,
|
||||
:klass => MyApplication::Billing::Nested::Firm,
|
||||
:class_name => 'Nested::Firm',
|
||||
:table_name => 'companies'
|
||||
end
|
||||
|
||||
def test_reflection_of_all_associations
|
||||
# FIXME these assertions bust a lot
|
||||
assert_equal 36, Firm.reflect_on_all_associations.size
|
||||
assert_equal 26, Firm.reflect_on_all_associations(:has_many).size
|
||||
assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
|
||||
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
|
||||
end
|
||||
|
||||
def test_reflection_should_not_raise_error_when_compared_to_other_object
|
||||
assert_nothing_raised { Firm.reflections[:clients] == Object.new }
|
||||
end
|
||||
|
||||
def test_has_many_through_reflection
|
||||
assert_kind_of ThroughReflection, Subscriber.reflect_on_association(:books)
|
||||
end
|
||||
|
||||
def test_collection_association
|
||||
assert Pirate.reflect_on_association(:birds).collection?
|
||||
assert Pirate.reflect_on_association(:parrots).collection?
|
||||
|
||||
assert !Pirate.reflect_on_association(:ship).collection?
|
||||
assert !Ship.reflect_on_association(:pirate).collection?
|
||||
end
|
||||
|
||||
def test_default_association_validation
|
||||
assert AssociationReflection.new(:has_many, :clients, {}, Firm).validate?
|
||||
|
||||
assert !AssociationReflection.new(:has_one, :client, {}, Firm).validate?
|
||||
assert !AssociationReflection.new(:belongs_to, :client, {}, Firm).validate?
|
||||
assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, {}, Firm).validate?
|
||||
end
|
||||
|
||||
def test_always_validate_association_if_explicit
|
||||
assert AssociationReflection.new(:has_one, :client, { :validate => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:belongs_to, :client, { :validate => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:has_many, :clients, { :validate => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :validate => true }, Firm).validate?
|
||||
end
|
||||
|
||||
def test_validate_association_if_autosave
|
||||
assert AssociationReflection.new(:has_one, :client, { :autosave => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:belongs_to, :client, { :autosave => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:has_many, :clients, { :autosave => true }, Firm).validate?
|
||||
assert AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true }, Firm).validate?
|
||||
end
|
||||
|
||||
def test_never_validate_association_if_explicit
|
||||
assert !AssociationReflection.new(:has_one, :client, { :autosave => true, :validate => false }, Firm).validate?
|
||||
assert !AssociationReflection.new(:belongs_to, :client, { :autosave => true, :validate => false }, Firm).validate?
|
||||
assert !AssociationReflection.new(:has_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
|
||||
assert !AssociationReflection.new(:has_and_belongs_to_many, :clients, { :autosave => true, :validate => false }, Firm).validate?
|
||||
end
|
||||
|
||||
private
|
||||
def assert_reflection(klass, association, options)
|
||||
assert reflection = klass.reflect_on_association(association)
|
||||
options.each do |method, value|
|
||||
assert_equal(value, reflection.send(method))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,22 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
|
||||
class ReloadModelsTest < ActiveRecord::TestCase
|
||||
fixtures :pets
|
||||
|
||||
def test_has_one_with_reload
|
||||
pet = Pet.find_by_name('parrot')
|
||||
pet.owner = Owner.find_by_name('ashley')
|
||||
|
||||
# Reload the class Owner, simulating auto-reloading of model classes in a
|
||||
# development environment. Note that meanwhile the class Pet is not
|
||||
# reloaded, simulating a class that is present in a plugin.
|
||||
Object.class_eval { remove_const :Owner }
|
||||
Kernel.load(File.expand_path(File.join(File.dirname(__FILE__), "../models/owner.rb")))
|
||||
|
||||
pet = Pet.find_by_name('parrot')
|
||||
pet.owner = Owner.find_by_name('ashley')
|
||||
assert_equal pet.owner, Owner.find_by_name('ashley')
|
||||
end
|
||||
end
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
module ActiveRecord
|
||||
module Testing
|
||||
module RepairHelper
|
||||
def self.included(base)
|
||||
base.class_eval do
|
||||
extend ClassMethods
|
||||
end
|
||||
end
|
||||
|
||||
module Toolbox
|
||||
def self.record_validations(*model_classes)
|
||||
model_classes.inject({}) do |repair, klass|
|
||||
repair[klass] ||= {}
|
||||
[:validate, :validate_on_create, :validate_on_update].each do |callback|
|
||||
the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks")
|
||||
repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup)
|
||||
end
|
||||
repair
|
||||
end
|
||||
end
|
||||
|
||||
def self.reset_validations(recorded)
|
||||
recorded.each do |klass, repairs|
|
||||
[:validate, :validate_on_create, :validate_on_update].each do |callback|
|
||||
klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def repair_validations(*model_classes)
|
||||
setup do
|
||||
@validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
|
||||
end
|
||||
teardown do
|
||||
ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(@validation_repairs)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def repair_validations(*model_classes, &block)
|
||||
validation_repairs = ActiveRecord::Testing::RepairHelper::Toolbox.record_validations(*model_classes)
|
||||
return block.call
|
||||
ensure
|
||||
ActiveRecord::Testing::RepairHelper::Toolbox.reset_validations(validation_repairs)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,176 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class Group < ActiveRecord::Base
|
||||
Group.table_name = 'group'
|
||||
belongs_to :select, :class_name => 'Select'
|
||||
has_one :values
|
||||
end
|
||||
|
||||
class Select < ActiveRecord::Base
|
||||
Select.table_name = 'select'
|
||||
has_many :groups
|
||||
end
|
||||
|
||||
class Values < ActiveRecord::Base
|
||||
Values.table_name = 'values'
|
||||
end
|
||||
|
||||
class Distinct < ActiveRecord::Base
|
||||
Distinct.table_name = 'distinct'
|
||||
has_and_belongs_to_many :selects
|
||||
has_many :values, :through => :groups
|
||||
end
|
||||
|
||||
# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
|
||||
# reserved word names (ie: group, order, values, etc...)
|
||||
class MysqlReservedWordTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
|
||||
# we call execute directly here (and do similar below) because ActiveRecord::Base#create_table()
|
||||
# will fail with these table names if these test cases fail
|
||||
|
||||
create_tables_directly 'group'=>'id int auto_increment primary key, `order` varchar(255), select_id int',
|
||||
'select'=>'id int auto_increment primary key',
|
||||
'values'=>'id int auto_increment primary key, group_id int',
|
||||
'distinct'=>'id int auto_increment primary key',
|
||||
'distincts_selects'=>'distinct_id int, select_id int'
|
||||
end
|
||||
|
||||
def teardown
|
||||
drop_tables_directly ['group', 'select', 'values', 'distinct', 'distincts_selects', 'order']
|
||||
end
|
||||
|
||||
# create tables with reserved-word names and columns
|
||||
def test_create_tables
|
||||
assert_nothing_raised {
|
||||
@connection.create_table :order do |t|
|
||||
t.column :group, :string
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
# rename tables with reserved-word names
|
||||
def test_rename_tables
|
||||
assert_nothing_raised { @connection.rename_table(:group, :order) }
|
||||
end
|
||||
|
||||
# alter column with a reserved-word name in a table with a reserved-word name
|
||||
def test_change_columns
|
||||
assert_nothing_raised { @connection.change_column_default(:group, :order, 'whatever') }
|
||||
#the quoting here will reveal any double quoting issues in change_column's interaction with the column method in the adapter
|
||||
assert_nothing_raised { @connection.change_column('group', 'order', :Int, :default => 0) }
|
||||
assert_nothing_raised { @connection.rename_column(:group, :order, :values) }
|
||||
end
|
||||
|
||||
# dump structure of table with reserved word name
|
||||
def test_structure_dump
|
||||
assert_nothing_raised { @connection.structure_dump }
|
||||
end
|
||||
|
||||
# introspect table with reserved word name
|
||||
def test_introspect
|
||||
assert_nothing_raised { @connection.columns(:group) }
|
||||
assert_nothing_raised { @connection.indexes(:group) }
|
||||
end
|
||||
|
||||
#fixtures
|
||||
self.use_instantiated_fixtures = true
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
#fixtures :group
|
||||
|
||||
def test_fixtures
|
||||
f = create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
|
||||
|
||||
assert_nothing_raised {
|
||||
f.each do |x|
|
||||
x.delete_existing_fixtures
|
||||
end
|
||||
}
|
||||
|
||||
assert_nothing_raised {
|
||||
f.each do |x|
|
||||
x.insert_fixtures
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
#activerecord model class with reserved-word table name
|
||||
def test_activerecord_model
|
||||
create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
|
||||
x = nil
|
||||
assert_nothing_raised { x = Group.new }
|
||||
x.order = 'x'
|
||||
assert_nothing_raised { x.save }
|
||||
x.order = 'y'
|
||||
assert_nothing_raised { x.save }
|
||||
assert_nothing_raised { y = Group.find_by_order('y') }
|
||||
assert_nothing_raised { y = Group.find(1) }
|
||||
x = Group.find(1)
|
||||
end
|
||||
|
||||
# has_one association with reserved-word table name
|
||||
def test_has_one_associations
|
||||
create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
|
||||
v = nil
|
||||
assert_nothing_raised { v = Group.find(1).values }
|
||||
assert_equal v.id, 2
|
||||
end
|
||||
|
||||
# belongs_to association with reserved-word table name
|
||||
def test_belongs_to_associations
|
||||
create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
|
||||
gs = nil
|
||||
assert_nothing_raised { gs = Select.find(2).groups }
|
||||
assert_equal gs.length, 2
|
||||
assert(gs.collect{|x| x.id}.sort == [2, 3])
|
||||
end
|
||||
|
||||
# has_and_belongs_to_many with reserved-word table name
|
||||
def test_has_and_belongs_to_many
|
||||
create_test_fixtures :select, :distinct, :group, :values, :distincts_selects
|
||||
s = nil
|
||||
assert_nothing_raised { s = Distinct.find(1).selects }
|
||||
assert_equal s.length, 2
|
||||
assert(s.collect{|x|x.id}.sort == [1, 2])
|
||||
end
|
||||
|
||||
# activerecord model introspection with reserved-word table and column names
|
||||
def test_activerecord_introspection
|
||||
assert_nothing_raised { Group.table_exists? }
|
||||
assert_nothing_raised { Group.columns }
|
||||
end
|
||||
|
||||
# Calculations
|
||||
def test_calculations_work_with_reserved_words
|
||||
assert_nothing_raised { Group.count }
|
||||
end
|
||||
|
||||
def test_associations_work_with_reserved_words
|
||||
assert_nothing_raised { Select.find(:all, :include => [:groups]) }
|
||||
end
|
||||
|
||||
#the following functions were added to DRY test cases
|
||||
|
||||
private
|
||||
# custom fixture loader, uses Fixtures#create_fixtures and appends base_path to the current file's path
|
||||
def create_test_fixtures(*fixture_names)
|
||||
Fixtures.create_fixtures(FIXTURES_ROOT + "/reserved_words", fixture_names)
|
||||
end
|
||||
|
||||
# custom drop table, uses execute on connection to drop a table if it exists. note: escapes table_name
|
||||
def drop_tables_directly(table_names, connection = @connection)
|
||||
table_names.each do |name|
|
||||
connection.execute("DROP TABLE IF EXISTS `#{name}`")
|
||||
end
|
||||
end
|
||||
|
||||
# custom create table, uses execute on connection to create a table, note: escapes table_name, does NOT escape columns
|
||||
def create_tables_directly (tables, connection = @connection)
|
||||
tables.each do |table_name, column_properties|
|
||||
connection.execute("CREATE TABLE `#{table_name}` ( #{column_properties} )")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/binary'
|
||||
|
||||
class SanitizeTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
end
|
||||
|
||||
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])
|
||||
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])
|
||||
end
|
||||
|
||||
def test_sanitize_sql_array_handles_bind_variables
|
||||
quoted_bambi = ActiveRecord::Base.connection.quote("Bambi")
|
||||
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi"])
|
||||
assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi".mb_chars])
|
||||
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote("Bambi\nand\nThumper")
|
||||
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper"])
|
||||
assert_equal "name=#{quoted_bambi_and_thumper}", Binary.send(:sanitize_sql_array, ["name=?", "Bambi\nand\nThumper".mb_chars])
|
||||
end
|
||||
end
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class SchemaThing < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class SchemaAuthorizationTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
TABLE_NAME = 'schema_things'
|
||||
COLUMNS = [
|
||||
'id serial primary key',
|
||||
'name character varying(50)'
|
||||
]
|
||||
USERS = ['rails_pg_schema_user1', 'rails_pg_schema_user2']
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute "SET search_path TO '$user',public"
|
||||
set_session_auth
|
||||
USERS.each do |u|
|
||||
@connection.execute "CREATE USER #{u}" rescue nil
|
||||
@connection.execute "CREATE SCHEMA AUTHORIZATION #{u}" rescue nil
|
||||
set_session_auth u
|
||||
@connection.execute "CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "INSERT INTO #{TABLE_NAME} (name) VALUES ('#{u}')"
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
|
||||
def teardown
|
||||
set_session_auth
|
||||
@connection.execute "RESET search_path"
|
||||
USERS.each do |u|
|
||||
@connection.execute "DROP SCHEMA #{u} CASCADE"
|
||||
@connection.execute "DROP USER #{u}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_invisible
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
set_session_auth
|
||||
@connection.execute "SELECT * FROM #{TABLE_NAME}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_uniqueness
|
||||
assert_nothing_raised do
|
||||
set_session_auth
|
||||
USERS.each do |u|
|
||||
set_session_auth u
|
||||
assert_equal u, @connection.select_value("SELECT name FROM #{TABLE_NAME} WHERE id = 1")
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_sequence_schema_caching
|
||||
assert_nothing_raised do
|
||||
USERS.each do |u|
|
||||
set_session_auth u
|
||||
st = SchemaThing.new :name => 'TEST1'
|
||||
st.save!
|
||||
st = SchemaThing.new :id => 5, :name => 'TEST2'
|
||||
st.save!
|
||||
set_session_auth
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def set_session_auth auth = nil
|
||||
@connection.execute "SET SESSION AUTHORIZATION #{auth || 'default'}"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,211 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'stringio'
|
||||
|
||||
|
||||
class SchemaDumperTest < ActiveRecord::TestCase
|
||||
def standard_dump
|
||||
stream = StringIO.new
|
||||
ActiveRecord::SchemaDumper.ignore_tables = []
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
stream.string
|
||||
end
|
||||
|
||||
def test_schema_dump
|
||||
output = standard_dump
|
||||
assert_match %r{create_table "accounts"}, output
|
||||
assert_match %r{create_table "authors"}, output
|
||||
assert_no_match %r{create_table "schema_migrations"}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_excludes_sqlite_sequence
|
||||
output = standard_dump
|
||||
assert_no_match %r{create_table "sqlite_sequence"}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_camelcase_table_name
|
||||
output = standard_dump
|
||||
assert_match %r{create_table "CamelCase"}, output
|
||||
end
|
||||
|
||||
def assert_line_up(lines, pattern, required = false)
|
||||
return assert(true) if lines.empty?
|
||||
matches = lines.map { |line| line.match(pattern) }
|
||||
assert matches.all? if required
|
||||
matches.compact!
|
||||
return assert(true) if matches.empty?
|
||||
assert_equal 1, matches.map{ |match| match.offset(0).first }.uniq.length
|
||||
end
|
||||
|
||||
def column_definition_lines(output = standard_dump)
|
||||
output.scan(/^( *)create_table.*?\n(.*?)^\1end/m).map{ |m| m.last.split(/\n/) }
|
||||
end
|
||||
|
||||
def test_types_line_up
|
||||
column_definition_lines.each do |column_set|
|
||||
next if column_set.empty?
|
||||
|
||||
lengths = column_set.map do |column|
|
||||
if match = column.match(/t\.(?:integer|decimal|float|datetime|timestamp|time|date|text|binary|string|boolean)\s+"/)
|
||||
match[0].length
|
||||
end
|
||||
end
|
||||
|
||||
assert_equal 1, lengths.uniq.length
|
||||
end
|
||||
end
|
||||
|
||||
def test_arguments_line_up
|
||||
column_definition_lines.each do |column_set|
|
||||
assert_line_up(column_set, /:default => /)
|
||||
assert_line_up(column_set, /:limit => /)
|
||||
assert_line_up(column_set, /:null => /)
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_dump_errors
|
||||
output = standard_dump
|
||||
assert_no_match %r{\# Could not dump table}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_not_null_columns
|
||||
stream = StringIO.new
|
||||
|
||||
ActiveRecord::SchemaDumper.ignore_tables = [/^[^r]/]
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
output = stream.string
|
||||
assert_match %r{:null => false}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_limit_constraint_for_integer_columns
|
||||
stream = StringIO.new
|
||||
|
||||
ActiveRecord::SchemaDumper.ignore_tables = [/^(?!integer_limits)/]
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
output = stream.string
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
assert_match %r{c_int_1.*:limit => 2}, output
|
||||
assert_match %r{c_int_2.*:limit => 2}, output
|
||||
|
||||
# int 3 is 4 bytes in postgresql
|
||||
assert_match %r{c_int_3.*}, output
|
||||
assert_no_match %r{c_int_3.*:limit}, output
|
||||
|
||||
assert_match %r{c_int_4.*}, output
|
||||
assert_no_match %r{c_int_4.*:limit}, output
|
||||
elsif current_adapter?(:MysqlAdapter)
|
||||
assert_match %r{c_int_1.*:limit => 1}, output
|
||||
assert_match %r{c_int_2.*:limit => 2}, output
|
||||
assert_match %r{c_int_3.*:limit => 3}, output
|
||||
|
||||
assert_match %r{c_int_4.*}, output
|
||||
assert_no_match %r{c_int_4.*:limit}, output
|
||||
elsif current_adapter?(:SQLiteAdapter)
|
||||
assert_match %r{c_int_1.*:limit => 1}, output
|
||||
assert_match %r{c_int_2.*:limit => 2}, output
|
||||
assert_match %r{c_int_3.*:limit => 3}, output
|
||||
assert_match %r{c_int_4.*:limit => 4}, output
|
||||
end
|
||||
assert_match %r{c_int_without_limit.*}, output
|
||||
assert_no_match %r{c_int_without_limit.*:limit}, output
|
||||
|
||||
if current_adapter?(:SQLiteAdapter)
|
||||
assert_match %r{c_int_5.*:limit => 5}, output
|
||||
assert_match %r{c_int_6.*:limit => 6}, output
|
||||
assert_match %r{c_int_7.*:limit => 7}, output
|
||||
assert_match %r{c_int_8.*:limit => 8}, output
|
||||
else
|
||||
assert_match %r{c_int_5.*:limit => 8}, output
|
||||
assert_match %r{c_int_6.*:limit => 8}, output
|
||||
assert_match %r{c_int_7.*:limit => 8}, output
|
||||
assert_match %r{c_int_8.*:limit => 8}, output
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_dump_with_string_ignored_table
|
||||
stream = StringIO.new
|
||||
|
||||
ActiveRecord::SchemaDumper.ignore_tables = ['accounts']
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
output = stream.string
|
||||
assert_no_match %r{create_table "accounts"}, output
|
||||
assert_match %r{create_table "authors"}, output
|
||||
assert_no_match %r{create_table "schema_migrations"}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_with_regexp_ignored_table
|
||||
stream = StringIO.new
|
||||
|
||||
ActiveRecord::SchemaDumper.ignore_tables = [/^account/]
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
output = stream.string
|
||||
assert_no_match %r{create_table "accounts"}, output
|
||||
assert_match %r{create_table "authors"}, output
|
||||
assert_no_match %r{create_table "schema_migrations"}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_illegal_ignored_table_value
|
||||
stream = StringIO.new
|
||||
ActiveRecord::SchemaDumper.ignore_tables = [5]
|
||||
assert_raise(StandardError) do
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_dumps_index_columns_in_right_order
|
||||
index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip
|
||||
assert_equal 'add_index "companies", ["firm_id", "type", "rating", "ruby_type"], :name => "company_index"', index_definition
|
||||
end
|
||||
|
||||
def test_schema_dump_should_honor_nonstandard_primary_keys
|
||||
output = standard_dump
|
||||
match = output.match(%r{create_table "movies"(.*)do})
|
||||
assert_not_nil(match, "nonstandardpk table not found")
|
||||
assert_match %r(:primary_key => "movieid"), match[1], "non-standard primary key not preserved"
|
||||
end
|
||||
|
||||
if current_adapter?(:MysqlAdapter)
|
||||
def test_schema_dump_should_not_add_default_value_for_mysql_text_field
|
||||
output = standard_dump
|
||||
assert_match %r{t.text\s+"body",\s+:null => false$}, output
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
|
||||
output = standard_dump
|
||||
assert_match %r{t.binary\s+"tiny_blob",\s+:limit => 255$}, output
|
||||
assert_match %r{t.binary\s+"normal_blob"$}, output
|
||||
assert_match %r{t.binary\s+"medium_blob",\s+:limit => 16777215$}, output
|
||||
assert_match %r{t.binary\s+"long_blob",\s+:limit => 2147483647$}, output
|
||||
assert_match %r{t.text\s+"tiny_text",\s+:limit => 255$}, output
|
||||
assert_match %r{t.text\s+"normal_text"$}, output
|
||||
assert_match %r{t.text\s+"medium_text",\s+:limit => 16777215$}, output
|
||||
assert_match %r{t.text\s+"long_text",\s+:limit => 2147483647$}, output
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_dump_includes_decimal_options
|
||||
stream = StringIO.new
|
||||
ActiveRecord::SchemaDumper.ignore_tables = [/^[^n]/]
|
||||
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream)
|
||||
output = stream.string
|
||||
assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
def test_schema_dump_includes_xml_shorthand_definition
|
||||
output = standard_dump
|
||||
if %r{create_table "postgresql_xml_data_type"} =~ output
|
||||
assert_match %r{t.xml "data"}, output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_schema_dump_keeps_id_column_when_id_is_false_and_id_column_added
|
||||
output = standard_dump
|
||||
match = output.match(%r{create_table "goofy_string_id"(.*)do.*\n(.*)\n})
|
||||
assert_not_nil(match, "goofy_string_id table not found")
|
||||
assert_match %r(:id => false), match[1], "no table id not preserved"
|
||||
assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class SchemaTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
SCHEMA_NAME = 'test_schema'
|
||||
SCHEMA2_NAME = 'test_schema2'
|
||||
TABLE_NAME = 'things'
|
||||
CAPITALIZED_TABLE_NAME = 'Things'
|
||||
INDEX_A_NAME = 'a_index_things_on_name'
|
||||
INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
|
||||
INDEX_A_COLUMN = 'name'
|
||||
INDEX_B_COLUMN_S1 = 'email'
|
||||
INDEX_B_COLUMN_S2 = 'moment'
|
||||
COLUMNS = [
|
||||
'id integer',
|
||||
'name character varying(50)',
|
||||
'email character varying(50)',
|
||||
'moment timestamp without time zone default now()'
|
||||
]
|
||||
|
||||
class Thing1 < ActiveRecord::Base
|
||||
set_table_name "test_schema.things"
|
||||
end
|
||||
|
||||
class Thing2 < ActiveRecord::Base
|
||||
set_table_name "test_schema2.things"
|
||||
end
|
||||
|
||||
class Thing3 < ActiveRecord::Base
|
||||
set_table_name 'test_schema."things.table"'
|
||||
end
|
||||
|
||||
class Thing4 < ActiveRecord::Base
|
||||
set_table_name 'test_schema."Things"'
|
||||
end
|
||||
|
||||
def setup
|
||||
@connection = ActiveRecord::Base.connection
|
||||
@connection.execute "CREATE SCHEMA #{SCHEMA_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{TABLE_NAME}.table\" (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.\"#{CAPITALIZED_TABLE_NAME}\" (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE SCHEMA #{SCHEMA2_NAME} CREATE TABLE #{TABLE_NAME} (#{COLUMNS.join(',')})"
|
||||
@connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});"
|
||||
@connection.execute "CREATE INDEX #{INDEX_A_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_A_COLUMN});"
|
||||
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S1});"
|
||||
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});"
|
||||
end
|
||||
|
||||
def teardown
|
||||
@connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE"
|
||||
@connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
|
||||
end
|
||||
|
||||
def test_with_schema_prefixed_table_name
|
||||
assert_nothing_raised do
|
||||
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{TABLE_NAME}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_schema_prefixed_capitalized_table_name
|
||||
assert_nothing_raised do
|
||||
assert_equal COLUMNS, columns("#{SCHEMA_NAME}.#{CAPITALIZED_TABLE_NAME}")
|
||||
end
|
||||
end
|
||||
|
||||
def test_with_schema_search_path
|
||||
assert_nothing_raised do
|
||||
with_schema_search_path(SCHEMA_NAME) do
|
||||
assert_equal COLUMNS, columns(TABLE_NAME)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def test_proper_encoding_of_table_name
|
||||
assert_equal '"table_name"', @connection.quote_table_name('table_name')
|
||||
assert_equal '"table.name"', @connection.quote_table_name('"table.name"')
|
||||
assert_equal '"schema_name"."table_name"', @connection.quote_table_name('schema_name.table_name')
|
||||
assert_equal '"schema_name"."table.name"', @connection.quote_table_name('schema_name."table.name"')
|
||||
assert_equal '"schema.name"."table_name"', @connection.quote_table_name('"schema.name".table_name')
|
||||
assert_equal '"schema.name"."table.name"', @connection.quote_table_name('"schema.name"."table.name"')
|
||||
end
|
||||
|
||||
def test_classes_with_qualified_schema_name
|
||||
assert_equal 0, Thing1.count
|
||||
assert_equal 0, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing1.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 0, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing2.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 0, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing3.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 1, Thing3.count
|
||||
assert_equal 0, Thing4.count
|
||||
|
||||
Thing4.create(:id => 1, :name => "thing1", :email => "thing1@localhost", :moment => Time.now)
|
||||
assert_equal 1, Thing1.count
|
||||
assert_equal 1, Thing2.count
|
||||
assert_equal 1, Thing3.count
|
||||
assert_equal 1, Thing4.count
|
||||
end
|
||||
|
||||
def test_raise_on_unquoted_schema_name
|
||||
assert_raise(ActiveRecord::StatementInvalid) do
|
||||
with_schema_search_path '$user,public'
|
||||
end
|
||||
end
|
||||
|
||||
def test_without_schema_search_path
|
||||
assert_raise(ActiveRecord::StatementInvalid) { columns(TABLE_NAME) }
|
||||
end
|
||||
|
||||
def test_ignore_nil_schema_search_path
|
||||
assert_nothing_raised { with_schema_search_path nil }
|
||||
end
|
||||
|
||||
def test_dump_indexes_for_schema_one
|
||||
do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1)
|
||||
end
|
||||
|
||||
def test_dump_indexes_for_schema_two
|
||||
do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
|
||||
end
|
||||
|
||||
def test_with_uppercase_index_name
|
||||
ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"}
|
||||
|
||||
ActiveRecord::Base.connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
|
||||
ActiveRecord::Base.connection.schema_search_path = SCHEMA_NAME
|
||||
assert_nothing_raised { ActiveRecord::Base.connection.remove_index! "things", "things_Index"}
|
||||
ActiveRecord::Base.connection.schema_search_path = "public"
|
||||
end
|
||||
|
||||
private
|
||||
def columns(table_name)
|
||||
@connection.send(:column_definitions, table_name).map do |name, type, default|
|
||||
"#{name} #{type}" + (default ? " default #{default}" : '')
|
||||
end
|
||||
end
|
||||
|
||||
def with_schema_search_path(schema_search_path)
|
||||
@connection.schema_search_path = schema_search_path
|
||||
yield if block_given?
|
||||
ensure
|
||||
@connection.schema_search_path = "'$user', public"
|
||||
end
|
||||
|
||||
def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name)
|
||||
with_schema_search_path(this_schema_name) do
|
||||
indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name}
|
||||
assert_equal 2,indexes.size
|
||||
|
||||
do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name)
|
||||
do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name)
|
||||
end
|
||||
end
|
||||
|
||||
def do_dump_index_assertions_for_one_index(this_index, this_index_name, this_index_column)
|
||||
assert_equal TABLE_NAME, this_index.table
|
||||
assert_equal 1, this_index.columns.size
|
||||
assert_equal this_index_column, this_index.columns[0]
|
||||
assert_equal this_index_name, this_index.name
|
||||
end
|
||||
end
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/contact'
|
||||
|
||||
class SerializationTest < ActiveRecord::TestCase
|
||||
FORMATS = [ :xml, :json ]
|
||||
|
||||
def setup
|
||||
@contact_attributes = {
|
||||
:name => 'aaron stack',
|
||||
:age => 25,
|
||||
:avatar => 'binarydata',
|
||||
:created_at => Time.utc(2006, 8, 1),
|
||||
:awesome => false,
|
||||
:preferences => { :gem => '<strong>ruby</strong>' }
|
||||
}
|
||||
|
||||
@contact = Contact.new(@contact_attributes)
|
||||
end
|
||||
|
||||
def test_serialize_should_be_reversible
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new.send("to_#{format}")
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
|
||||
assert_equal @contact_attributes.keys.collect(&:to_s).sort, contact.attributes.keys.collect(&:to_s).sort, "For #{format}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_serialize_should_allow_attribute_only_filtering
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
assert_equal @contact_attributes[:name], contact.name, "For #{format}"
|
||||
assert_nil contact.avatar, "For #{format}"
|
||||
end
|
||||
end
|
||||
|
||||
def test_serialize_should_allow_attribute_except_filtering
|
||||
for format in FORMATS
|
||||
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
|
||||
contact = Contact.new.send("from_#{format}", @serialized)
|
||||
assert_nil contact.name, "For #{format}"
|
||||
assert_nil contact.age, "For #{format}"
|
||||
assert_equal @contact_attributes[:awesome], contact.awesome, "For #{format}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/minimalistic'
|
||||
|
||||
class StoredProcedureTest < ActiveRecord::TestCase
|
||||
fixtures :topics
|
||||
|
||||
# Test that MySQL allows multiple results for stored procedures
|
||||
if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
|
||||
def test_multi_results_from_find_by_sql
|
||||
topics = Topic.find_by_sql 'CALL topics();'
|
||||
assert_equal 1, topics.size
|
||||
assert ActiveRecord::Base.connection.active?, "Bad connection use by 'MysqlAdapter.select'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/subject'
|
||||
|
||||
# confirm that synonyms work just like tables; in this case
|
||||
# the "subjects" table in Oracle (defined in oci.sql) is just
|
||||
# a synonym to the "topics" table
|
||||
|
||||
class TestOracleSynonym < ActiveRecord::TestCase
|
||||
|
||||
def test_oracle_synonym
|
||||
topic = Topic.new
|
||||
subject = Subject.new
|
||||
assert_equal(topic.attributes, subject.attributes)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
require 'cases/helper'
|
||||
require 'models/developer'
|
||||
require 'models/owner'
|
||||
require 'models/pet'
|
||||
|
||||
class TimestampTest < ActiveRecord::TestCase
|
||||
fixtures :developers, :owners, :pets
|
||||
|
||||
def setup
|
||||
@developer = Developer.first
|
||||
@previously_updated_at = @developer.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_changed_record_updates_its_timestamp
|
||||
@developer.name = "Jack Bauer"
|
||||
@developer.save!
|
||||
|
||||
assert @previously_updated_at != @developer.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_unchanged_record_doesnt_update_its_timestamp
|
||||
@developer.save!
|
||||
|
||||
assert @previously_updated_at == @developer.updated_at
|
||||
end
|
||||
|
||||
def test_touching_a_record_updates_its_timestamp
|
||||
@developer.touch
|
||||
|
||||
assert @previously_updated_at != @developer.updated_at
|
||||
end
|
||||
|
||||
def test_touching_a_different_attribute
|
||||
previously_created_at = @developer.created_at
|
||||
@developer.touch(:created_at)
|
||||
|
||||
assert previously_created_at != @developer.created_at
|
||||
end
|
||||
|
||||
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_destroying_a_record_with_a_belongs_to_that_specifies_touching_the_parent_should_update_the_parent_updated_at
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_updated_at = owner.updated_at
|
||||
|
||||
pet.destroy
|
||||
|
||||
assert previously_owner_updated_at != pet.owner.updated_at
|
||||
end
|
||||
|
||||
def test_saving_a_record_with_a_belongs_to_that_specifies_touching_a_specific_attribute_the_parent_should_update_that_attribute
|
||||
Pet.belongs_to :owner, :touch => :happy_at
|
||||
|
||||
pet = Pet.first
|
||||
owner = pet.owner
|
||||
previously_owner_happy_at = owner.happy_at
|
||||
|
||||
pet.name = "Fluffy the Third"
|
||||
pet.save
|
||||
|
||||
assert previously_owner_happy_at != pet.owner.happy_at
|
||||
ensure
|
||||
Pet.belongs_to :owner, :touch => true
|
||||
end
|
||||
end
|
||||
|
|
@ -1,543 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/developer'
|
||||
require 'models/book'
|
||||
require 'models/author'
|
||||
require 'models/post'
|
||||
|
||||
class TransactionTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
fixtures :topics, :developers, :authors, :posts
|
||||
|
||||
def setup
|
||||
@first, @second = Topic.find(1, 2).sort_by { |t| t.id }
|
||||
end
|
||||
|
||||
def test_successful
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
end
|
||||
|
||||
assert Topic.find(1).approved?, "First should have been approved"
|
||||
assert !Topic.find(2).approved?, "Second should have been unapproved"
|
||||
end
|
||||
|
||||
def transaction_with_return
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def test_update_attributes_should_rollback_on_failure
|
||||
author = Author.find(1)
|
||||
posts_count = author.posts.size
|
||||
assert posts_count > 0
|
||||
status = author.update_attributes(:name => nil, :post_ids => [])
|
||||
assert !status
|
||||
assert_equal posts_count, author.posts(true).size
|
||||
end
|
||||
|
||||
def test_update_attributes_should_rollback_on_failure!
|
||||
author = Author.find(1)
|
||||
posts_count = author.posts.size
|
||||
assert posts_count > 0
|
||||
assert_raise(ActiveRecord::RecordInvalid) do
|
||||
author.update_attributes!(:name => nil, :post_ids => [])
|
||||
end
|
||||
assert_equal posts_count, author.posts(true).size
|
||||
end
|
||||
|
||||
def test_successful_with_return
|
||||
class << Topic.connection
|
||||
alias :real_commit_db_transaction :commit_db_transaction
|
||||
def commit_db_transaction
|
||||
$committed = true
|
||||
real_commit_db_transaction
|
||||
end
|
||||
end
|
||||
|
||||
$committed = false
|
||||
transaction_with_return
|
||||
assert $committed
|
||||
|
||||
assert Topic.find(1).approved?, "First should have been approved"
|
||||
assert !Topic.find(2).approved?, "Second should have been unapproved"
|
||||
ensure
|
||||
class << Topic.connection
|
||||
alias :commit_db_transaction :real_commit_db_transaction rescue nil
|
||||
end
|
||||
end
|
||||
|
||||
def test_successful_with_instance_method
|
||||
@first.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
end
|
||||
|
||||
assert Topic.find(1).approved?, "First should have been approved"
|
||||
assert !Topic.find(2).approved?, "Second should have been unapproved"
|
||||
end
|
||||
|
||||
def test_failing_on_exception
|
||||
begin
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
raise "Bad things!"
|
||||
end
|
||||
rescue
|
||||
# caught it
|
||||
end
|
||||
|
||||
assert @first.approved?, "First should still be changed in the objects"
|
||||
assert !@second.approved?, "Second should still be changed in the objects"
|
||||
|
||||
assert !Topic.find(1).approved?, "First shouldn't have been approved"
|
||||
assert Topic.find(2).approved?, "Second should still be approved"
|
||||
end
|
||||
|
||||
def test_raising_exception_in_callback_rollbacks_in_save
|
||||
add_exception_raising_after_save_callback_to_topic
|
||||
|
||||
begin
|
||||
@first.approved = true
|
||||
@first.save
|
||||
flunk
|
||||
rescue => e
|
||||
assert_equal "Make the transaction rollback", e.message
|
||||
assert !Topic.find(1).approved?
|
||||
ensure
|
||||
remove_exception_raising_after_save_callback_to_topic
|
||||
end
|
||||
end
|
||||
|
||||
def test_cancellation_from_before_destroy_rollbacks_in_destroy
|
||||
add_cancelling_before_destroy_with_db_side_effect_to_topic
|
||||
begin
|
||||
nbooks_before_destroy = Book.count
|
||||
status = @first.destroy
|
||||
assert !status
|
||||
assert_nothing_raised(ActiveRecord::RecordNotFound) { @first.reload }
|
||||
assert_equal nbooks_before_destroy, Book.count
|
||||
ensure
|
||||
remove_cancelling_before_destroy_with_db_side_effect_to_topic
|
||||
end
|
||||
end
|
||||
|
||||
def test_cancellation_from_before_filters_rollbacks_in_save
|
||||
%w(validation save).each do |filter|
|
||||
send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
|
||||
begin
|
||||
nbooks_before_save = Book.count
|
||||
original_author_name = @first.author_name
|
||||
@first.author_name += '_this_should_not_end_up_in_the_db'
|
||||
status = @first.save
|
||||
assert !status
|
||||
assert_equal original_author_name, @first.reload.author_name
|
||||
assert_equal nbooks_before_save, Book.count
|
||||
ensure
|
||||
send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_cancellation_from_before_filters_rollbacks_in_save!
|
||||
%w(validation save).each do |filter|
|
||||
send("add_cancelling_before_#{filter}_with_db_side_effect_to_topic")
|
||||
begin
|
||||
nbooks_before_save = Book.count
|
||||
original_author_name = @first.author_name
|
||||
@first.author_name += '_this_should_not_end_up_in_the_db'
|
||||
@first.save!
|
||||
flunk
|
||||
rescue => e
|
||||
assert_equal original_author_name, @first.reload.author_name
|
||||
assert_equal nbooks_before_save, Book.count
|
||||
ensure
|
||||
send("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_callback_rollback_in_create
|
||||
new_topic = Topic.new(
|
||||
:title => "A new topic",
|
||||
:author_name => "Ben",
|
||||
:author_email_address => "ben@example.com",
|
||||
:written_on => "2003-07-16t15:28:11.2233+01:00",
|
||||
:last_read => "2004-04-15",
|
||||
:bonus_time => "2005-01-30t15:28:00.00+01:00",
|
||||
:content => "Have a nice day",
|
||||
:approved => false)
|
||||
new_record_snapshot = new_topic.new_record?
|
||||
id_present = new_topic.has_attribute?(Topic.primary_key)
|
||||
id_snapshot = new_topic.id
|
||||
|
||||
# Make sure the second save gets the after_create callback called.
|
||||
2.times do
|
||||
begin
|
||||
add_exception_raising_after_create_callback_to_topic
|
||||
new_topic.approved = true
|
||||
new_topic.save
|
||||
flunk
|
||||
rescue => e
|
||||
assert_equal "Make the transaction rollback", e.message
|
||||
assert_equal new_record_snapshot, new_topic.new_record?, "The topic should have its old new_record value"
|
||||
assert_equal id_snapshot, new_topic.id, "The topic should have its old id"
|
||||
assert_equal id_present, new_topic.has_attribute?(Topic.primary_key)
|
||||
ensure
|
||||
remove_exception_raising_after_create_callback_to_topic
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_explicit_transactions
|
||||
Topic.transaction do
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
end
|
||||
end
|
||||
|
||||
assert Topic.find(1).approved?, "First should have been approved"
|
||||
assert !Topic.find(2).approved?, "Second should have been unapproved"
|
||||
end
|
||||
|
||||
def test_manually_rolling_back_a_transaction
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save
|
||||
@second.save
|
||||
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
|
||||
assert @first.approved?, "First should still be changed in the objects"
|
||||
assert !@second.approved?, "Second should still be changed in the objects"
|
||||
|
||||
assert !Topic.find(1).approved?, "First shouldn't have been approved"
|
||||
assert Topic.find(2).approved?, "Second should still be approved"
|
||||
end
|
||||
|
||||
def test_invalid_keys_for_transaction
|
||||
assert_raise ArgumentError do
|
||||
Topic.transaction :nested => true do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_force_savepoint_in_nested_transaction
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save!
|
||||
@second.save!
|
||||
|
||||
begin
|
||||
Topic.transaction :requires_new => true do
|
||||
@first.happy = false
|
||||
@first.save!
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
assert @first.reload.approved?
|
||||
assert !@second.reload.approved?
|
||||
end if Topic.connection.supports_savepoints?
|
||||
|
||||
def test_no_savepoint_in_nested_transaction_without_force
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@second.approved = false
|
||||
@first.save!
|
||||
@second.save!
|
||||
|
||||
begin
|
||||
Topic.transaction do
|
||||
@first.approved = false
|
||||
@first.save!
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
assert !@first.reload.approved?
|
||||
assert !@second.reload.approved?
|
||||
end if Topic.connection.supports_savepoints?
|
||||
|
||||
def test_many_savepoints
|
||||
Topic.transaction do
|
||||
@first.content = "One"
|
||||
@first.save!
|
||||
|
||||
begin
|
||||
Topic.transaction :requires_new => true do
|
||||
@first.content = "Two"
|
||||
@first.save!
|
||||
|
||||
begin
|
||||
Topic.transaction :requires_new => true do
|
||||
@first.content = "Three"
|
||||
@first.save!
|
||||
|
||||
begin
|
||||
Topic.transaction :requires_new => true do
|
||||
@first.content = "Four"
|
||||
@first.save!
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
|
||||
@three = @first.reload.content
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
|
||||
@two = @first.reload.content
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
|
||||
@one = @first.reload.content
|
||||
end
|
||||
|
||||
assert_equal "One", @one
|
||||
assert_equal "Two", @two
|
||||
assert_equal "Three", @three
|
||||
end if Topic.connection.supports_savepoints?
|
||||
|
||||
def test_rollback_when_commit_raises
|
||||
Topic.connection.expects(:begin_db_transaction)
|
||||
Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
|
||||
Topic.connection.expects(:outside_transaction?).returns(false)
|
||||
Topic.connection.expects(:rollback_db_transaction)
|
||||
|
||||
assert_raise RuntimeError do
|
||||
Topic.transaction do
|
||||
# do nothing
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter) && defined?(PGconn::PQTRANS_IDLE)
|
||||
def test_outside_transaction_works
|
||||
assert Topic.connection.outside_transaction?
|
||||
Topic.connection.begin_db_transaction
|
||||
assert !Topic.connection.outside_transaction?
|
||||
Topic.connection.rollback_db_transaction
|
||||
assert Topic.connection.outside_transaction?
|
||||
end
|
||||
|
||||
def test_rollback_wont_be_executed_if_no_transaction_active
|
||||
assert_raise RuntimeError do
|
||||
Topic.transaction do
|
||||
Topic.connection.rollback_db_transaction
|
||||
Topic.connection.expects(:rollback_db_transaction).never
|
||||
raise "Rails doesn't scale!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_open_transactions_count_is_reset_to_zero_if_no_transaction_active
|
||||
Topic.transaction do
|
||||
Topic.transaction do
|
||||
Topic.connection.rollback_db_transaction
|
||||
end
|
||||
assert_equal 0, Topic.connection.open_transactions
|
||||
end
|
||||
assert_equal 0, Topic.connection.open_transactions
|
||||
end
|
||||
end
|
||||
|
||||
def test_sqlite_add_column_in_transaction
|
||||
return true unless current_adapter?(:SQLite3Adapter, :SQLiteAdapter)
|
||||
|
||||
# Test first if column creation/deletion works correctly when no
|
||||
# transaction is in place.
|
||||
#
|
||||
# We go back to the connection for the column queries because
|
||||
# Topic.columns is cached and won't report changes to the DB
|
||||
|
||||
assert_nothing_raised do
|
||||
Topic.reset_column_information
|
||||
Topic.connection.add_column('topics', 'stuff', :string)
|
||||
assert Topic.column_names.include?('stuff')
|
||||
|
||||
Topic.reset_column_information
|
||||
Topic.connection.remove_column('topics', 'stuff')
|
||||
assert !Topic.column_names.include?('stuff')
|
||||
end
|
||||
|
||||
if Topic.connection.supports_ddl_transactions?
|
||||
assert_nothing_raised do
|
||||
Topic.transaction { Topic.connection.add_column('topics', 'stuff', :string) }
|
||||
end
|
||||
else
|
||||
Topic.transaction do
|
||||
assert_raise(ActiveRecord::StatementInvalid) { Topic.connection.add_column('topics', 'stuff', :string) }
|
||||
raise ActiveRecord::Rollback
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
def add_exception_raising_after_save_callback_to_topic
|
||||
Topic.class_eval { def after_save() raise "Make the transaction rollback" end }
|
||||
end
|
||||
|
||||
def remove_exception_raising_after_save_callback_to_topic
|
||||
Topic.class_eval { remove_method :after_save }
|
||||
end
|
||||
|
||||
def add_exception_raising_after_create_callback_to_topic
|
||||
Topic.class_eval { def after_create() raise "Make the transaction rollback" end }
|
||||
end
|
||||
|
||||
def remove_exception_raising_after_create_callback_to_topic
|
||||
Topic.class_eval { remove_method :after_create }
|
||||
end
|
||||
|
||||
%w(validation save destroy).each do |filter|
|
||||
define_method("add_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
|
||||
Topic.class_eval "def before_#{filter}() Book.create; false end"
|
||||
end
|
||||
|
||||
define_method("remove_cancelling_before_#{filter}_with_db_side_effect_to_topic") do
|
||||
Topic.class_eval "remove_method :before_#{filter}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class TransactionsWithTransactionalFixturesTest < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = true
|
||||
fixtures :topics
|
||||
|
||||
def test_automatic_savepoint_in_outer_transaction
|
||||
@first = Topic.find(1)
|
||||
|
||||
begin
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@first.save!
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
assert !@first.reload.approved?
|
||||
end
|
||||
end
|
||||
|
||||
def test_no_automatic_savepoint_for_inner_transaction
|
||||
@first = Topic.find(1)
|
||||
|
||||
Topic.transaction do
|
||||
@first.approved = true
|
||||
@first.save!
|
||||
|
||||
begin
|
||||
Topic.transaction do
|
||||
@first.approved = false
|
||||
@first.save!
|
||||
raise
|
||||
end
|
||||
rescue
|
||||
end
|
||||
end
|
||||
|
||||
assert !@first.reload.approved?
|
||||
end
|
||||
end if Topic.connection.supports_savepoints?
|
||||
|
||||
if current_adapter?(:PostgreSQLAdapter)
|
||||
class ConcurrentTransactionTest < TransactionTest
|
||||
use_concurrent_connections
|
||||
|
||||
# This will cause transactions to overlap and fail unless they are performed on
|
||||
# separate database connections.
|
||||
def test_transaction_per_thread
|
||||
assert_nothing_raised do
|
||||
threads = (1..3).map do
|
||||
Thread.new do
|
||||
Topic.transaction do
|
||||
topic = Topic.find(1)
|
||||
topic.approved = !topic.approved?
|
||||
topic.save!
|
||||
topic.approved = !topic.approved?
|
||||
topic.save!
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
threads.each { |t| t.join }
|
||||
end
|
||||
end
|
||||
|
||||
# Test for dirty reads among simultaneous transactions.
|
||||
def test_transaction_isolation__read_committed
|
||||
# Should be invariant.
|
||||
original_salary = Developer.find(1).salary
|
||||
temporary_salary = 200000
|
||||
|
||||
assert_nothing_raised do
|
||||
threads = (1..3).map do
|
||||
Thread.new do
|
||||
Developer.transaction do
|
||||
# Expect original salary.
|
||||
dev = Developer.find(1)
|
||||
assert_equal original_salary, dev.salary
|
||||
|
||||
dev.salary = temporary_salary
|
||||
dev.save!
|
||||
|
||||
# Expect temporary salary.
|
||||
dev = Developer.find(1)
|
||||
assert_equal temporary_salary, dev.salary
|
||||
|
||||
dev.salary = original_salary
|
||||
dev.save!
|
||||
|
||||
# Expect original salary.
|
||||
dev = Developer.find(1)
|
||||
assert_equal original_salary, dev.salary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Keep our eyes peeled.
|
||||
threads << Thread.new do
|
||||
10.times do
|
||||
sleep 0.05
|
||||
Developer.transaction do
|
||||
# Always expect original salary.
|
||||
assert_equal original_salary, Developer.find(1).salary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
threads.each { |t| t.join }
|
||||
end
|
||||
|
||||
assert_equal original_salary, Developer.find(1).salary
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,32 +0,0 @@
|
|||
require "cases/helper"
|
||||
|
||||
class TestRecord < ActiveRecord::Base
|
||||
end
|
||||
|
||||
class TestUnconnectedAdapter < ActiveRecord::TestCase
|
||||
self.use_transactional_fixtures = false
|
||||
|
||||
def setup
|
||||
@underlying = ActiveRecord::Base.connection
|
||||
@specification = ActiveRecord::Base.remove_connection
|
||||
end
|
||||
|
||||
def teardown
|
||||
@underlying = nil
|
||||
ActiveRecord::Base.establish_connection(@specification)
|
||||
end
|
||||
|
||||
def test_connection_no_longer_established
|
||||
assert_raise(ActiveRecord::ConnectionNotEstablished) do
|
||||
TestRecord.find(1)
|
||||
end
|
||||
|
||||
assert_raise(ActiveRecord::ConnectionNotEstablished) do
|
||||
TestRecord.new.save
|
||||
end
|
||||
end
|
||||
|
||||
def test_underlying_adapter_no_longer_active
|
||||
assert !@underlying.active?, "Removed adapter should no longer be active"
|
||||
end
|
||||
end
|
||||
|
|
@ -1,925 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
require 'models/reply'
|
||||
require 'models/person'
|
||||
|
||||
module ActiveRecordValidationsI18nTestHelper
|
||||
def store_translations(*args)
|
||||
data = args.extract_options!
|
||||
locale = args.shift || 'en'
|
||||
I18n.backend.send(:init_translations)
|
||||
I18n.backend.store_translations(locale, :activerecord => data)
|
||||
end
|
||||
|
||||
def delete_translation(key)
|
||||
I18n.backend.instance_eval do
|
||||
keys = I18n.send(:normalize_translation_keys, 'en', key, nil)
|
||||
keys.inject(translations) { |result, k| keys.last == k ? result.delete(k.to_sym) : result[k.to_sym] }
|
||||
end
|
||||
end
|
||||
|
||||
def reset_callbacks(*models)
|
||||
models.each do |model|
|
||||
model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ACTIVERECORD VALIDATIONS
|
||||
#
|
||||
# For each validation:
|
||||
#
|
||||
# * test expect that it adds an error with the appropriate arguments
|
||||
# * test that it looks up the correct default message
|
||||
|
||||
class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase
|
||||
include ActiveRecordValidationsI18nTestHelper
|
||||
|
||||
def setup
|
||||
reset_callbacks(Topic)
|
||||
@topic = Topic.new
|
||||
@reply = Reply.new
|
||||
@old_load_path, @old_backend = I18n.load_path, I18n.backend
|
||||
I18n.load_path.clear
|
||||
I18n.backend = I18n::Backend::Simple.new
|
||||
I18n.backend.store_translations('en', :activerecord => {:errors => {:messages => {:custom => nil}}})
|
||||
end
|
||||
|
||||
def teardown
|
||||
reset_callbacks(Topic)
|
||||
I18n.load_path.replace(@old_load_path)
|
||||
I18n.backend = @old_backend
|
||||
end
|
||||
|
||||
def expect_error_added(model, attribute, type, options)
|
||||
model.errors.expects(:add).with(attribute, type, options)
|
||||
yield
|
||||
model.valid?
|
||||
end
|
||||
|
||||
def assert_message_translations(model, attribute, type, &block)
|
||||
assert_default_message_translation(model, attribute, type, &block)
|
||||
reset_callbacks(model.class)
|
||||
model.errors.clear
|
||||
assert_custom_message_translation(model, attribute, type, &block)
|
||||
end
|
||||
|
||||
def assert_custom_message_translation(model, attribute, type)
|
||||
store_translations(:errors => { :models => { model.class.name.underscore => { :attributes => { attribute => { type => 'custom message' } } } } })
|
||||
yield
|
||||
model.valid?
|
||||
assert_equal 'custom message', model.errors.on(attribute)
|
||||
end
|
||||
|
||||
def assert_default_message_translation(model, attribute, type)
|
||||
store_translations(:errors => { :messages => { type => 'default message' } })
|
||||
yield
|
||||
model.valid?
|
||||
assert_equal 'default message', model.errors.on(attribute)
|
||||
end
|
||||
|
||||
def unique_topic
|
||||
@unique ||= Topic.create(:title => 'unique!')
|
||||
end
|
||||
|
||||
def replied_topic
|
||||
@replied_topic ||= begin
|
||||
topic = Topic.create(:title => "topic")
|
||||
topic.replies << Reply.new
|
||||
topic
|
||||
end
|
||||
end
|
||||
|
||||
# validates_confirmation_of
|
||||
|
||||
test "#validates_confirmation_of given no custom message" do
|
||||
expect_error_added(@topic, :title, :confirmation, :default => nil) do
|
||||
Topic.validates_confirmation_of :title
|
||||
@topic.title = 'title'
|
||||
@topic.title_confirmation = 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_confirmation_of given a custom message" do
|
||||
expect_error_added(@topic, :title, :confirmation, :default => 'custom') do
|
||||
Topic.validates_confirmation_of :title, :message => 'custom'
|
||||
@topic.title_confirmation = 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_confirmation_of finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :confirmation) do
|
||||
Topic.validates_confirmation_of :title
|
||||
@topic.title_confirmation = 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_acceptance_of
|
||||
|
||||
test "#validates_acceptance_of given no custom message" do
|
||||
expect_error_added(@topic, :title, :accepted, :default => nil) do
|
||||
Topic.validates_acceptance_of :title, :allow_nil => false
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_acceptance_of given a custom message" do
|
||||
expect_error_added(@topic, :title, :accepted, :default => 'custom') do
|
||||
Topic.validates_acceptance_of :title, :message => 'custom', :allow_nil => false
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_acceptance_of finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :accepted) do
|
||||
Topic.validates_acceptance_of :title, :allow_nil => false
|
||||
end
|
||||
end
|
||||
|
||||
# validates_presence_of
|
||||
|
||||
test "#validates_presence_of given no custom message" do
|
||||
expect_error_added(@topic, :title, :blank, :default => nil) do
|
||||
Topic.validates_presence_of :title
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_presence_of given a custom message" do
|
||||
expect_error_added(@topic, :title, :blank, :default => 'custom') do
|
||||
Topic.validates_presence_of :title, :message => 'custom'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_presence_of finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :blank) do
|
||||
Topic.validates_presence_of :title
|
||||
end
|
||||
end
|
||||
|
||||
# validates_length_of :too_short
|
||||
|
||||
test "#validates_length_of (:too_short) and no custom message" do
|
||||
expect_error_added(@topic, :title, :too_short, :default => nil, :count => 3) do
|
||||
Topic.validates_length_of :title, :within => 3..5
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:too_short) and a custom message" do
|
||||
expect_error_added(@topic, :title, :too_short, :default => 'custom', :count => 3) do
|
||||
Topic.validates_length_of :title, :within => 3..5, :too_short => 'custom'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:too_short) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :too_short) do
|
||||
Topic.validates_length_of :title, :within => 3..5
|
||||
end
|
||||
end
|
||||
|
||||
# validates_length_of :too_long
|
||||
|
||||
test "#validates_length_of (:too_long) and no custom message" do
|
||||
expect_error_added(@topic, :title, :too_long, :default => nil, :count => 5) do
|
||||
Topic.validates_length_of :title, :within => 3..5
|
||||
@topic.title = 'this title is too long'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:too_long) and a custom message" do
|
||||
expect_error_added(@topic, :title, :too_long, :default => 'custom', :count => 5) do
|
||||
Topic.validates_length_of :title, :within => 3..5, :too_long => 'custom'
|
||||
@topic.title = 'this title is too long'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:too_long) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :too_long) do
|
||||
Topic.validates_length_of :title, :within => 3..5
|
||||
@topic.title = 'this title is too long'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_length_of :is
|
||||
|
||||
test "#validates_length_of (:is) and no custom message" do
|
||||
expect_error_added(@topic, :title, :wrong_length, :default => nil, :count => 5) do
|
||||
Topic.validates_length_of :title, :is => 5
|
||||
@topic.title = 'this title has the wrong length'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:is) and a custom message" do
|
||||
expect_error_added(@topic, :title, :wrong_length, :default => 'custom', :count => 5) do
|
||||
Topic.validates_length_of :title, :is => 5, :wrong_length => 'custom'
|
||||
@topic.title = 'this title has the wrong length'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_length_of (:is) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :wrong_length) do
|
||||
Topic.validates_length_of :title, :is => 5
|
||||
@topic.title = 'this title has the wrong length'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_uniqueness_of
|
||||
|
||||
test "#validates_uniqueness_of and no custom message" do
|
||||
expect_error_added(@topic, :title, :taken, :default => nil, :value => 'unique!') do
|
||||
Topic.validates_uniqueness_of :title
|
||||
@topic.title = unique_topic.title
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_uniqueness_of and a custom message" do
|
||||
expect_error_added(@topic, :title, :taken, :default => 'custom', :value => 'unique!') do
|
||||
Topic.validates_uniqueness_of :title, :message => 'custom'
|
||||
@topic.title = unique_topic.title
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_uniqueness_of finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :taken) do
|
||||
Topic.validates_uniqueness_of :title
|
||||
@topic.title = unique_topic.title
|
||||
end
|
||||
end
|
||||
|
||||
# validates_format_of
|
||||
|
||||
test "#validates_format_of and no custom message" do
|
||||
expect_error_added(@topic, :title, :invalid, :default => nil, :value => '72x') do
|
||||
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
|
||||
@topic.title = '72x'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_format_of and a custom message" do
|
||||
expect_error_added(@topic, :title, :invalid, :default => 'custom', :value => '72x') do
|
||||
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/, :message => 'custom'
|
||||
@topic.title = '72x'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_format_of finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :invalid) do
|
||||
Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/
|
||||
@topic.title = '72x'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_inclusion_of
|
||||
|
||||
test "#validates_inclusion_of and no custom message" do
|
||||
list = %w(a b c)
|
||||
expect_error_added(@topic, :title, :inclusion, :default => nil, :value => 'z') do
|
||||
Topic.validates_inclusion_of :title, :in => list
|
||||
@topic.title = 'z'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_inclusion_of and a custom message" do
|
||||
list = %w(a b c)
|
||||
expect_error_added(@topic, :title, :inclusion, :default => 'custom', :value => 'z') do
|
||||
Topic.validates_inclusion_of :title, :in => list, :message => 'custom'
|
||||
@topic.title = 'z'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_inclusion_of finds the correct message translations" do
|
||||
list = %w(a b c)
|
||||
assert_message_translations(@topic, :title, :inclusion) do
|
||||
Topic.validates_inclusion_of :title, :in => list
|
||||
@topic.title = 'z'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_exclusion_of
|
||||
|
||||
test "#validates_exclusion_of and no custom message" do
|
||||
list = %w(a b c)
|
||||
expect_error_added(@topic, :title, :exclusion, :default => nil, :value => 'a') do
|
||||
Topic.validates_exclusion_of :title, :in => list
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_exclusion_of and a custom message" do
|
||||
list = %w(a b c)
|
||||
expect_error_added(@topic, :title, :exclusion, :default => 'custom', :value => 'a') do
|
||||
Topic.validates_exclusion_of :title, :in => list, :message => 'custom'
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_exclusion_of finds the correct message translations" do
|
||||
list = %w(a b c)
|
||||
assert_message_translations(@topic, :title, :exclusion) do
|
||||
Topic.validates_exclusion_of :title, :in => list
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_numericality_of :not_a_number, without :only_integer
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, w/o :only_integer) no custom message" do
|
||||
expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do
|
||||
Topic.validates_numericality_of :title
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, w/o :only_integer) and a custom message" do
|
||||
expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do
|
||||
Topic.validates_numericality_of :title, :message => 'custom'
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, w/o :only_integer) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :not_a_number) do
|
||||
Topic.validates_numericality_of :title
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_numericality_of :not_a_number, with :only_integer
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, with :only_integer) no custom message" do
|
||||
expect_error_added(@topic, :title, :not_a_number, :default => nil, :value => 'a') do
|
||||
Topic.validates_numericality_of :title, :only_integer => true
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, with :only_integer) and a custom message" do
|
||||
expect_error_added(@topic, :title, :not_a_number, :default => 'custom', :value => 'a') do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :message => 'custom'
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:not_a_number, with :only_integer) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :not_a_number) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true
|
||||
@topic.title = 'a'
|
||||
end
|
||||
end
|
||||
|
||||
# validates_numericality_of :odd
|
||||
|
||||
test "#validates_numericality_of (:odd) no custom message" do
|
||||
expect_error_added(@topic, :title, :odd, :default => nil, :value => 0) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :odd => true
|
||||
@topic.title = 0
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:odd) and a custom message" do
|
||||
expect_error_added(@topic, :title, :odd, :default => 'custom', :value => 0) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :odd => true, :message => 'custom'
|
||||
@topic.title = 0
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:odd) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :odd) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :odd => true
|
||||
@topic.title = 0
|
||||
end
|
||||
end
|
||||
|
||||
# validates_numericality_of :even
|
||||
|
||||
test "#validates_numericality_of (:even) no custom message" do
|
||||
expect_error_added(@topic, :title, :even, :default => nil, :value => 1) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :even => true
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:even) and a custom message" do
|
||||
expect_error_added(@topic, :title, :even, :default => 'custom', :value => 1) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :even => true, :message => 'custom'
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:even) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :even) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :even => true
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
# validates_numericality_of :less_than
|
||||
|
||||
test "#validates_numericality_of (:less_than) no custom message" do
|
||||
expect_error_added(@topic, :title, :less_than, :default => nil, :value => 1, :count => 0) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:less_than) and a custom message" do
|
||||
expect_error_added(@topic, :title, :less_than, :default => 'custom', :value => 1, :count => 0) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom'
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_numericality_of (:less_than) finds the correct message translations" do
|
||||
assert_message_translations(@topic, :title, :less_than) do
|
||||
Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0
|
||||
@topic.title = 1
|
||||
end
|
||||
end
|
||||
|
||||
# validates_associated
|
||||
|
||||
test "#validates_associated no custom message" do
|
||||
expect_error_added(replied_topic, :replies, :invalid, :default => nil, :value => replied_topic.replies) do
|
||||
Topic.validates_associated :replies
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_associated and a custom message" do
|
||||
expect_error_added(replied_topic, :replies, :invalid, :default => 'custom', :value => replied_topic.replies) do
|
||||
Topic.validates_associated :replies, :message => 'custom'
|
||||
end
|
||||
end
|
||||
|
||||
test "#validates_associated finds the correct message translations" do
|
||||
assert_message_translations(replied_topic, :replies, :invalid) do
|
||||
Topic.validates_associated :replies
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ACTIVERECORD ERROR
|
||||
#
|
||||
# * test that it passes given interpolation arguments, the human model name and human attribute name
|
||||
# * test that it looks messages up with the the correct keys
|
||||
# * test that it looks up the correct default messages
|
||||
|
||||
class ActiveRecordErrorI18nTests < ActiveSupport::TestCase
|
||||
include ActiveRecordValidationsI18nTestHelper
|
||||
|
||||
def setup
|
||||
@reply = Reply.new
|
||||
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
I18n.backend = @old_backend
|
||||
I18n.locale = nil
|
||||
end
|
||||
|
||||
def assert_error_message(message, *args)
|
||||
assert_equal message, ActiveRecord::Error.new(@reply, *args).message
|
||||
end
|
||||
|
||||
def assert_full_message(message, *args)
|
||||
assert_equal message, ActiveRecord::Error.new(@reply, *args).full_message
|
||||
end
|
||||
|
||||
test ":default is only given to message if a symbol is supplied" do
|
||||
store_translations(:errors => { :messages => { :"foo bar" => "You fooed: %{value}." } })
|
||||
@reply.errors.add(:title, :inexistent, :default => "foo bar")
|
||||
assert_equal "foo bar", @reply.errors[:title]
|
||||
end
|
||||
|
||||
test "#generate_message passes the model attribute value for interpolation" do
|
||||
store_translations(:errors => { :messages => { :foo => "You fooed: %{value}." } })
|
||||
@reply.title = "da title"
|
||||
assert_error_message 'You fooed: da title.', :title, :foo
|
||||
end
|
||||
|
||||
test "#generate_message passes the human_name of the model for interpolation" do
|
||||
store_translations(
|
||||
:errors => { :messages => { :foo => "You fooed: %{model}." } },
|
||||
:models => { :topic => 'da topic' }
|
||||
)
|
||||
assert_error_message 'You fooed: da topic.', :title, :foo
|
||||
end
|
||||
|
||||
test "#generate_message passes the human_name of the attribute for interpolation" do
|
||||
store_translations(
|
||||
:errors => { :messages => { :foo => "You fooed: %{attribute}." } },
|
||||
:attributes => { :topic => { :title => 'da topic title' } }
|
||||
)
|
||||
assert_error_message 'You fooed: da topic title.', :title, :foo
|
||||
end
|
||||
|
||||
# generate_message will look up the key for the error message (e.g. :blank) in these namespaces:
|
||||
#
|
||||
# activerecord.errors.models.reply.attributes.title
|
||||
# activerecord.errors.models.reply
|
||||
# activerecord.errors.models.topic.attributes.title
|
||||
# activerecord.errors.models.topic
|
||||
# [default from class level :validates_foo statement if this is a String]
|
||||
# activerecord.errors.messages
|
||||
|
||||
test "#generate_message key fallbacks (given a String as key)" do
|
||||
store_translations(
|
||||
:errors => {
|
||||
:models => {
|
||||
:reply => {
|
||||
:attributes => { :title => { :custom => 'activerecord.errors.models.reply.attributes.title.custom' } },
|
||||
:custom => 'activerecord.errors.models.reply.custom'
|
||||
},
|
||||
:topic => {
|
||||
:attributes => { :title => { :custom => 'activerecord.errors.models.topic.attributes.title.custom' } },
|
||||
:custom => 'activerecord.errors.models.topic.custom'
|
||||
}
|
||||
},
|
||||
:messages => {
|
||||
:custom => 'activerecord.errors.messages.custom',
|
||||
:kaputt => 'activerecord.errors.messages.kaputt'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
assert_error_message 'activerecord.errors.models.reply.attributes.title.custom', :title, :kaputt, :message => 'custom'
|
||||
delete_translation :'activerecord.errors.models.reply.attributes.title.custom'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.reply.custom', :title, :kaputt, :message => 'custom'
|
||||
delete_translation :'activerecord.errors.models.reply.custom'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.topic.attributes.title.custom', :title, :kaputt, :message => 'custom'
|
||||
delete_translation :'activerecord.errors.models.topic.attributes.title.custom'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.topic.custom', :title, :kaputt, :message => 'custom'
|
||||
delete_translation :'activerecord.errors.models.topic.custom'
|
||||
|
||||
assert_error_message 'activerecord.errors.messages.custom', :title, :kaputt, :message => 'custom'
|
||||
delete_translation :'activerecord.errors.messages.custom'
|
||||
|
||||
# Implementing this would clash with the AR default behaviour of using validates_foo :message => 'foo'
|
||||
# as an untranslated string. I.e. at this point we can either fall back to the given string from the
|
||||
# class-level macro (validates_*) or fall back to the default message for this validation type.
|
||||
# assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt, :message => 'custom'
|
||||
|
||||
assert_error_message 'custom', :title, :kaputt, :message => 'custom'
|
||||
end
|
||||
|
||||
test "#generate_message key fallbacks (given a Symbol as key)" do
|
||||
store_translations(
|
||||
:errors => {
|
||||
:models => {
|
||||
:reply => {
|
||||
:attributes => { :title => { :kaputt => 'activerecord.errors.models.reply.attributes.title.kaputt' } },
|
||||
:kaputt => 'activerecord.errors.models.reply.kaputt'
|
||||
},
|
||||
:topic => {
|
||||
:attributes => { :title => { :kaputt => 'activerecord.errors.models.topic.attributes.title.kaputt' } },
|
||||
:kaputt => 'activerecord.errors.models.topic.kaputt'
|
||||
}
|
||||
},
|
||||
:messages => {
|
||||
:kaputt => 'activerecord.errors.messages.kaputt'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
assert_error_message 'activerecord.errors.models.reply.attributes.title.kaputt', :title, :kaputt
|
||||
delete_translation :'activerecord.errors.models.reply.attributes.title.kaputt'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.reply.kaputt', :title, :kaputt
|
||||
delete_translation :'activerecord.errors.models.reply.kaputt'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.topic.attributes.title.kaputt', :title, :kaputt
|
||||
delete_translation :'activerecord.errors.models.topic.attributes.title.kaputt'
|
||||
|
||||
assert_error_message 'activerecord.errors.models.topic.kaputt', :title, :kaputt
|
||||
delete_translation :'activerecord.errors.models.topic.kaputt'
|
||||
|
||||
assert_error_message 'activerecord.errors.messages.kaputt', :title, :kaputt
|
||||
end
|
||||
|
||||
# full_messages
|
||||
|
||||
test "#full_message with no format present" do
|
||||
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' } })
|
||||
assert_full_message 'Title is kaputt', :title, :kaputt
|
||||
end
|
||||
|
||||
test "#full_message with a format present" do
|
||||
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :format => '%{attribute}: %{message}' } })
|
||||
assert_full_message 'Title: is kaputt', :title, :kaputt
|
||||
end
|
||||
|
||||
test "#full_message with a type specific format present" do
|
||||
store_translations(:errors => { :messages => { :kaputt => 'is kaputt' }, :full_messages => { :kaputt => '%{attribute} %{message}!' } })
|
||||
assert_full_message 'Title is kaputt!', :title, :kaputt
|
||||
end
|
||||
|
||||
test "#full_message with class-level specified custom message" do
|
||||
store_translations(:errors => { :messages => { :broken => 'is kaputt' }, :full_messages => { :broken => '%{attribute} %{message}?!' } })
|
||||
assert_full_message 'Title is kaputt?!', :title, :kaputt, :message => :broken
|
||||
end
|
||||
|
||||
test "#full_message with different scope" do
|
||||
store_translations(:my_errors => { :messages => { :kaputt => 'is kaputt' } })
|
||||
assert_full_message 'Title is kaputt', :title, :kaputt, :scope => [:activerecord, :my_errors]
|
||||
|
||||
store_translations(:my_errors => { :full_messages => { :kaputt => '%{attribute} %{message}!' } })
|
||||
assert_full_message 'Title is kaputt!', :title, :kaputt, :scope => [:activerecord, :my_errors]
|
||||
end
|
||||
|
||||
# switch locales
|
||||
|
||||
test "#message allows to switch locales" do
|
||||
store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } })
|
||||
store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } })
|
||||
|
||||
assert_error_message 'is kaputt', :title, :kaputt
|
||||
I18n.locale = :de
|
||||
assert_error_message 'ist kaputt', :title, :kaputt
|
||||
I18n.locale = :en
|
||||
assert_error_message 'is kaputt', :title, :kaputt
|
||||
end
|
||||
|
||||
test "#full_message allows to switch locales" do
|
||||
store_translations(:en, :errors => { :messages => { :kaputt => 'is kaputt' } }, :attributes => { :topic => { :title => 'The title' } })
|
||||
store_translations(:de, :errors => { :messages => { :kaputt => 'ist kaputt' } }, :attributes => { :topic => { :title => 'Der Titel' } })
|
||||
|
||||
assert_full_message 'The title is kaputt', :title, :kaputt
|
||||
I18n.locale = :de
|
||||
assert_full_message 'Der Titel ist kaputt', :title, :kaputt
|
||||
I18n.locale = :en
|
||||
assert_full_message 'The title is kaputt', :title, :kaputt
|
||||
end
|
||||
end
|
||||
|
||||
# ACTIVERECORD DEFAULT ERROR MESSAGES
|
||||
#
|
||||
# * test that Error generates the default error messages
|
||||
|
||||
class ActiveRecordDefaultErrorMessagesI18nTests < ActiveSupport::TestCase
|
||||
def assert_default_error_message(message, *args)
|
||||
assert_equal message, error_message(*args)
|
||||
end
|
||||
|
||||
def error_message(*args)
|
||||
ActiveRecord::Error.new(Topic.new, :title, *args).message
|
||||
end
|
||||
|
||||
# used by: validates_inclusion_of
|
||||
test "default error message: inclusion" do
|
||||
assert_default_error_message 'is not included in the list', :inclusion, :value => 'title'
|
||||
end
|
||||
|
||||
# used by: validates_exclusion_of
|
||||
test "default error message: exclusion" do
|
||||
assert_default_error_message 'is reserved', :exclusion, :value => 'title'
|
||||
end
|
||||
|
||||
# used by: validates_associated and validates_format_of
|
||||
test "default error message: invalid" do
|
||||
assert_default_error_message 'is invalid', :invalid, :value => 'title'
|
||||
end
|
||||
|
||||
# used by: validates_confirmation_of
|
||||
test "default error message: confirmation" do
|
||||
assert_default_error_message "doesn't match confirmation", :confirmation, :default => nil
|
||||
end
|
||||
|
||||
# used by: validates_acceptance_of
|
||||
test "default error message: accepted" do
|
||||
assert_default_error_message "must be accepted", :accepted
|
||||
end
|
||||
|
||||
# used by: add_on_empty
|
||||
test "default error message: empty" do
|
||||
assert_default_error_message "can't be empty", :empty
|
||||
end
|
||||
|
||||
# used by: add_on_blank
|
||||
test "default error message: blank" do
|
||||
assert_default_error_message "can't be blank", :blank
|
||||
end
|
||||
|
||||
# used by: validates_length_of
|
||||
test "default error message: too_long" do
|
||||
assert_default_error_message "is too long (maximum is 10 characters)", :too_long, :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_length_of
|
||||
test "default error message: too_short" do
|
||||
assert_default_error_message "is too short (minimum is 10 characters)", :too_short, :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_length_of
|
||||
test "default error message: wrong_length" do
|
||||
assert_default_error_message "is the wrong length (should be 10 characters)", :wrong_length, :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_uniqueness_of
|
||||
test "default error message: taken" do
|
||||
assert_default_error_message "has already been taken", :taken, :value => 'title'
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: not_a_number" do
|
||||
assert_default_error_message "is not a number", :not_a_number, :value => 'title'
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: greater_than" do
|
||||
assert_default_error_message "must be greater than 10", :greater_than, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: greater_than_or_equal_to" do
|
||||
assert_default_error_message "must be greater than or equal to 10", :greater_than_or_equal_to, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: equal_to" do
|
||||
assert_default_error_message "must be equal to 10", :equal_to, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: less_than" do
|
||||
assert_default_error_message "must be less than 10", :less_than, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: less_than_or_equal_to" do
|
||||
assert_default_error_message "must be less than or equal to 10", :less_than_or_equal_to, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: odd" do
|
||||
assert_default_error_message "must be odd", :odd, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
# used by: validates_numericality_of
|
||||
test "default error message: even" do
|
||||
assert_default_error_message "must be even", :even, :value => 'title', :count => 10
|
||||
end
|
||||
|
||||
test "custom message string interpolation" do
|
||||
assert_equal 'custom message title', error_message(:invalid, :default => 'custom message %{value}', :value => 'title')
|
||||
end
|
||||
end
|
||||
|
||||
# ACTIVERECORD VALIDATION ERROR MESSAGES - FULL STACK
|
||||
#
|
||||
# * test a few combinations full stack to ensure the tests above are correct
|
||||
|
||||
class I18nPerson < Person
|
||||
end
|
||||
|
||||
class ActiveRecordValidationsI18nFullStackTests < ActiveSupport::TestCase
|
||||
include ActiveRecordValidationsI18nTestHelper
|
||||
|
||||
def setup
|
||||
reset_callbacks(I18nPerson)
|
||||
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
||||
@person = I18nPerson.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
reset_callbacks(I18nPerson)
|
||||
I18n.backend = @old_backend
|
||||
end
|
||||
|
||||
def assert_name_invalid(message)
|
||||
yield
|
||||
@person.valid?
|
||||
assert_equal message, @person.errors.on(:name)
|
||||
end
|
||||
|
||||
# Symbols as class-level validation messages
|
||||
|
||||
test "Symbol as class level validation message translated per attribute (translation on child class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {:broken => "is broken"}}}}}
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
test "Symbol as class level validation message translated per attribute (translation on base class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:person => {:attributes => {:name => {:broken => "is broken"}}}}}
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
test "Symbol as class level validation message translated per model (translation on child class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:i18n_person => {:broken => "is broken"}}}
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
test "Symbol as class level validation message translated per model (translation on base class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:person => {:broken => "is broken"}}}
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
test "Symbol as class level validation message translated as error message" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:messages => {:broken => "is broken"}}
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
# Strings as class-level validation messages
|
||||
|
||||
test "String as class level validation message translated per attribute (translation on child class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:i18n_person => {:attributes => {:name => {"is broken" => "is broken"}}}}}
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken"
|
||||
end
|
||||
end
|
||||
|
||||
test "String as class level validation message translated per attribute (translation on base class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:person => {:attributes => {:name => {"is broken" => "is broken"}}}}}
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken"
|
||||
end
|
||||
end
|
||||
|
||||
test "String as class level validation message translated per model (translation on child class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:i18n_person => {"is broken" => "is broken"}}}
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken"
|
||||
end
|
||||
end
|
||||
|
||||
test "String as class level validation message translated per model (translation on base class)" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:models => {:person => {"is broken" => "is broken"}}}
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken"
|
||||
end
|
||||
end
|
||||
|
||||
test "String as class level validation message translated as error message" do
|
||||
assert_name_invalid("is broken") do
|
||||
store_translations :errors => {:messages => {"is broken" => "is broken"}}
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken"
|
||||
end
|
||||
end
|
||||
|
||||
test "String as class level validation message not translated (uses message as default)" do
|
||||
assert_name_invalid("is broken!") do
|
||||
I18nPerson.validates_presence_of :name, :message => "is broken!"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ActiveRecordValidationsI18nFullMessagesFullStackTests < ActiveSupport::TestCase
|
||||
include ActiveRecordValidationsI18nTestHelper
|
||||
|
||||
def setup
|
||||
reset_callbacks(I18nPerson)
|
||||
@old_backend, I18n.backend = I18n.backend, I18n::Backend::Simple.new
|
||||
@person = I18nPerson.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
reset_callbacks(I18nPerson)
|
||||
I18n.backend = @old_backend
|
||||
end
|
||||
|
||||
def assert_full_message(message)
|
||||
yield
|
||||
@person.valid?
|
||||
assert_equal message, @person.errors.full_messages.join
|
||||
end
|
||||
|
||||
test "full_message format stored per custom error message key" do
|
||||
assert_full_message("Name is broken!") do
|
||||
store_translations :errors => { :messages => { :broken => 'is broken' }, :full_messages => { :broken => '%{attribute} %{message}!' } }
|
||||
I18nPerson.validates_presence_of :name, :message => :broken
|
||||
end
|
||||
end
|
||||
|
||||
test "full_message format stored per error type" do
|
||||
assert_full_message("Name can't be blank!") do
|
||||
store_translations :errors => { :full_messages => { :blank => '%{attribute} %{message}!' } }
|
||||
I18nPerson.validates_presence_of :name
|
||||
end
|
||||
end
|
||||
# ActiveRecord#RecordInvalid exception
|
||||
|
||||
test "full_message format stored as default" do
|
||||
assert_full_message("Name: can't be blank") do
|
||||
store_translations :errors => { :full_messages => { :format => '%{attribute}: %{message}' } }
|
||||
I18nPerson.validates_presence_of :name
|
||||
end
|
||||
end
|
||||
test "RecordInvalid exception can be localized" do
|
||||
topic = Topic.new
|
||||
topic.errors.add(:title, :invalid)
|
||||
topic.errors.add(:title, :blank)
|
||||
assert_equal "Validation failed: Title is invalid, Title can't be blank", ActiveRecord::RecordInvalid.new(topic).message
|
||||
end
|
||||
end
|
||||
1684
vendor/rails/activerecord/test/cases/validations_test.rb
vendored
1684
vendor/rails/activerecord/test/cases/validations_test.rb
vendored
File diff suppressed because it is too large
Load diff
|
|
@ -1,240 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/contact'
|
||||
require 'models/post'
|
||||
require 'models/author'
|
||||
require 'models/tagging'
|
||||
require 'models/comment'
|
||||
require 'models/company_in_module'
|
||||
|
||||
class XmlSerializationTest < ActiveRecord::TestCase
|
||||
def test_should_serialize_default_root
|
||||
@xml = Contact.new.to_xml
|
||||
assert_match %r{^<contact>}, @xml
|
||||
assert_match %r{</contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_default_root_with_namespace
|
||||
@xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
|
||||
assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, @xml
|
||||
assert_match %r{</contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_custom_root
|
||||
@xml = Contact.new.to_xml :root => 'xml_contact'
|
||||
assert_match %r{^<xml-contact>}, @xml
|
||||
assert_match %r{</xml-contact>$}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_undasherized_tags
|
||||
@xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
|
||||
assert_match %r{^<xml_contact>}, @xml
|
||||
assert_match %r{</xml_contact>$}, @xml
|
||||
assert_match %r{<created_at}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_camelized_tags
|
||||
@xml = Contact.new.to_xml :root => 'xml_contact', :camelize => true
|
||||
assert_match %r{^<XmlContact>}, @xml
|
||||
assert_match %r{</XmlContact>$}, @xml
|
||||
assert_match %r{<CreatedAt}, @xml
|
||||
end
|
||||
|
||||
def test_should_allow_skipped_types
|
||||
@xml = Contact.new(:age => 25).to_xml :skip_types => true
|
||||
assert %r{<age>25</age>}.match(@xml)
|
||||
end
|
||||
|
||||
def test_should_include_yielded_additions
|
||||
@xml = Contact.new.to_xml do |xml|
|
||||
xml.creator "David"
|
||||
end
|
||||
assert_match %r{<creator>David</creator>}, @xml
|
||||
end
|
||||
end
|
||||
|
||||
class DefaultXmlSerializationTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@xml = Contact.new(:name => 'aaron stack', :age => 25, :avatar => 'binarydata', :created_at => Time.utc(2006, 8, 1), :awesome => false, :preferences => { :gem => 'ruby' }).to_xml
|
||||
end
|
||||
|
||||
def test_should_serialize_string
|
||||
assert_match %r{<name>aaron stack</name>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_integer
|
||||
assert_match %r{<age type="integer">25</age>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_binary
|
||||
assert_match %r{YmluYXJ5ZGF0YQ==\n</avatar>}, @xml
|
||||
assert_match %r{<avatar(.*)(type="binary")}, @xml
|
||||
assert_match %r{<avatar(.*)(encoding="base64")}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_datetime
|
||||
assert_match %r{<created-at type=\"datetime\">2006-08-01T00:00:00Z</created-at>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_boolean
|
||||
assert_match %r{<awesome type=\"boolean\">false</awesome>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_yaml
|
||||
assert_match %r{<preferences type=\"yaml\">--- \n:gem: ruby\n</preferences>}, @xml
|
||||
end
|
||||
end
|
||||
|
||||
class NilXmlSerializationTest < ActiveRecord::TestCase
|
||||
def setup
|
||||
@xml = Contact.new.to_xml(:root => 'xml_contact')
|
||||
end
|
||||
|
||||
def test_should_serialize_string
|
||||
assert_match %r{<name nil="true"></name>}, @xml
|
||||
end
|
||||
|
||||
def test_should_serialize_integer
|
||||
assert %r{<age (.*)></age>}.match(@xml)
|
||||
attributes = $1
|
||||
assert_match %r{nil="true"}, attributes
|
||||
assert_match %r{type="integer"}, attributes
|
||||
end
|
||||
|
||||
def test_should_serialize_binary
|
||||
assert %r{<avatar (.*)></avatar>}.match(@xml)
|
||||
attributes = $1
|
||||
assert_match %r{type="binary"}, attributes
|
||||
assert_match %r{encoding="base64"}, attributes
|
||||
assert_match %r{nil="true"}, attributes
|
||||
end
|
||||
|
||||
def test_should_serialize_datetime
|
||||
assert %r{<created-at (.*)></created-at>}.match(@xml)
|
||||
attributes = $1
|
||||
assert_match %r{nil="true"}, attributes
|
||||
assert_match %r{type="datetime"}, attributes
|
||||
end
|
||||
|
||||
def test_should_serialize_boolean
|
||||
assert %r{<awesome (.*)></awesome>}.match(@xml)
|
||||
attributes = $1
|
||||
assert_match %r{type="boolean"}, attributes
|
||||
assert_match %r{nil="true"}, attributes
|
||||
end
|
||||
|
||||
def test_should_serialize_yaml
|
||||
assert %r{<preferences(.*)></preferences>}.match(@xml)
|
||||
attributes = $1
|
||||
assert_match %r{type="yaml"}, attributes
|
||||
assert_match %r{nil="true"}, attributes
|
||||
end
|
||||
end
|
||||
|
||||
class DatabaseConnectedXmlModuleSerializationTest < ActiveRecord::TestCase
|
||||
|
||||
fixtures :projects, :developers, :developers_projects
|
||||
|
||||
def test_module
|
||||
project = MyApplication::Business::Project.find :first
|
||||
xml = project.to_xml
|
||||
assert_match %r{<my-application-business-project>}, xml
|
||||
assert_match %r{</my-application-business-project>}, xml
|
||||
end
|
||||
|
||||
def test_module_with_include
|
||||
project = MyApplication::Business::Project.find :first
|
||||
xml = project.to_xml :include => :developers
|
||||
assert_match %r{<developer type="MyApplication::Business::Developer">}, xml
|
||||
assert_match %r{</developer>}, xml
|
||||
end
|
||||
end
|
||||
|
||||
class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
|
||||
fixtures :authors, :posts
|
||||
# to_xml used to mess with the hash the user provided which
|
||||
# caused the builder to be reused. This meant the document kept
|
||||
# getting appended to.
|
||||
def test_passing_hash_shouldnt_reuse_builder
|
||||
options = {:include=>:posts}
|
||||
david = authors(:david)
|
||||
first_xml_size = david.to_xml(options).size
|
||||
second_xml_size = david.to_xml(options).size
|
||||
assert_equal first_xml_size, second_xml_size
|
||||
end
|
||||
|
||||
def test_include_uses_association_name
|
||||
xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0
|
||||
assert_match %r{<hello-posts type="array">}, xml
|
||||
assert_match %r{<hello-post type="Post">}, xml
|
||||
assert_match %r{<hello-post type="StiPost">}, xml
|
||||
end
|
||||
|
||||
def test_included_associations_should_skip_types
|
||||
xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0, :skip_types => true
|
||||
assert_match %r{<hello-posts>}, xml
|
||||
assert_match %r{<hello-post>}, xml
|
||||
assert_match %r{<hello-post>}, xml
|
||||
end
|
||||
|
||||
def test_methods_are_called_on_object
|
||||
xml = authors(:david).to_xml :methods => :label, :indent => 0
|
||||
assert_match %r{<label>.*</label>}, xml
|
||||
end
|
||||
|
||||
def test_should_not_call_methods_on_associations_that_dont_respond
|
||||
xml = authors(:david).to_xml :include=>:hello_posts, :methods => :label, :indent => 2
|
||||
assert !authors(:david).hello_posts.first.respond_to?(:label)
|
||||
assert_match %r{^ <label>.*</label>}, xml
|
||||
assert_no_match %r{^ <label>}, xml
|
||||
end
|
||||
|
||||
def test_procs_are_called_on_object
|
||||
proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
|
||||
xml = authors(:david).to_xml(:procs => [ proc ])
|
||||
assert_match %r{<nationality>Danish</nationality>}, xml
|
||||
end
|
||||
|
||||
def test_top_level_procs_arent_applied_to_associations
|
||||
author_proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
|
||||
xml = authors(:david).to_xml(:procs => [ author_proc ], :include => :posts, :indent => 2)
|
||||
|
||||
assert_match %r{^ <nationality>Danish</nationality>}, xml
|
||||
assert_no_match %r{^ {6}<nationality>Danish</nationality>}, xml
|
||||
end
|
||||
|
||||
def test_procs_on_included_associations_are_called
|
||||
posts_proc = Proc.new { |options| options[:builder].tag!('copyright', 'DHH') }
|
||||
xml = authors(:david).to_xml(
|
||||
:indent => 2,
|
||||
:include => {
|
||||
:posts => { :procs => [ posts_proc ] }
|
||||
}
|
||||
)
|
||||
|
||||
assert_no_match %r{^ <copyright>DHH</copyright>}, xml
|
||||
assert_match %r{^ {6}<copyright>DHH</copyright>}, xml
|
||||
end
|
||||
|
||||
def test_should_include_empty_has_many_as_empty_array
|
||||
authors(:david).posts.delete_all
|
||||
xml = authors(:david).to_xml :include=>:posts, :indent => 2
|
||||
|
||||
assert_equal [], Hash.from_xml(xml)['author']['posts']
|
||||
assert_match %r{^ <posts type="array"/>}, xml
|
||||
end
|
||||
|
||||
def test_should_has_many_array_elements_should_include_type_when_different_from_guessed_value
|
||||
xml = authors(:david).to_xml :include=>:posts_with_comments, :indent => 2
|
||||
|
||||
assert Hash.from_xml(xml)
|
||||
assert_match %r{^ <posts-with-comments type="array">}, xml
|
||||
assert_match %r{^ <posts-with-comment type="Post">}, xml
|
||||
assert_match %r{^ <posts-with-comment type="StiPost">}, xml
|
||||
|
||||
types = Hash.from_xml(xml)['author']['posts_with_comments'].collect {|t| t['type'] }
|
||||
assert types.include?('SpecialPost')
|
||||
assert types.include?('Post')
|
||||
assert types.include?('StiPost')
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
require "cases/helper"
|
||||
require 'models/topic'
|
||||
|
||||
class YamlSerializationTest < ActiveRecord::TestCase
|
||||
def test_to_yaml_with_time_with_zone_should_not_raise_exception
|
||||
Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
|
||||
ActiveRecord::Base.time_zone_aware_attributes = true
|
||||
topic = Topic.new(:written_on => DateTime.now)
|
||||
assert_nothing_raised { topic.to_yaml }
|
||||
end
|
||||
end
|
||||
5
vendor/rails/activerecord/test/config.rb
vendored
5
vendor/rails/activerecord/test/config.rb
vendored
|
|
@ -1,5 +0,0 @@
|
|||
TEST_ROOT = File.expand_path(File.dirname(__FILE__))
|
||||
ASSETS_ROOT = TEST_ROOT + "/assets"
|
||||
FIXTURES_ROOT = TEST_ROOT + "/fixtures"
|
||||
MIGRATIONS_ROOT = TEST_ROOT + "/migrations"
|
||||
SCHEMA_ROOT = TEST_ROOT + "/schema"
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
print "Using Derby via JRuby, activerecord-jdbc-adapter and activerecord-jdbcderby-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'jdbcderby',
|
||||
:database => 'activerecord_unittest'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'jdbcderby',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
print "Using H2 via JRuby, activerecord-jdbc-adapter and activerecord-jdbch2-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'jdbch2',
|
||||
:database => 'activerecord_unittest'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'jdbch2',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
print "Using HSQLDB via JRuby, activerecord-jdbc-adapter and activerecord-jdbchsqldb-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'jdbchsqldb',
|
||||
:database => 'activerecord_unittest'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'jdbchsqldb',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
print "Using MySQL via JRuby, activerecord-jdbc-adapter and activerecord-jdbcmysql-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
# GRANT ALL PRIVILEGES ON activerecord_unittest.* to 'rails'@'localhost';
|
||||
# GRANT ALL PRIVILEGES ON activerecord_unittest2.* to 'rails'@'localhost';
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'jdbcmysql',
|
||||
:username => 'rails',
|
||||
:encoding => 'utf8',
|
||||
:database => 'activerecord_unittest',
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'jdbcmysql',
|
||||
:username => 'rails',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
print "Using Postgrsql via JRuby, activerecord-jdbc-adapter and activerecord-postgresql-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
# createuser rails --createdb --no-superuser --no-createrole
|
||||
# createdb -O rails activerecord_unittest
|
||||
# createdb -O rails activerecord_unittest2
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'jdbcpostgresql',
|
||||
:username => ENV['USER'] || 'rails',
|
||||
:database => 'activerecord_unittest'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'jdbcpostgresql',
|
||||
:username => ENV['USER'] || 'rails',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
print "Using SQLite3 via JRuby, activerecord-jdbc-adapter and activerecord-jdbcsqlite3-adapter\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
class SqliteError < StandardError
|
||||
end
|
||||
|
||||
BASE_DIR = FIXTURES_ROOT
|
||||
sqlite_test_db = "#{BASE_DIR}/fixture_database.sqlite3"
|
||||
sqlite_test_db2 = "#{BASE_DIR}/fixture_database_2.sqlite3"
|
||||
|
||||
def make_connection(clazz, db_file)
|
||||
ActiveRecord::Base.configurations = { clazz.name => { :adapter => 'jdbcsqlite3', :database => db_file, :timeout => 5000 } }
|
||||
unless File.exist?(db_file)
|
||||
puts "SQLite3 database not found at #{db_file}. Rebuilding it."
|
||||
sqlite_command = %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
|
||||
puts "Executing '#{sqlite_command}'"
|
||||
raise SqliteError.new("Seems that there is no sqlite3 executable available") unless system(sqlite_command)
|
||||
end
|
||||
clazz.establish_connection(clazz.name)
|
||||
end
|
||||
|
||||
make_connection(ActiveRecord::Base, sqlite_test_db)
|
||||
make_connection(Course, sqlite_test_db2)
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
print "Using native DB2\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'db2',
|
||||
:host => 'localhost',
|
||||
:username => 'arunit',
|
||||
:password => 'arunit',
|
||||
:database => 'arunit'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'db2',
|
||||
:host => 'localhost',
|
||||
:username => 'arunit',
|
||||
:password => 'arunit',
|
||||
:database => 'arunit2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
print "Using native Firebird\n"
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'firebird',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => 'rails',
|
||||
:database => 'activerecord_unittest',
|
||||
:charset => 'UTF8'
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'firebird',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => 'rails',
|
||||
:database => 'activerecord_unittest2'
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
puts 'Using native Frontbase'
|
||||
require_dependency 'models/course'
|
||||
require 'logger'
|
||||
|
||||
ActiveRecord::Base.logger = Logger.new("debug.log")
|
||||
|
||||
ActiveRecord::Base.configurations = {
|
||||
'arunit' => {
|
||||
:adapter => 'frontbase',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => '',
|
||||
:database => 'activerecord_unittest',
|
||||
:session_name => "unittest-#{$$}"
|
||||
},
|
||||
'arunit2' => {
|
||||
:adapter => 'frontbase',
|
||||
:host => 'localhost',
|
||||
:username => 'rails',
|
||||
:password => '',
|
||||
:database => 'activerecord_unittest2',
|
||||
:session_name => "unittest-#{$$}"
|
||||
}
|
||||
}
|
||||
|
||||
ActiveRecord::Base.establish_connection 'arunit'
|
||||
Course.establish_connection 'arunit2'
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue