Skip to content

Commit fc4ae9d

Browse files
committed
Fix and add tests for writable violations due to surfaced changes at insertion
1 parent 26c6df1 commit fc4ae9d

2 files changed

Lines changed: 38 additions & 1 deletion

File tree

lib/ecto/repo/schema.ex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ defmodule Ecto.Repo.Schema do
452452
# On insert, we always merge the whole struct into the
453453
# changeset as changes, except the primary key if it is nil.
454454
changeset = put_repo_and_action(changeset, :insert, repo, tuplet)
455-
changeset = Relation.surface_changes(changeset, struct, keep_fields ++ assocs)
455+
changeset = Relation.surface_changes(changeset, struct, keep_fields ++ drop_fields ++ assocs)
456456
changeset = update_in(changeset.changes, &drop_non_writable_changes!(&1, drop_fields, schema, :insert))
457457

458458
wrap_in_transaction(adapter, adapter_meta, opts, changeset, assocs, embeds, prepare, fn ->

test/ecto/repo_test.exs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2513,6 +2513,15 @@ defmodule Ecto.RepoTest do
25132513
end
25142514
end
25152515

2516+
test "insert with surfaced changes on_writable_violation: :nothing saves changes for writable: :always/:insert and ignores changes for writable: :never" do
2517+
%MySchemaWritable{id: 1, always: 10, never: 11, insert: 12}
2518+
|> Ecto.Changeset.change(%{})
2519+
|> TestRepo.insert()
2520+
2521+
assert_received {:insert, %{fields: inserted_fields}}
2522+
assert Enum.sort(inserted_fields) == [always: 10, id: 1, insert: 12]
2523+
end
2524+
25162525
test "insert with on_writable_violation: :nothing saves changes for writable: :always/:insert and ignores changes for writable: :never" do
25172526
%MySchemaWritable{id: 1}
25182527
|> Ecto.Changeset.change(%{always: 10, never: 11, insert: 12})
@@ -2522,6 +2531,19 @@ defmodule Ecto.RepoTest do
25222531
assert Enum.sort(inserted_fields) == [always: 10, id: 1, insert: 12]
25232532
end
25242533

2534+
test "insert with with surfaced changes and on_writable_violation: :warn saves changes for writable: :always/:insert, ignores changes for writable: :never, and logs a warning" do
2535+
log = capture_log(fn ->
2536+
%MySchemaWritableWarn{id: 1, always: 10, never: 11, insert: 12}
2537+
|> Ecto.Changeset.change(%{})
2538+
|> TestRepo.insert()
2539+
2540+
assert_received {:insert, %{fields: inserted_fields}}
2541+
assert Enum.sort(inserted_fields) == [always: 10, id: 1, insert: 12]
2542+
end)
2543+
2544+
assert log =~ "attempted to write to non-writable field :never during insert"
2545+
end
2546+
25252547
test "insert with on_writable_violation: :warn saves changes for writable: :always/:insert, ignores changes for writable: :never, and logs a warning" do
25262548
log = capture_log(fn ->
25272549
%MySchemaWritableWarn{id: 1}
@@ -2535,6 +2557,21 @@ defmodule Ecto.RepoTest do
25352557
assert log =~ "attempted to write to non-writable field :never during insert"
25362558
end
25372559

2560+
test "insert with surfaced changes and on_writable_violation: :raise saves changes for writable: :always/:insert and raises for changes for writable: :never" do
2561+
assert_raise ArgumentError, "attempted to write to non-writable field :never during insert", fn ->
2562+
%MySchemaWritableRaise{id: 1, never: 10}
2563+
|> Ecto.Changeset.change(%{})
2564+
|> TestRepo.insert()
2565+
end
2566+
2567+
%MySchemaWritableRaise{id: 2, insert: 11, always: 12}
2568+
|> Ecto.Changeset.change(%{})
2569+
|> TestRepo.insert()
2570+
2571+
assert_received {:insert, %{fields: inserted_fields}}
2572+
assert Enum.sort(inserted_fields) == [always: 12, id: 2, insert: 11]
2573+
end
2574+
25382575
test "insert with on_writable_violation: :raise saves changes for writable: :always/:insert and raises for changes for writable: :never" do
25392576
assert_raise ArgumentError, "attempted to write to non-writable field :never during insert", fn ->
25402577
%MySchemaWritableRaise{id: 1}

0 commit comments

Comments
 (0)