From 92cecb396e4fadf29ef01229644a8c4174f09913 Mon Sep 17 00:00:00 2001 From: Mathieu Decaffmeyer <5883963+mathieuprog@users.noreply.github.com> Date: Sun, 26 May 2024 20:12:10 +0800 Subject: [PATCH] Test travers_errors --- test/polymorphic_embed_test.exs | 472 +++++++++++++++++++ test/support/models/not_polymorphic/event.ex | 21 + test/support/models/not_polymorphic/todo.ex | 21 + test/support/models/polymorphic/event.ex | 21 + test/support/models/polymorphic/todo.ex | 21 + 5 files changed, 556 insertions(+) create mode 100644 test/support/models/not_polymorphic/event.ex create mode 100644 test/support/models/not_polymorphic/todo.ex create mode 100644 test/support/models/polymorphic/event.ex create mode 100644 test/support/models/polymorphic/todo.ex diff --git a/test/polymorphic_embed_test.exs b/test/polymorphic_embed_test.exs index c4f07b4..1da1bf0 100644 --- a/test/polymorphic_embed_test.exs +++ b/test/polymorphic_embed_test.exs @@ -294,6 +294,478 @@ defmodule PolymorphicEmbedTest do end end + test "traverse_errors on nested *-to-many relations" do + for generator <- @generators do + event_module = get_module(Event, generator) + + event_attrs = %{ + reminders: [ + %{ + text: "This is an SMS reminder", + channel: %{ + my_type_field: "sms" + }, + contexts: [ + %{ + __type__: "location", + address: "hello", + country: %{ + name: "" + } + }, + %{ + __type__: "location", + address: "" + } + ] + } + ] + } + + changeset = + struct(event_module) + |> event_module.changeset(event_attrs) + + insert_result = Repo.insert(changeset) + + assert {:error, + %Ecto.Changeset{ + action: :insert, + valid?: false, + errors: errors, + changes: %{ + reminders: [ + %{ + action: :insert, + valid?: false, + errors: reminder_errors, + changes: %{ + channel: %{ + action: :insert, + valid?: false, + errors: channel_errors + }, + contexts: [ + %{ + action: :insert, + valid?: false, + errors: context1_errors, + changes: %{ + country: %{ + action: :insert, + valid?: false, + errors: country_errors + } + } + }, + %{ + action: :insert, + valid?: false, + errors: context2_errors + } + ] + } + } + ] + } + }} = insert_result + + assert [] = errors + + assert [date: {"can't be blank", [validation: :required]}] = reminder_errors + + assert %{ + number: {"can't be blank", [validation: :required]}, + country_code: {"can't be blank", [validation: :required]}, + provider: {"can't be blank", [validation: :required]} + } = Map.new(channel_errors) + + assert [] = context1_errors + assert %{address: {"can't be blank", [validation: :required]}} = Map.new(context2_errors) + assert %{name: {"can't be blank", [validation: :required]}} = Map.new(country_errors) + + traverse_errors_fun = + if polymorphic?(generator) do + &PolymorphicEmbed.traverse_errors/2 + else + &Ecto.Changeset.traverse_errors/2 + end + + %{ + reminders: [ + %{ + channel: %{ + country_code: ["can't be blank"], + number: ["can't be blank"], + provider: ["can't be blank"] + }, + contexts: [%{country: %{name: ["can't be blank"]}}, %{address: ["can't be blank"]}], + date: ["can't be blank"] + } + ] + } = + traverse_errors_fun.( + changeset, + fn {msg, opts} -> + Enum.reduce(opts, msg, fn {key, value}, acc -> + String.replace(acc, "%{#{key}}", to_string(value)) + end) + end + ) + end + end + + test "traverse_errors on nested embeds_many relations" do + for generator <- @generators do + event_module = get_module(Event, generator) + + event_attrs = %{ + embedded_reminders: [ + %{ + text: "This is an SMS reminder", + channel: %{ + my_type_field: "sms" + }, + contexts: [ + %{ + __type__: "location", + address: "hello", + country: %{ + name: "" + } + }, + %{ + __type__: "location", + address: "" + } + ] + } + ] + } + + changeset = + struct(event_module) + |> event_module.changeset(event_attrs) + + insert_result = Repo.insert(changeset) + + assert {:error, + %Ecto.Changeset{ + action: :insert, + valid?: false, + errors: errors, + changes: %{ + embedded_reminders: [ + %{ + action: :insert, + valid?: false, + errors: reminder_errors, + changes: %{ + channel: %{ + action: :insert, + valid?: false, + errors: channel_errors + }, + contexts: [ + %{ + action: :insert, + valid?: false, + errors: context1_errors, + changes: %{ + country: %{ + action: :insert, + valid?: false, + errors: country_errors + } + } + }, + %{ + action: :insert, + valid?: false, + errors: context2_errors + } + ] + } + } + ] + } + }} = insert_result + + assert [] = errors + + assert [date: {"can't be blank", [validation: :required]}] = reminder_errors + + assert %{ + number: {"can't be blank", [validation: :required]}, + country_code: {"can't be blank", [validation: :required]}, + provider: {"can't be blank", [validation: :required]} + } = Map.new(channel_errors) + + assert [] = context1_errors + assert %{address: {"can't be blank", [validation: :required]}} = Map.new(context2_errors) + assert %{name: {"can't be blank", [validation: :required]}} = Map.new(country_errors) + + traverse_errors_fun = + if polymorphic?(generator) do + &PolymorphicEmbed.traverse_errors/2 + else + &Ecto.Changeset.traverse_errors/2 + end + + %{ + embedded_reminders: [ + %{ + channel: %{ + country_code: ["can't be blank"], + number: ["can't be blank"], + provider: ["can't be blank"] + }, + contexts: [%{country: %{name: ["can't be blank"]}}, %{address: ["can't be blank"]}], + date: ["can't be blank"] + } + ] + } = + traverse_errors_fun.( + changeset, + fn {msg, opts} -> + Enum.reduce(opts, msg, fn {key, value}, acc -> + String.replace(acc, "%{#{key}}", to_string(value)) + end) + end + ) + end + end + + test "traverse_errors on nested *-to-one relations" do + for generator <- @generators do + todo_module = get_module(Todo, generator) + + todo_attrs = %{ + reminder: %{ + text: "This is an SMS reminder", + channel: %{ + my_type_field: "sms" + }, + contexts: [ + %{ + __type__: "location", + address: "hello", + country: %{ + name: "" + } + }, + %{ + __type__: "location", + address: "" + } + ] + } + } + + changeset = + struct(todo_module) + |> todo_module.changeset(todo_attrs) + + insert_result = Repo.insert(changeset) + + assert {:error, + %Ecto.Changeset{ + action: :insert, + valid?: false, + errors: errors, + changes: %{ + reminder: %{ + action: :insert, + valid?: false, + errors: reminder_errors, + changes: %{ + channel: %{ + action: :insert, + valid?: false, + errors: channel_errors + }, + contexts: [ + %{ + action: :insert, + valid?: false, + errors: context1_errors, + changes: %{ + country: %{ + action: :insert, + valid?: false, + errors: country_errors + } + } + }, + %{ + action: :insert, + valid?: false, + errors: context2_errors + } + ] + } + } + } + }} = insert_result + + assert [] = errors + + assert [date: {"can't be blank", [validation: :required]}] = reminder_errors + + assert %{ + number: {"can't be blank", [validation: :required]}, + country_code: {"can't be blank", [validation: :required]}, + provider: {"can't be blank", [validation: :required]} + } = Map.new(channel_errors) + + assert [] = context1_errors + assert %{address: {"can't be blank", [validation: :required]}} = Map.new(context2_errors) + assert %{name: {"can't be blank", [validation: :required]}} = Map.new(country_errors) + + traverse_errors_fun = + if polymorphic?(generator) do + &PolymorphicEmbed.traverse_errors/2 + else + &Ecto.Changeset.traverse_errors/2 + end + + %{ + reminder: %{ + channel: %{ + country_code: ["can't be blank"], + number: ["can't be blank"], + provider: ["can't be blank"] + }, + contexts: [%{country: %{name: ["can't be blank"]}}, %{address: ["can't be blank"]}], + date: ["can't be blank"] + } + } = + traverse_errors_fun.( + changeset, + fn {msg, opts} -> + Enum.reduce(opts, msg, fn {key, value}, acc -> + String.replace(acc, "%{#{key}}", to_string(value)) + end) + end + ) + end + end + + test "traverse_errors on nested embeds_one relations" do + for generator <- @generators do + todo_module = get_module(Todo, generator) + + todo_attrs = %{ + embedded_reminder: %{ + text: "This is an SMS reminder", + channel: %{ + my_type_field: "sms" + }, + contexts: [ + %{ + __type__: "location", + address: "hello", + country: %{ + name: "" + } + }, + %{ + __type__: "location", + address: "" + } + ] + } + } + + changeset = + struct(todo_module) + |> todo_module.changeset(todo_attrs) + + insert_result = Repo.insert(changeset) + + assert {:error, + %Ecto.Changeset{ + action: :insert, + valid?: false, + errors: errors, + changes: %{ + embedded_reminder: %{ + action: :insert, + valid?: false, + errors: reminder_errors, + changes: %{ + channel: %{ + action: :insert, + valid?: false, + errors: channel_errors + }, + contexts: [ + %{ + action: :insert, + valid?: false, + errors: context1_errors, + changes: %{ + country: %{ + action: :insert, + valid?: false, + errors: country_errors + } + } + }, + %{ + action: :insert, + valid?: false, + errors: context2_errors + } + ] + } + } + } + }} = insert_result + + assert [] = errors + + assert [date: {"can't be blank", [validation: :required]}] = reminder_errors + + assert %{ + number: {"can't be blank", [validation: :required]}, + country_code: {"can't be blank", [validation: :required]}, + provider: {"can't be blank", [validation: :required]} + } = Map.new(channel_errors) + + assert [] = context1_errors + assert %{address: {"can't be blank", [validation: :required]}} = Map.new(context2_errors) + assert %{name: {"can't be blank", [validation: :required]}} = Map.new(country_errors) + + traverse_errors_fun = + if polymorphic?(generator) do + &PolymorphicEmbed.traverse_errors/2 + else + &Ecto.Changeset.traverse_errors/2 + end + + %{ + embedded_reminder: %{ + channel: %{ + country_code: ["can't be blank"], + number: ["can't be blank"], + provider: ["can't be blank"] + }, + contexts: [%{country: %{name: ["can't be blank"]}}, %{address: ["can't be blank"]}], + date: ["can't be blank"] + } + } = + traverse_errors_fun.( + changeset, + fn {msg, opts} -> + Enum.reduce(opts, msg, fn {key, value}, acc -> + String.replace(acc, "%{#{key}}", to_string(value)) + end) + end + ) + end + end + test "traverse_errors on changesets with valid polymorphic structs" do for generator <- @generators do reminder_module = get_module(Reminder, generator) diff --git a/test/support/models/not_polymorphic/event.ex b/test/support/models/not_polymorphic/event.ex new file mode 100644 index 0000000..b70fba8 --- /dev/null +++ b/test/support/models/not_polymorphic/event.ex @@ -0,0 +1,21 @@ +defmodule PolymorphicEmbed.Regular.Event do + @moduledoc """ + An (calendar) event, which can optionally have multiple reminders. + """ + use Ecto.Schema + import Ecto.Changeset + alias PolymorphicEmbed.Regular.Reminder + + schema "events" do + has_many(:reminders, Reminder) + embeds_many(:embedded_reminders, Reminder) + timestamps() + end + + def changeset(struct, params) do + struct + |> cast(params, []) + |> cast_assoc(:reminders) + |> cast_embed(:embedded_reminders) + end +end diff --git a/test/support/models/not_polymorphic/todo.ex b/test/support/models/not_polymorphic/todo.ex new file mode 100644 index 0000000..ea0a103 --- /dev/null +++ b/test/support/models/not_polymorphic/todo.ex @@ -0,0 +1,21 @@ +defmodule PolymorphicEmbed.Regular.Todo do + @moduledoc """ + A todo item, which can optionally have a single reminder. + """ + use Ecto.Schema + import Ecto.Changeset + alias PolymorphicEmbed.Regular.Reminder + + schema "todos" do + belongs_to(:reminder, Reminder) + embeds_one(:embedded_reminder, Reminder) + timestamps() + end + + def changeset(struct, params) do + struct + |> cast(params, []) + |> cast_assoc(:reminder) + |> cast_embed(:embedded_reminder) + end +end diff --git a/test/support/models/polymorphic/event.ex b/test/support/models/polymorphic/event.ex new file mode 100644 index 0000000..876036d --- /dev/null +++ b/test/support/models/polymorphic/event.ex @@ -0,0 +1,21 @@ +defmodule PolymorphicEmbed.Event do + @moduledoc """ + An (calendar) event, which can optionally have multiple reminders. + """ + use Ecto.Schema + import Ecto.Changeset + alias PolymorphicEmbed.Reminder + + schema "events" do + has_many(:reminders, Reminder) + embeds_many(:embedded_reminders, Reminder) + timestamps() + end + + def changeset(struct, params) do + struct + |> cast(params, []) + |> cast_assoc(:reminders) + |> cast_embed(:embedded_reminders) + end +end diff --git a/test/support/models/polymorphic/todo.ex b/test/support/models/polymorphic/todo.ex new file mode 100644 index 0000000..e7e074e --- /dev/null +++ b/test/support/models/polymorphic/todo.ex @@ -0,0 +1,21 @@ +defmodule PolymorphicEmbed.Todo do + @moduledoc """ + A todo item, which can optionally have a single reminder. + """ + use Ecto.Schema + import Ecto.Changeset + alias PolymorphicEmbed.Reminder + + schema "todos" do + belongs_to(:reminder, Reminder) + embeds_one(:embedded_reminder, Reminder) + timestamps() + end + + def changeset(struct, params) do + struct + |> cast(params, []) + |> cast_assoc(:reminder) + |> cast_embed(:embedded_reminder) + end +end