splode/test/splode_test.exs
2024-05-21 19:32:23 -04:00

216 lines
5.7 KiB
Elixir

defmodule SplodeTest do
use ExUnit.Case
# Error classes
defmodule HwError do
@moduledoc false
use Splode.ErrorClass, class: :hw
end
defmodule SwError do
@moduledoc false
use Splode.ErrorClass, class: :sw
end
defmodule ContainerErrorClass do
@moduledoc false
use Splode.ErrorClass, class: :ui
end
# 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
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
defmodule SystemError do
@moduledoc false
use Splode,
error_classes: [
hw: HwError,
sw: SwError
],
unknown_error: UnknownError
end
defmodule ContainerError do
@moduledoc false
use Splode,
error_classes: [
interaction: ContainerErrorClass,
hw: HwError,
sw: SwError
],
unknown_error: ContainerUnknownError,
merge_with: [SystemError]
end
defmodule ContainerWithoutMergeWith do
@moduledoc false
use Splode,
error_classes: [
interaction: ContainerErrorClass
],
unknown_error: ContainerUnknownError,
merge_with: []
end
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()
example_container_error = ExampleContainerError.exception() |> ContainerError.to_error()
%{
cpu: cpu,
ram: ram,
div: div,
null: null,
example_container_error: example_container_error
}
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
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()
interaction_error = ContainerError.to_class([hw_error, example_container_error])
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
hw_error = [cpu, ram] |> SystemError.to_class()
interaction_error = ContainerWithoutMergeWith.to_class([hw_error, example_container_error])
assert %{errors: [%SplodeTest.ContainerUnknownError{}, %SplodeTest.ContainerUnknownError{}]} =
interaction_error
end
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
end