From 46ecc11c0d4925bc011e0cf688b98af853de5145 Mon Sep 17 00:00:00 2001 From: Peter Solnica Date: Mon, 29 Jan 2024 08:54:28 +0100 Subject: [PATCH] Better union validation It's better to evaluate right side only if left side primitive check failed, because only this type of failure means that maybe we have an input that matches right-side specificiation. --- lib/drops/types/union.ex | 20 ++++++++++++++++++++ test/contract/type_test.exs | 5 +---- test/contract/types/custom_test.exs | 5 +---- test/contract/types/union_test.exs | 12 +++--------- 4 files changed, 25 insertions(+), 17 deletions(-) diff --git a/lib/drops/types/union.ex b/lib/drops/types/union.ex index 19f8cdf..8baed90 100644 --- a/lib/drops/types/union.ex +++ b/lib/drops/types/union.ex @@ -20,6 +20,26 @@ defmodule Drops.Types.Union do """ defmodule Validator do + def validate(%{left: %{primitive: _} = left, right: %{primitive: _} = right}, input) do + case Drops.Type.Validator.validate(left, input) do + {:ok, value} -> + {:ok, value} + + {:error, meta} = left_error -> + if is_list(meta) and meta[:predicate] != :type? do + left_error + else + case Drops.Type.Validator.validate(right, input) do + {:ok, value} -> + {:ok, value} + + {:error, _} = right_error -> + {:error, {:or, {left_error, right_error}}} + end + end + end + end + def validate(%{left: left, right: right}, input) do case Drops.Type.Validator.validate(left, input) do {:ok, value} -> diff --git a/test/contract/type_test.exs b/test/contract/type_test.exs index 5e6da15..8d777d9 100644 --- a/test/contract/type_test.exs +++ b/test/contract/type_test.exs @@ -76,10 +76,7 @@ defmodule Drops.Contract.TypeTest do end test "returns error from left predicates", %{contract: contract} do - assert_errors( - ["test must be filled or test must be a map"], - contract.conform(%{test: []}) - ) + assert_errors(["test must be filled"], contract.conform(%{test: []})) end test "returns errors from right predicates", %{contract: contract} do diff --git a/test/contract/types/custom_test.exs b/test/contract/types/custom_test.exs index 80580b2..9b72d32 100644 --- a/test/contract/types/custom_test.exs +++ b/test/contract/types/custom_test.exs @@ -88,10 +88,7 @@ defmodule Drops.Contract.Types.CustomTest do contract.conform(%{test: "Hello World"}) ) - assert_errors( - ["test must be greater than 0 or test must be a float"], - contract.conform(%{test: -1}) - ) + assert_errors(["test must be greater than 0"], contract.conform(%{test: -1})) end end diff --git a/test/contract/types/union_test.exs b/test/contract/types/union_test.exs index 566fdce..7bf3431 100644 --- a/test/contract/types/union_test.exs +++ b/test/contract/types/union_test.exs @@ -1,4 +1,4 @@ -defmodule Drops.Contract.Types.StringTest do +defmodule Drops.Contract.Types.UnionTest do use Drops.ContractCase describe "a union of two primitive types" do @@ -40,10 +40,7 @@ defmodule Drops.Contract.Types.StringTest do end test "returns error when left side is a failure", %{contract: contract} do - assert_errors( - ["test size must be 5 or test must be an integer"], - contract.conform(%{test: "Hello World"}) - ) + assert_errors(["test size must be 5"], contract.conform(%{test: "Hello World"})) end test "returns error when left side and right are failures", %{contract: contract} do @@ -100,10 +97,7 @@ defmodule Drops.Contract.Types.StringTest do end test "returns error when left side is a failure", %{contract: contract} do - assert_errors( - ["test size must be 5 or test must be an integer"], - contract.conform(%{test: "Hello World"}) - ) + assert_errors(["test size must be 5"], contract.conform(%{test: "Hello World"})) end test "returns success when right side is a failure", %{contract: contract} do