From 06c5adf10fb1369acd8515ff727b8b46f17488c2 Mon Sep 17 00:00:00 2001 From: Amichai Mantinband Date: Tue, 2 Jan 2024 20:32:36 +0200 Subject: [PATCH] Add Chain and ChainAsync --- src/ErrorOr.cs | 39 +- src/ErrorOrExtensions.cs | 19 + tests/ErrorOr.ChainAsyncTests.cs | 51 ++ tests/ErrorOr.ChainTests.cs | 47 ++ ...yncTests.cs => ErrorOr.MatchAsyncTests.cs} | 0 .../{MatchTests.cs => ErrorOr.MatchTests.cs} | 0 ...ncTests.cs => ErrorOr.SwitchAsyncTests.cs} | 0 ...{SwitchTests.cs => ErrorOr.SwitchTests.cs} | 0 tests/ErrorOrTests.cs | 694 +++++++++--------- 9 files changed, 491 insertions(+), 359 deletions(-) create mode 100644 src/ErrorOrExtensions.cs create mode 100644 tests/ErrorOr.ChainAsyncTests.cs create mode 100644 tests/ErrorOr.ChainTests.cs rename tests/{MatchAsyncTests.cs => ErrorOr.MatchAsyncTests.cs} (100%) rename tests/{MatchTests.cs => ErrorOr.MatchTests.cs} (100%) rename tests/{SwitchAsyncTests.cs => ErrorOr.SwitchAsyncTests.cs} (100%) rename tests/{SwitchTests.cs => ErrorOr.SwitchTests.cs} (100%) diff --git a/src/ErrorOr.cs b/src/ErrorOr.cs index eb99a8a..4656da8 100644 --- a/src/ErrorOr.cs +++ b/src/ErrorOr.cs @@ -259,21 +259,36 @@ public async Task MatchFirstAsync(Func> return await onValue(Value).ConfigureAwait(false); } -} -/// -/// Provides utility methods for creating instances of . -/// -public static class ErrorOr -{ /// - /// Creates an instance from a value. + /// If the state is a value, the provided function is executed and its result is returned. + /// + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public ErrorOr Chain(Func> onValue) + { + if (IsError) + { + return Errors; + } + + return onValue(Value); + } + + /// + /// If the state is a value, the provided function is executed asynchronously and its result is returned. /// - /// The type of the value. - /// The value from which to create an ErrorOr instance. - /// An instance containing the specified value. - public static ErrorOr From(TValue value) + /// The type of the result. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original . + public async Task> ChainAsync(Func>> onValue) { - return value; + if (IsError) + { + return Errors; + } + + return await onValue(Value); } } diff --git a/src/ErrorOrExtensions.cs b/src/ErrorOrExtensions.cs new file mode 100644 index 0000000..2aa1e45 --- /dev/null +++ b/src/ErrorOrExtensions.cs @@ -0,0 +1,19 @@ +namespace ErrorOr; + +public static class ErrorOrExtensions +{ + /// + /// If the state of is a value, the provided function is executed asynchronously and its result is returned. + /// + /// The type of the result. + /// The type of the next result. + /// The error. + /// The function to execute if the state is a value. + /// The result from calling if state is value; otherwise the original errors. + public static async Task> ChainAsync(this Task> errorOr, Func>> onValue) + { + var result = await errorOr; + + return await result.ChainAsync(onValue); + } +} diff --git a/tests/ErrorOr.ChainAsyncTests.cs b/tests/ErrorOr.ChainAsyncTests.cs new file mode 100644 index 0000000..39109f0 --- /dev/null +++ b/tests/ErrorOr.ChainAsyncTests.cs @@ -0,0 +1,51 @@ +using ErrorOr; +using FluentAssertions; + +namespace Tests; + +public class ChainAsyncTests +{ + record Person(string Name); + + [Fact] + public async Task ChainErrorOrsAsync_WhenStateIsValue_ShouldInvokeNextInChain() + { + // Arrange + ErrorOr errorOrPerson = new Person("Amichai"); + + static Task> GetNameAsync(Person person) => Task.FromResult(ErrorOrFactory.From(person.Name)); + static Task> CreatePersonFromNameAsync(string name) => Task.FromResult(ErrorOrFactory.From(new Person(name))); + + // Act + var result = await errorOrPerson + .ChainAsync(person => GetNameAsync(person)) + .ChainAsync(name => CreatePersonFromNameAsync(name)) + .ChainAsync(person => GetNameAsync(person)) + .ChainAsync(name => CreatePersonFromNameAsync(name)); + + // Assert + result.IsError.Should().BeFalse(); + result.Value.Should().BeEquivalentTo(errorOrPerson.Value); + } + + [Fact] + public async Task ChainErrorOrsAsync_WhenStateIsError_ShouldReturnErrors() + { + // Arrange + ErrorOr errorOrPerson = Error.NotFound(); + + static Task> GetNameAsync(Person person) => Task.FromResult(ErrorOrFactory.From(person.Name)); + static Task> CreatePersonFromNameAsync(string name) => Task.FromResult(ErrorOrFactory.From(new Person(name))); + + // Act + var result = await errorOrPerson + .ChainAsync(person => GetNameAsync(person)) + .ChainAsync(name => CreatePersonFromNameAsync(name)) + .ChainAsync(person => GetNameAsync(person)) + .ChainAsync(name => CreatePersonFromNameAsync(name)); + + // Assert + result.IsError.Should().BeTrue(); + result.FirstError.Should().BeEquivalentTo(Error.NotFound()); + } +} diff --git a/tests/ErrorOr.ChainTests.cs b/tests/ErrorOr.ChainTests.cs new file mode 100644 index 0000000..7d76221 --- /dev/null +++ b/tests/ErrorOr.ChainTests.cs @@ -0,0 +1,47 @@ +using ErrorOr; +using FluentAssertions; + +namespace Tests; + +public class ChainTests +{ + record Person(string Name); + + [Fact] + public void ChainErrorOrs_WhenHasValue_ShouldInvokeNextInChain() + { + // Arrange + ErrorOr errorOrPerson = new Person("Amichai"); + + static ErrorOr GetName(Person person) => person.Name; + static ErrorOr CreatePersonFromName(string name) => new Person(name); + + // Act + ErrorOr result = errorOrPerson + .Chain(person => GetName(person)) + .Chain(name => CreatePersonFromName(name)); + + // Assert + result.IsError.Should().BeFalse(); + result.Value.Should().BeEquivalentTo(errorOrPerson.Value); + } + + [Fact] + public void ChainErrorOrs_WhenHasError_ShouldReturnErrors() + { + // Arrange + ErrorOr errorOrPerson = Error.NotFound(); + + static ErrorOr GetName(Person person) => person.Name; + static ErrorOr CreatePersonFromName(string name) => new Person(name); + + // Act + ErrorOr result = errorOrPerson + .Chain(person => GetName(person)) + .Chain(name => CreatePersonFromName(name)); + + // Assert + result.IsError.Should().BeTrue(); + result.FirstError.Should().BeEquivalentTo(Error.NotFound()); + } +} diff --git a/tests/MatchAsyncTests.cs b/tests/ErrorOr.MatchAsyncTests.cs similarity index 100% rename from tests/MatchAsyncTests.cs rename to tests/ErrorOr.MatchAsyncTests.cs diff --git a/tests/MatchTests.cs b/tests/ErrorOr.MatchTests.cs similarity index 100% rename from tests/MatchTests.cs rename to tests/ErrorOr.MatchTests.cs diff --git a/tests/SwitchAsyncTests.cs b/tests/ErrorOr.SwitchAsyncTests.cs similarity index 100% rename from tests/SwitchAsyncTests.cs rename to tests/ErrorOr.SwitchAsyncTests.cs diff --git a/tests/SwitchTests.cs b/tests/ErrorOr.SwitchTests.cs similarity index 100% rename from tests/SwitchTests.cs rename to tests/ErrorOr.SwitchTests.cs diff --git a/tests/ErrorOrTests.cs b/tests/ErrorOrTests.cs index 963ba8d..db57aac 100644 --- a/tests/ErrorOrTests.cs +++ b/tests/ErrorOrTests.cs @@ -1,347 +1,347 @@ -namespace UnitTests; - -using ErrorOr; -using FluentAssertions; - -public class ErrorOrTests -{ - private record Person(string Name); - - [Fact] - public void CreateFromFactory_WhenAccessingValue_ShouldReturnValue() - { - // Arrange - IEnumerable value = new[] { "value" }; - - // Act - var errorOrPerson = ErrorOrFactory.From(value); - - // Assert - errorOrPerson.IsError.Should().BeFalse(); - errorOrPerson.Value.Should().BeSameAs(value); - } - - [Fact] - public void CreateFromFactory_WhenAccessingErrors_ShouldReturnUnexpectedError() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOrFactory.From(value); - - // Act - var errors = errorOrPerson.Errors; - - // Assert - errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void CreateFromFactory_WhenAccessingErrorsOrEmptyList_ShouldReturnEmptyList() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOrFactory.From(value); - - // Act - var errors = errorOrPerson.ErrorsOrEmptyList; - - // Assert - errors.Should().BeEmpty(); - } - - [Fact] - public void CreateFromFactory_WhenAccessingFirstError_ShouldReturnUnexpectedError() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOrFactory.From(value); - - // Act - var firstError = errorOrPerson.FirstError; - - // Assert - firstError.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void CreateFromValue_WhenAccessingValue_ShouldReturnValue() - { - // Arrange - IEnumerable value = new[] { "value" }; - - // Act - var errorOrPerson = ErrorOr.From(value); - - // Assert - errorOrPerson.IsError.Should().BeFalse(); - errorOrPerson.Value.Should().BeSameAs(value); - } - - [Fact] - public void CreateFromValue_WhenAccessingErrors_ShouldReturnUnexpectedError() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOr.From(value); - - // Act - var errors = errorOrPerson.Errors; - - // Assert - errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void CreateFromValue_WhenAccessingErrorsOrEmptyList_ShouldReturnEmptyList() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOr.From(value); - - // Act - var errors = errorOrPerson.ErrorsOrEmptyList; - - // Assert - errors.Should().BeEmpty(); - } - - [Fact] - public void CreateFromValue_WhenAccessingFirstError_ShouldReturnUnexpectedError() - { - // Arrange - IEnumerable value = new[] { "value" }; - var errorOrPerson = ErrorOr.From(value); - - // Act - var firstError = errorOrPerson.FirstError; - - // Assert - firstError.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void CreateFromErrorList_WhenAccessingErrors_ShouldReturnErrorList() - { - // Arrange - var errors = new List { Error.Validation("User.Name", "Name is too short") }; - var errorOrPerson = ErrorOr.From(errors); - - // Act & Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.Errors.Should().ContainSingle().Which.Should().Be(errors.Single()); - } - - [Fact] - public void CreateFromErrorList_WhenAccessingErrorsOrEmptyList_ShouldReturnErrorList() - { - // Arrange - var errors = new List { Error.Validation("User.Name", "Name is too short") }; - var errorOrPerson = ErrorOr.From(errors); - - // Act & Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.ErrorsOrEmptyList.Should().ContainSingle().Which.Should().Be(errors.Single()); - } - - [Fact] - public void CreateFromErrorList_WhenAccessingValue_ShouldReturnDefault() - { - // Arrange - var errors = new List { Error.Validation("User.Name", "Name is too short") }; - var errorOrPerson = ErrorOr.From(errors); - - // Act - var value = errorOrPerson.Value; - - // Assert - value.Should().Be(default); - } - - [Fact] - public void ImplicitCastResult_WhenAccessingResult_ShouldReturnValue() - { - // Arrange - var result = new Person("Amichai"); - - // Act - ErrorOr errorOr = result; - - // Assert - errorOr.IsError.Should().BeFalse(); - errorOr.Value.Should().Be(result); - } - - [Fact] - public void ImplicitCastResult_WhenAccessingErrors_ShouldReturnUnexpectedError() - { - ErrorOr errorOrPerson = new Person("Amichai"); - - // Act - var errors = errorOrPerson.Errors; - - // Assert - errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void ImplicitCastResult_WhenAccessingFirstError_ShouldReturnUnexpectedError() - { - ErrorOr errorOrPerson = new Person("Amichai"); - - // Act - var firstError = errorOrPerson.FirstError; - - // Assert - firstError.Type.Should().Be(ErrorType.Unexpected); - } - - [Fact] - public void ImplicitCastPrimitiveResult_WhenAccessingResult_ShouldReturnValue() - { - // Arrange - const int result = 4; - - // Act - ErrorOr errorOrInt = result; - - // Assert - errorOrInt.IsError.Should().BeFalse(); - errorOrInt.Value.Should().Be(result); - } - - [Fact] - public void ImplicitCastErrorOrType_WhenAccessingResult_ShouldReturnValue() - { - // Act - ErrorOr errorOrSuccess = Result.Success; - ErrorOr errorOrCreated = Result.Created; - ErrorOr errorOrDeleted = Result.Deleted; - ErrorOr errorOrUpdated = Result.Updated; - - // Assert - errorOrSuccess.IsError.Should().BeFalse(); - errorOrSuccess.Value.Should().Be(Result.Success); - - errorOrCreated.IsError.Should().BeFalse(); - errorOrCreated.Value.Should().Be(Result.Created); - - errorOrDeleted.IsError.Should().BeFalse(); - errorOrDeleted.Value.Should().Be(Result.Deleted); - - errorOrUpdated.IsError.Should().BeFalse(); - errorOrUpdated.Value.Should().Be(Result.Updated); - } - - [Fact] - public void ImplicitCastSingleError_WhenAccessingErrors_ShouldReturnErrorList() - { - // Arrange - var error = Error.Validation("User.Name", "Name is too short"); - - // Act - ErrorOr errorOrPerson = error; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.Errors.Should().ContainSingle().Which.Should().Be(error); - } - - [Fact] - public void ImplicitCastError_WhenAccessingValue_ShouldReturnDefault() - { - // Arrange - ErrorOr errorOrPerson = Error.Validation("User.Name", "Name is too short"); - - // Act - var value = errorOrPerson.Value; - - // Assert - value.Should().Be(default); - } - - [Fact] - public void ImplicitCastSingleError_WhenAccessingFirstError_ShouldReturnError() - { - // Arrange - var error = Error.Validation("User.Name", "Name is too short"); - - // Act - ErrorOr errorOrPerson = error; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.FirstError.Should().Be(error); - } - - [Fact] - public void ImplicitCastErrorList_WhenAccessingErrors_ShouldReturnErrorList() - { - // Arrange - var errors = new List - { - Error.Validation("User.Name", "Name is too short"), - Error.Validation("User.Age", "User is too young"), - }; - - // Act - ErrorOr errorOrPerson = errors; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.Errors.Should().HaveCount(errors.Count).And.BeEquivalentTo(errors); - } - - [Fact] - public void ImplicitCastErrorArray_WhenAccessingErrors_ShouldReturnErrorArray() - { - // Arrange - var errors = new[] - { - Error.Validation("User.Name", "Name is too short"), - Error.Validation("User.Age", "User is too young"), - }; - - // Act - ErrorOr errorOrPerson = errors; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.Errors.Should().HaveCount(errors.Length).And.BeEquivalentTo(errors); - } - - [Fact] - public void ImplicitCastErrorList_WhenAccessingFirstError_ShouldReturnFirstError() - { - // Arrange - var errors = new List - { - Error.Validation("User.Name", "Name is too short"), - Error.Validation("User.Age", "User is too young"), - }; - - // Act - ErrorOr errorOrPerson = errors; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.FirstError.Should().Be(errors[0]); - } - - [Fact] - public void ImplicitCastErrorArray_WhenAccessingFirstError_ShouldReturnFirstError() - { - // Arrange - var errors = new[] - { - Error.Validation("User.Name", "Name is too short"), - Error.Validation("User.Age", "User is too young"), - }; - - // Act - ErrorOr errorOrPerson = errors; - - // Assert - errorOrPerson.IsError.Should().BeTrue(); - errorOrPerson.FirstError.Should().Be(errors[0]); - } -} +namespace UnitTests; + +using ErrorOr; +using FluentAssertions; + +public class ErrorOrTests +{ + private record Person(string Name); + + [Fact] + public void CreateFromFactory_WhenAccessingValue_ShouldReturnValue() + { + // Arrange + IEnumerable value = new[] { "value" }; + + // Act + var errorOrPerson = ErrorOrFactory.From(value); + + // Assert + errorOrPerson.IsError.Should().BeFalse(); + errorOrPerson.Value.Should().BeSameAs(value); + } + + [Fact] + public void CreateFromFactory_WhenAccessingErrors_ShouldReturnUnexpectedError() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var errors = errorOrPerson.Errors; + + // Assert + errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void CreateFromFactory_WhenAccessingErrorsOrEmptyList_ShouldReturnEmptyList() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var errors = errorOrPerson.ErrorsOrEmptyList; + + // Assert + errors.Should().BeEmpty(); + } + + [Fact] + public void CreateFromFactory_WhenAccessingFirstError_ShouldReturnUnexpectedError() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var firstError = errorOrPerson.FirstError; + + // Assert + firstError.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void CreateFromValue_WhenAccessingValue_ShouldReturnValue() + { + // Arrange + IEnumerable value = new[] { "value" }; + + // Act + var errorOrPerson = ErrorOrFactory.From(value); + + // Assert + errorOrPerson.IsError.Should().BeFalse(); + errorOrPerson.Value.Should().BeSameAs(value); + } + + [Fact] + public void CreateFromValue_WhenAccessingErrors_ShouldReturnUnexpectedError() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var errors = errorOrPerson.Errors; + + // Assert + errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void CreateFromValue_WhenAccessingErrorsOrEmptyList_ShouldReturnEmptyList() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var errors = errorOrPerson.ErrorsOrEmptyList; + + // Assert + errors.Should().BeEmpty(); + } + + [Fact] + public void CreateFromValue_WhenAccessingFirstError_ShouldReturnUnexpectedError() + { + // Arrange + IEnumerable value = new[] { "value" }; + var errorOrPerson = ErrorOrFactory.From(value); + + // Act + var firstError = errorOrPerson.FirstError; + + // Assert + firstError.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void CreateFromErrorList_WhenAccessingErrors_ShouldReturnErrorList() + { + // Arrange + var errors = new List { Error.Validation("User.Name", "Name is too short") }; + var errorOrPerson = ErrorOr.From(errors); + + // Act & Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.Errors.Should().ContainSingle().Which.Should().Be(errors.Single()); + } + + [Fact] + public void CreateFromErrorList_WhenAccessingErrorsOrEmptyList_ShouldReturnErrorList() + { + // Arrange + var errors = new List { Error.Validation("User.Name", "Name is too short") }; + var errorOrPerson = ErrorOr.From(errors); + + // Act & Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.ErrorsOrEmptyList.Should().ContainSingle().Which.Should().Be(errors.Single()); + } + + [Fact] + public void CreateFromErrorList_WhenAccessingValue_ShouldReturnDefault() + { + // Arrange + var errors = new List { Error.Validation("User.Name", "Name is too short") }; + var errorOrPerson = ErrorOr.From(errors); + + // Act + var value = errorOrPerson.Value; + + // Assert + value.Should().Be(default); + } + + [Fact] + public void ImplicitCastResult_WhenAccessingResult_ShouldReturnValue() + { + // Arrange + var result = new Person("Amichai"); + + // Act + ErrorOr errorOr = result; + + // Assert + errorOr.IsError.Should().BeFalse(); + errorOr.Value.Should().Be(result); + } + + [Fact] + public void ImplicitCastResult_WhenAccessingErrors_ShouldReturnUnexpectedError() + { + ErrorOr errorOrPerson = new Person("Amichai"); + + // Act + var errors = errorOrPerson.Errors; + + // Assert + errors.Should().ContainSingle().Which.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void ImplicitCastResult_WhenAccessingFirstError_ShouldReturnUnexpectedError() + { + ErrorOr errorOrPerson = new Person("Amichai"); + + // Act + var firstError = errorOrPerson.FirstError; + + // Assert + firstError.Type.Should().Be(ErrorType.Unexpected); + } + + [Fact] + public void ImplicitCastPrimitiveResult_WhenAccessingResult_ShouldReturnValue() + { + // Arrange + const int result = 4; + + // Act + ErrorOr errorOrInt = result; + + // Assert + errorOrInt.IsError.Should().BeFalse(); + errorOrInt.Value.Should().Be(result); + } + + [Fact] + public void ImplicitCastErrorOrType_WhenAccessingResult_ShouldReturnValue() + { + // Act + ErrorOr errorOrSuccess = Result.Success; + ErrorOr errorOrCreated = Result.Created; + ErrorOr errorOrDeleted = Result.Deleted; + ErrorOr errorOrUpdated = Result.Updated; + + // Assert + errorOrSuccess.IsError.Should().BeFalse(); + errorOrSuccess.Value.Should().Be(Result.Success); + + errorOrCreated.IsError.Should().BeFalse(); + errorOrCreated.Value.Should().Be(Result.Created); + + errorOrDeleted.IsError.Should().BeFalse(); + errorOrDeleted.Value.Should().Be(Result.Deleted); + + errorOrUpdated.IsError.Should().BeFalse(); + errorOrUpdated.Value.Should().Be(Result.Updated); + } + + [Fact] + public void ImplicitCastSingleError_WhenAccessingErrors_ShouldReturnErrorList() + { + // Arrange + var error = Error.Validation("User.Name", "Name is too short"); + + // Act + ErrorOr errorOrPerson = error; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.Errors.Should().ContainSingle().Which.Should().Be(error); + } + + [Fact] + public void ImplicitCastError_WhenAccessingValue_ShouldReturnDefault() + { + // Arrange + ErrorOr errorOrPerson = Error.Validation("User.Name", "Name is too short"); + + // Act + var value = errorOrPerson.Value; + + // Assert + value.Should().Be(default); + } + + [Fact] + public void ImplicitCastSingleError_WhenAccessingFirstError_ShouldReturnError() + { + // Arrange + var error = Error.Validation("User.Name", "Name is too short"); + + // Act + ErrorOr errorOrPerson = error; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.FirstError.Should().Be(error); + } + + [Fact] + public void ImplicitCastErrorList_WhenAccessingErrors_ShouldReturnErrorList() + { + // Arrange + var errors = new List + { + Error.Validation("User.Name", "Name is too short"), + Error.Validation("User.Age", "User is too young"), + }; + + // Act + ErrorOr errorOrPerson = errors; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.Errors.Should().HaveCount(errors.Count).And.BeEquivalentTo(errors); + } + + [Fact] + public void ImplicitCastErrorArray_WhenAccessingErrors_ShouldReturnErrorArray() + { + // Arrange + var errors = new[] + { + Error.Validation("User.Name", "Name is too short"), + Error.Validation("User.Age", "User is too young"), + }; + + // Act + ErrorOr errorOrPerson = errors; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.Errors.Should().HaveCount(errors.Length).And.BeEquivalentTo(errors); + } + + [Fact] + public void ImplicitCastErrorList_WhenAccessingFirstError_ShouldReturnFirstError() + { + // Arrange + var errors = new List + { + Error.Validation("User.Name", "Name is too short"), + Error.Validation("User.Age", "User is too young"), + }; + + // Act + ErrorOr errorOrPerson = errors; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.FirstError.Should().Be(errors[0]); + } + + [Fact] + public void ImplicitCastErrorArray_WhenAccessingFirstError_ShouldReturnFirstError() + { + // Arrange + var errors = new[] + { + Error.Validation("User.Name", "Name is too short"), + Error.Validation("User.Age", "User is too young"), + }; + + // Act + ErrorOr errorOrPerson = errors; + + // Assert + errorOrPerson.IsError.Should().BeTrue(); + errorOrPerson.FirstError.Should().Be(errors[0]); + } +}