2024-03-15 10:21:48 +13:00
|
|
|
defmodule SplodeTest do
|
|
|
|
use ExUnit.Case
|
2024-04-24 22:22:48 +12:00
|
|
|
|
|
|
|
# Error classes
|
|
|
|
|
|
|
|
defmodule HwError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.ErrorClass, class: :hw
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule SwError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.ErrorClass, class: :sw
|
|
|
|
end
|
|
|
|
|
2024-05-22 09:31:44 +12:00
|
|
|
defmodule ContainerErrorClass do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.ErrorClass, class: :ui
|
|
|
|
end
|
|
|
|
|
2024-04-24 22:22:48 +12:00
|
|
|
# Errors
|
|
|
|
|
|
|
|
defmodule CpuError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, class: :hw
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule RamError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, class: :hw
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule DivByZeroException do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, fields: [:num, :denom], class: :sw
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule NullReferenceException do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, class: :sw
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule UnknownError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, fields: [:error], class: :unknown
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
2024-05-22 09:31:44 +12:00
|
|
|
defmodule ExampleContainerError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, fields: [:description], class: :ui
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule ContainerUnknownError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode.Error, fields: [:error], class: :unknown
|
|
|
|
def message(err), do: err |> inspect()
|
|
|
|
end
|
|
|
|
|
2024-04-24 22:22:48 +12:00
|
|
|
defmodule SystemError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode,
|
|
|
|
error_classes: [
|
|
|
|
hw: HwError,
|
|
|
|
sw: SwError
|
|
|
|
],
|
|
|
|
unknown_error: UnknownError
|
|
|
|
end
|
|
|
|
|
2024-05-22 09:31:44 +12:00
|
|
|
defmodule ContainerError do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode,
|
|
|
|
error_classes: [
|
2024-05-22 11:23:59 +12:00
|
|
|
interaction: ContainerErrorClass,
|
|
|
|
hw: HwError,
|
|
|
|
sw: SwError
|
2024-05-22 09:31:44 +12:00
|
|
|
],
|
|
|
|
unknown_error: ContainerUnknownError,
|
|
|
|
merge_with: [SystemError]
|
|
|
|
end
|
|
|
|
|
|
|
|
defmodule ContainerWithoutMergeWith do
|
|
|
|
@moduledoc false
|
|
|
|
use Splode,
|
|
|
|
error_classes: [
|
|
|
|
interaction: ContainerErrorClass
|
|
|
|
],
|
|
|
|
unknown_error: ContainerUnknownError,
|
|
|
|
merge_with: []
|
|
|
|
end
|
|
|
|
|
2024-04-24 22:22:48 +12:00
|
|
|
test "splode_error?" do
|
|
|
|
refute SystemError.splode_error?(:error)
|
|
|
|
refute SystemError.splode_error?(%{})
|
|
|
|
refute SystemError.splode_error?([])
|
|
|
|
|
|
|
|
assert SystemError.splode_error?(HwError.exception())
|
|
|
|
assert SystemError.splode_error?(SwError.exception())
|
|
|
|
|
|
|
|
assert SystemError.splode_error?(CpuError.exception())
|
|
|
|
assert SystemError.splode_error?(RamError.exception())
|
|
|
|
assert SystemError.splode_error?(DivByZeroException.exception())
|
|
|
|
assert SystemError.splode_error?(NullReferenceException.exception())
|
|
|
|
assert SystemError.splode_error?(UnknownError.exception())
|
|
|
|
end
|
|
|
|
|
|
|
|
test "set_path" do
|
|
|
|
null = NullReferenceException.exception(path: [:a])
|
|
|
|
null = SystemError.set_path(null, :b)
|
|
|
|
|
|
|
|
assert null.path == [:b, :a]
|
|
|
|
end
|
|
|
|
|
|
|
|
describe "to_class" do
|
|
|
|
setup do
|
|
|
|
cpu = CpuError.exception() |> SystemError.to_error()
|
|
|
|
ram = RamError.exception() |> SystemError.to_error()
|
|
|
|
div = DivByZeroException.exception() |> SystemError.to_error()
|
|
|
|
null = NullReferenceException.exception() |> SystemError.to_error()
|
2024-05-22 09:31:44 +12:00
|
|
|
example_container_error = ExampleContainerError.exception() |> ContainerError.to_error()
|
2024-04-24 22:22:48 +12:00
|
|
|
|
2024-05-22 09:31:44 +12:00
|
|
|
%{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
div: div,
|
|
|
|
null: null,
|
|
|
|
example_container_error: example_container_error
|
|
|
|
}
|
2024-04-24 22:22:48 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "wraps errors in error class with same class", %{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
div: div,
|
|
|
|
null: null
|
|
|
|
} do
|
|
|
|
# H/W errors
|
|
|
|
hw_error = [cpu, ram] |> SystemError.to_class()
|
|
|
|
assert %HwError{errors: [^cpu, ^ram]} = hw_error
|
|
|
|
|
|
|
|
# S/W errors
|
|
|
|
sw_error = [div, null] |> SystemError.to_class()
|
|
|
|
assert %SwError{errors: [^div, ^null]} = sw_error
|
|
|
|
end
|
|
|
|
|
|
|
|
test "error class with smaller index is selected for mixed class case", %{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
div: div,
|
|
|
|
null: null
|
|
|
|
} do
|
|
|
|
errors = [cpu, ram, div, null] |> Enum.shuffle()
|
|
|
|
assert %HwError{errors: ^errors} = errors |> SystemError.to_class()
|
|
|
|
end
|
|
|
|
|
|
|
|
test "idempotent", %{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
div: div,
|
|
|
|
null: null
|
|
|
|
} do
|
|
|
|
error =
|
|
|
|
[cpu, ram, div, null] |> Enum.shuffle() |> Enum.take(2) |> SystemError.to_class()
|
|
|
|
|
|
|
|
assert error == error |> SystemError.to_class()
|
|
|
|
end
|
2024-05-22 09:31:44 +12:00
|
|
|
|
|
|
|
test "to_error flattens nested errors when included in merge_with", %{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
example_container_error: example_container_error
|
|
|
|
} do
|
|
|
|
hw_error = [cpu, ram] |> SystemError.to_class()
|
|
|
|
|
2024-05-22 11:23:59 +12:00
|
|
|
interaction_error = ContainerError.to_class([hw_error, example_container_error])
|
2024-05-22 09:31:44 +12:00
|
|
|
|
|
|
|
assert %{errors: [^cpu, ^ram, ^example_container_error]} = interaction_error
|
|
|
|
end
|
|
|
|
|
|
|
|
test "to_error doesn't flatten nested errors when not included in merge_with", %{
|
|
|
|
cpu: cpu,
|
|
|
|
ram: ram,
|
|
|
|
example_container_error: example_container_error
|
|
|
|
} do
|
2024-05-22 11:23:59 +12:00
|
|
|
hw_error = [cpu, ram] |> SystemError.to_class()
|
2024-05-22 09:31:44 +12:00
|
|
|
|
2024-05-22 11:23:59 +12:00
|
|
|
interaction_error = ContainerWithoutMergeWith.to_class([hw_error, example_container_error])
|
2024-05-22 09:31:44 +12:00
|
|
|
|
2024-05-22 11:32:23 +12:00
|
|
|
assert %{errors: [%SplodeTest.ContainerUnknownError{}, %SplodeTest.ContainerUnknownError{}]} =
|
|
|
|
interaction_error
|
2024-05-22 09:31:44 +12:00
|
|
|
end
|
2024-04-24 22:22:48 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
test "to_error" do
|
|
|
|
error_tuple = {:error, :div_by_zero}
|
|
|
|
assert %UnknownError{class: :unknown} = SystemError.to_error(error_tuple)
|
|
|
|
|
|
|
|
runtime_error = %RuntimeError{}
|
|
|
|
assert %UnknownError{class: :unknown} = SystemError.to_error(runtime_error)
|
|
|
|
|
|
|
|
div_by_zero = DivByZeroException.exception()
|
|
|
|
assert %DivByZeroException{} = SystemError.to_error(div_by_zero)
|
|
|
|
end
|
|
|
|
|
|
|
|
test "from_json" do
|
|
|
|
div_by_zero =
|
|
|
|
SystemError.from_json(DivByZeroException, %{"num" => 10, "denom" => 0})
|
|
|
|
|
|
|
|
assert %DivByZeroException{num: 10, denom: 0} = div_by_zero
|
|
|
|
end
|
2024-03-15 10:21:48 +13:00
|
|
|
end
|