Skip to content

Commit 3255411

Browse files
byrootzzak
andcommitted
Add tests around has_one changing the target and validators
Ref: rails#48330 When replacing the has_one target, it seems more correct to first delete the old record, so that if the associated model has a uniqueness validator, it won't fail. Both the delete and the insert are in a single transaction, so if the new record fail to be saved, the transaction will be rolled back, which seems correct. If `dependent: :destroy` isn't set, Active Record will try to orphan the record, which may or may not be valid depending on the schema and validators. Co-Authored-By: zzak <[email protected]>
1 parent 655278e commit 3255411

File tree

1 file changed

+42
-0
lines changed

1 file changed

+42
-0
lines changed

activerecord/test/cases/associations/has_one_associations_test.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
require "models/membership"
2121
require "models/parrot"
2222
require "models/cpk"
23+
require "models/user"
24+
require "models/content"
2325

2426
class HasOneAssociationsTest < ActiveRecord::TestCase
2527
self.use_transactional_tests = false unless supports_savepoints?
@@ -905,6 +907,46 @@ def test_has_one_with_touch_option_on_nonpersisted_built_associations_doesnt_upd
905907
end
906908
end
907909

910+
class InvalidPost < Post
911+
validate :always_fail
912+
913+
def always_fail
914+
errors.add(:base, "Oops")
915+
end
916+
end
917+
918+
test "preserve old record if new one is invalid" do
919+
author = Author.create!(id: 33, name: "Hank Moody")
920+
author.create_post!(id: 1234, title: "Some title", body: "Some content")
921+
922+
assert_no_difference -> { Post.count } do
923+
assert_raises ActiveRecord::RecordNotSaved do
924+
author.post = InvalidPost.new(title: "Some title", body: "Some content")
925+
end
926+
author.save!
927+
end
928+
end
929+
930+
class SpecialContentPosition < ActiveRecord::Base
931+
self.table_name = "content_positions"
932+
belongs_to :content, class_name: name + "::SpecialContentPosition"
933+
validates :content_id, presence: true, uniqueness: true
934+
end
935+
936+
class SpecialContent < ActiveRecord::Base
937+
self.table_name = "content"
938+
has_one :content_position, dependent: :destroy, foreign_key: :content_id, class_name: SpecialContentPosition.name
939+
end
940+
941+
test "uniqueness validator doesn't prevent from replacing the old record if dependent: :destroy is set" do
942+
content = SpecialContent.create!
943+
content.create_content_position!
944+
945+
assert_no_difference -> { ContentPosition.count } do
946+
content.create_content_position!
947+
end
948+
end
949+
908950
test "composite primary key malformed association" do
909951
error = assert_raises(ActiveRecord::CompositePrimaryKeyMismatchError) do
910952
order = Cpk::BrokenOrder.new(id: [1, 2], book: Cpk::Book.new(title: "Some book"))

0 commit comments

Comments
 (0)