fix: Validate all conditions in a numericality validation instead of only the last (#997)

The previous `reduce` loop did not break on the first failure and
return the error, it returned the last iteration of the loop

Some more work may need to be done on the atomic side of the validation,
I'm not familiar with how they work!
This commit is contained in:
Rebecca Le 2024-04-10 21:25:42 +08:00 committed by GitHub
parent f504ab6a67
commit 3fce34cda6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 39 additions and 33 deletions

View file

@ -34,41 +34,30 @@ defmodule Ash.Resource.Validation.Compare do
case value do
{:ok, value} ->
Enum.reduce(
Keyword.take(opts, [
opts
|> Keyword.take([
:greater_than,
:less_than,
:greater_than_or_equal_to,
:less_than_or_equal_to
]),
:ok,
fn validation, _ ->
case validation do
])
|> Enum.find_value(fn
{:greater_than, attribute} ->
if Comp.greater_than?(value, attribute_value(changeset, attribute)),
do: :ok,
else: invalid_attribute_error(opts, value)
if !Comp.greater_than?(value, attribute_value(changeset, attribute)),
do: invalid_attribute_error(opts, value)
{:greater_than_or_equal_to, attribute} ->
if Comp.greater_or_equal?(value, attribute_value(changeset, attribute)),
do: :ok,
else: invalid_attribute_error(opts, value)
if !Comp.greater_or_equal?(value, attribute_value(changeset, attribute)),
do: invalid_attribute_error(opts, value)
{:less_than, attribute} ->
if Comp.less_than?(value, attribute_value(changeset, attribute)),
do: :ok,
else: invalid_attribute_error(opts, value)
if !Comp.less_than?(value, attribute_value(changeset, attribute)),
do: invalid_attribute_error(opts, value)
{:less_than_or_equal_to, attribute} ->
if Comp.less_or_equal?(value, attribute_value(changeset, attribute)),
do: :ok,
else: invalid_attribute_error(opts, value)
true ->
:ok
end
end
)
if !Comp.less_or_equal?(value, attribute_value(changeset, attribute)),
do: invalid_attribute_error(opts, value)
end) || :ok
_ ->
:ok

View file

@ -136,8 +136,25 @@ defmodule Ash.Test.Resource.Validation.CompareTest do
end
end
test "validate against a range of values" do
{:ok, opts} =
Compare.init(attribute: :number_one, greater_than: 0, less_than_or_equal_to: 10)
# Below range
changeset = Post |> Ash.Changeset.for_create(:create, %{number_one: -1})
assert_error(changeset, opts, "must be greater than 0 and must be less than or equal to 10")
# In range
changeset = Post |> Ash.Changeset.for_create(:create, %{number_one: 5})
assert :ok = Compare.validate(changeset, opts, %{})
# Above range
changeset = Post |> Ash.Changeset.for_create(:create, %{number_one: 11})
assert_error(changeset, opts, "must be greater than 0 and must be less than or equal to 10")
end
defp assert_error(changeset, opts, expected_message) do
{:error, %{message: message, vars: vars}} = Compare.validate(changeset, opts, %{})
assert {:error, %{message: message, vars: vars}} = Compare.validate(changeset, opts, %{})
assert expected_message == translate_message(message, vars)
end