diff --git a/CHANGELOG.md b/CHANGELOG.md index 380fdf5..2babd30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ +## Unreleased + +### Added + +* Ensure field/assoc/embed exists when listing errors in `flat_errors_on/3`. This prevents accidental test passes on typos in assertions like `refute_errors_on(cs, :sommtypo)`. + ## [1.0.0] - 2023-12-21 No changes from v0.17.0. diff --git a/lib/bitcrowd_ecto/assertions.ex b/lib/bitcrowd_ecto/assertions.ex index aa22dd1..0445b44 100644 --- a/lib/bitcrowd_ecto/assertions.ex +++ b/lib/bitcrowd_ecto/assertions.ex @@ -43,7 +43,9 @@ defmodule BitcrowdEcto.Assertions do @doc since: "0.1.0" @spec flat_errors_on(Changeset.t(), atom) :: [String.t() | atom] @spec flat_errors_on(Changeset.t(), atom, [{:metadata, atom}]) :: [String.t() | atom] - def flat_errors_on(changeset, field, opts \\ []) do + def flat_errors_on(%Changeset{} = changeset, field, opts \\ []) when is_atom(field) do + assert_field_or_assoc_or_embed(changeset, field) + metadata = opts |> Keyword.get(:metadata, [:constraint, :validation]) @@ -66,6 +68,23 @@ defmodule BitcrowdEcto.Assertions do end) end + defp assert_field_or_assoc_or_embed(%Changeset{} = changeset, field) when is_atom(field) do + assert_field_or_assoc_or_embed(changeset.data, field) + end + + defp assert_field_or_assoc_or_embed(%{__struct__: schema}, field) when is_atom(field) do + fields_and_assocs_and_embeds = + List.flatten([ + schema.__schema__(:fields), + schema.__schema__(:associations), + schema.__schema__(:embeds) + ]) + + assert field in fields_and_assocs_and_embeds, + message: + "Trying to access #{inspect(field)} on #{inspect(schema)}, but it is not a field, association, or embed." + end + @doc """ Asserts that a changeset contains a given error on a given field. diff --git a/test/bitcrowd_ecto/assertions_test.exs b/test/bitcrowd_ecto/assertions_test.exs index a41c8af..89947b0 100644 --- a/test/bitcrowd_ecto/assertions_test.exs +++ b/test/bitcrowd_ecto/assertions_test.exs @@ -30,6 +30,12 @@ defmodule BitcrowdEcto.AssertionsTest do assert :bar in flat_errors_on(cs, :some_string, metadata: :foo) assert :bar in flat_errors_on(cs, :some_string, metadata: [:foo]) end + + test "fails when trying to list errors for a field/assoc/embed that does not exist" do + assert_raise ExUnit.AssertionError, ~r/not a field/, fn -> + assert flat_errors_on(changeset(), :doesnotexist) + end + end end describe "assert_error_on/4" do