mirror of
https://github.com/ash-project/ash.git
synced 2024-09-20 13:33:20 +12:00
2d9f9ee362
note: these have been deprecated for a very long time, removing them will not be considered a breaking change (and there will be clear compile errors)
209 lines
7.7 KiB
Elixir
209 lines
7.7 KiB
Elixir
defmodule Ash.Test.ErrorTest do
|
|
@moduledoc false
|
|
use ExUnit.Case, async: true
|
|
|
|
defmodule TestError do
|
|
use Ash.Error.Exception
|
|
def_ash_error([:some_field])
|
|
end
|
|
|
|
defmodule TestResource do
|
|
use Ash.Resource, data_layer: Ash.DataLayer.Ets
|
|
|
|
actions do
|
|
defaults [:create, :read, :update, :destroy]
|
|
end
|
|
|
|
attributes do
|
|
uuid_primary_key :id
|
|
end
|
|
end
|
|
|
|
describe "to_error_class" do
|
|
test "returns exception if it is a map/struct with class: :special" do
|
|
assert Ash.Error.to_error_class(%{class: :special}, []) == %{class: :special}
|
|
end
|
|
|
|
test "returns exception if it is a map/struct with class: :special wrapped in a list" do
|
|
assert Ash.Error.to_error_class([%{class: :special}], []) == %{class: :special}
|
|
end
|
|
|
|
test "returns exception if it is a map/struct with class: :special wrapped in an Invalid error" do
|
|
err = Ash.Error.Invalid.exception(errors: [%{class: :special}])
|
|
|
|
assert Ash.Error.to_error_class(err, []) == %{class: :special}
|
|
end
|
|
|
|
test "returns chosen error if the value argument is a list of values" do
|
|
values = ["foo", "bar"]
|
|
|
|
result = Ash.Error.to_error_class(values, [])
|
|
|
|
assert match?(%Ash.Error.Unknown{}, result)
|
|
|
|
# given the test arrangement, each error in the error class should
|
|
# * be an Ash.Error.Unknown.UnknownError
|
|
# * have a .class == :unknown
|
|
# * have a .error that is a distinct element of the uniq subset of the values provided
|
|
|
|
assert same_elements?(Enum.map(result.errors, fn err -> err.error end), values)
|
|
|
|
for err <- result.errors do
|
|
assert match?(%Ash.Error.Unknown.UnknownError{}, err)
|
|
assert err.class == :unknown
|
|
end
|
|
end
|
|
|
|
test "returns chosen error if the value argument is a list of errors" do
|
|
err1 = Ash.Error.Unknown.UnknownError.exception(error: :an_error)
|
|
err2 = Ash.Error.Invalid.exception(errors: [:more, :errors])
|
|
|
|
result = Ash.Error.to_error_class([err1, err2], [])
|
|
|
|
# the parent error will be of the invalid class because it take precedence over unknown
|
|
assert match?(%Ash.Error.Invalid{}, result)
|
|
|
|
# the parent error's errors field gets prepended to the list of other errors
|
|
assert same_elements?(result.errors, [:more, :errors, err1])
|
|
end
|
|
|
|
test "has a context field populated when there is a single error" do
|
|
test_error = TestError.exception([])
|
|
|
|
err = Ash.Error.to_error_class(test_error, error_context: "some context")
|
|
|
|
assert err.error_context == ["some context"]
|
|
end
|
|
|
|
test "has a context field populated when there is a list of errors" do
|
|
test_error1 = TestError.exception(some_field: :a)
|
|
test_error2 = TestError.exception(some_field: :b)
|
|
|
|
err = Ash.Error.to_error_class([test_error1, test_error2], error_context: "some context")
|
|
|
|
assert err.error_context == ["some context"]
|
|
end
|
|
|
|
test "accumulates error_context field in child errors" do
|
|
error1 = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!", nil, error_context: "some other context")
|
|
|
|
error_class =
|
|
Ash.Error.to_error_class([error1, error2], error_context: "some higher context")
|
|
|
|
child_error_1 = Enum.find(error_class.errors, fn err -> err.error == "whoops!" end)
|
|
assert child_error_1.error_context == ["some higher context", "some context"]
|
|
|
|
child_error_2 = Enum.find(error_class.errors, fn err -> err.error == "whoops, again!!" end)
|
|
assert child_error_2.error_context == ["some higher context", "some other context"]
|
|
end
|
|
|
|
test "accumulates error_context field in child errors who have no error_context of their own" do
|
|
error1 = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!", nil)
|
|
|
|
error_class =
|
|
Ash.Error.to_error_class([error1, error2], error_context: "some higher context")
|
|
|
|
child_error_1 = Enum.find(error_class.errors, fn err -> err.error == "whoops!" end)
|
|
assert child_error_1.error_context == ["some higher context", "some context"]
|
|
|
|
child_error_2 = Enum.find(error_class.errors, fn err -> err.error == "whoops, again!!" end)
|
|
assert child_error_2.error_context == ["some higher context"]
|
|
end
|
|
|
|
test "leaves child error contexts unchanged if no error_context field provided" do
|
|
error1 = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!", nil, error_context: "some other context")
|
|
|
|
error_class = Ash.Error.to_error_class([error1, error2])
|
|
|
|
child_error_1 = Enum.find(error_class.errors, fn err -> err.error == "whoops!" end)
|
|
assert child_error_1.error_context == ["some context"]
|
|
|
|
child_error_2 = Enum.find(error_class.errors, fn err -> err.error == "whoops, again!!" end)
|
|
assert child_error_2.error_context == ["some other context"]
|
|
end
|
|
|
|
test "error message contains error context breadcrumbs" do
|
|
error1 = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!", nil, error_context: "some other context")
|
|
|
|
error_class =
|
|
Ash.Error.to_error_class([error1, error2], error_context: "some higher context")
|
|
|
|
error_message = Ash.Error.Unknown.message(error_class)
|
|
|
|
assert error_message =~ "Context: some higher context > some context"
|
|
assert error_message =~ "Context: some higher context > some other context"
|
|
end
|
|
|
|
test "error message still renders when there's no error context" do
|
|
error1 = Ash.Error.to_ash_error("whoops!")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!")
|
|
|
|
error_class = Ash.Error.to_error_class([error1, error2])
|
|
|
|
error_message = Ash.Error.Unknown.message(error_class)
|
|
|
|
assert error_message =~ "Unknown Error\n\n* whoops!"
|
|
end
|
|
|
|
test "has a context field populated in changeset" do
|
|
test_error = TestError.exception([])
|
|
|
|
cs = Ash.Changeset.for_create(TestResource, :create)
|
|
|
|
err = Ash.Error.to_error_class(test_error, changeset: cs, error_context: "some context")
|
|
|
|
assert err.error_context == ["some context"]
|
|
|
|
[cs_error] = err.changeset.errors
|
|
assert cs_error.error_context == ["some context"]
|
|
end
|
|
|
|
test "accumulates error_context field in changeset's copy of error hierarchy" do
|
|
error1 = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
error2 = Ash.Error.to_ash_error("whoops, again!!", nil, error_context: "some other context")
|
|
|
|
cs = Ash.Changeset.for_create(TestResource, :create)
|
|
|
|
error_class =
|
|
Ash.Error.to_error_class([error1, error2],
|
|
changeset: cs,
|
|
error_context: "some higher context"
|
|
)
|
|
|
|
cs_child_error_1 =
|
|
Enum.find(error_class.changeset.errors, fn err -> err.error == "whoops!" end)
|
|
|
|
cs_child_error_2 =
|
|
Enum.find(error_class.changeset.errors, fn err -> err.error == "whoops, again!!" end)
|
|
|
|
assert cs_child_error_1.error_context == ["some higher context", "some context"]
|
|
assert cs_child_error_2.error_context == ["some higher context", "some other context"]
|
|
end
|
|
end
|
|
|
|
describe "to_ash_error" do
|
|
test "populates error_context field" do
|
|
error = Ash.Error.to_ash_error("whoops!", nil, error_context: "some context")
|
|
|
|
assert error.error_context == ["some context"]
|
|
end
|
|
end
|
|
|
|
defp same_elements?(xs, ys) when is_list(xs) and is_list(ys) do
|
|
Enum.sort(clean(xs)) == Enum.sort(clean(ys))
|
|
end
|
|
|
|
defp same_elements?(_, _), do: false
|
|
|
|
defp clean(list) when is_list(list), do: Enum.map(list, &clean/1)
|
|
|
|
defp clean(%{stacktrace: _} = value) do
|
|
%{value | stacktrace: nil}
|
|
end
|
|
|
|
defp clean(other), do: other
|
|
end
|