diff --git a/.github/workflows/continues_integration.yml b/.github/workflows/continues_integration.yml
index ace20ba..ef274f8 100644
--- a/.github/workflows/continues_integration.yml
+++ b/.github/workflows/continues_integration.yml
@@ -26,4 +26,4 @@ jobs:
- name: Build
run: dotnet build Ghanavats.ResultPattern.sln --configuration Release
- name: Test
- run: dotnet test --no-build --verbosity normal
+ run: dotnet test tests/Ghanavats.ResultPattern.Tests/Ghanavats.ResultPattern.Tests.csproj
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 94f7a3d..cc32700 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,6 +1,17 @@
-
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
\ No newline at end of file
diff --git a/Ghanavats.ResultPattern.sln b/Ghanavats.ResultPattern.sln
index 6bb330a..2170bc8 100644
--- a/Ghanavats.ResultPattern.sln
+++ b/Ghanavats.ResultPattern.sln
@@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt
README.md = README.md
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ghanavats.ResultPattern.Tests", "tests\Ghanavats.ResultPattern.Tests\Ghanavats.ResultPattern.Tests.csproj", "{857C6761-2A3B-4919-8FA7-433E2B2157B4}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -23,8 +25,13 @@ Global
{654CA5E2-972C-4561-BA0E-B532E1327A15}.Debug|Any CPU.Build.0 = Debug|Any CPU
{654CA5E2-972C-4561-BA0E-B532E1327A15}.Release|Any CPU.ActiveCfg = Release|Any CPU
{654CA5E2-972C-4561-BA0E-B532E1327A15}.Release|Any CPU.Build.0 = Release|Any CPU
+ {857C6761-2A3B-4919-8FA7-433E2B2157B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {857C6761-2A3B-4919-8FA7-433E2B2157B4}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {857C6761-2A3B-4919-8FA7-433E2B2157B4}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {857C6761-2A3B-4919-8FA7-433E2B2157B4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{654CA5E2-972C-4561-BA0E-B532E1327A15} = {211150B0-9A81-462E-8D30-C00B45504AD6}
+ {857C6761-2A3B-4919-8FA7-433E2B2157B4} = {78FEF537-F8DF-4722-BEB6-091C57BA134A}
EndGlobalSection
EndGlobal
diff --git a/README.md b/README.md
index 8803b65..be5e6f8 100644
--- a/README.md
+++ b/README.md
@@ -2,11 +2,15 @@
A Robust and Flexible Result Handling Framework for Modern Applications
## Overview
-Ghanavats.ResultPattern is a comprehensive framework designed to bring consistency
+Ghanavats.ResultPattern is a small/simple, unambitious framework designed to bring consistency
and flexibility to result handling in your applications.
Whether you need to return simple success/error outcomes or handle complex validation scenarios,
this solution provides the tools you need to implement robust result patterns effortlessly.
+The idea behind this was to learn the Result Pattern by implementing it in a good way.
+The plan was not and is not to get all developers, worldwide, to adopt it.
+Instead, to teach myself, and maybe if I am lucky, to show you what it may look like.
+
## The solution includes
1. **Ghanavats.ResultPattern NuGet Package**
diff --git a/src/Ghanavats.ResultPattern/Extensions/FluentValidationFailureExtension.cs b/src/Ghanavats.ResultPattern/Extensions/FluentValidationFailureExtension.cs
index 486b25a..a2122f7 100644
--- a/src/Ghanavats.ResultPattern/Extensions/FluentValidationFailureExtension.cs
+++ b/src/Ghanavats.ResultPattern/Extensions/FluentValidationFailureExtension.cs
@@ -10,6 +10,11 @@ public static class FluentValidationFailureExtension
{
public static IEnumerable PopulateValidationErrors(this ValidationResult? input)
{
- return input is null ? [] : input.Errors.Select(x => new ValidationError(x.ErrorMessage, x.ErrorCode, (ValidationErrorType)x.Severity));
+ return input is null
+ ? []
+ : input.Errors
+ .Select(validationFailure => new ValidationError(validationFailure.ErrorMessage,
+ validationFailure.ErrorCode,
+ (ValidationErrorType)validationFailure.Severity));
}
}
diff --git a/src/Ghanavats.ResultPattern/Extensions/ResultExtensions.cs b/src/Ghanavats.ResultPattern/Extensions/ResultExtensions.cs
new file mode 100644
index 0000000..5b26eeb
--- /dev/null
+++ b/src/Ghanavats.ResultPattern/Extensions/ResultExtensions.cs
@@ -0,0 +1,53 @@
+using Ghanavats.ResultPattern.Enums;
+
+namespace Ghanavats.ResultPattern.Extensions;
+
+public static class ResultExtensions
+{
+ internal static IReadOnlyCollection GetMessages(this Result[] results, ResultStatus status,
+ bool includeValidationErrors = false)
+ {
+ switch (status)
+ {
+ case ResultStatus.Error:
+ return GetErrorMessages();
+ case ResultStatus.Invalid:
+ return includeValidationErrors
+ ? GetValidationErrors()
+ : GetValidationMessages();
+ case ResultStatus.Ok:
+ case ResultStatus.None:
+ case ResultStatus.NotFound:
+ throw new NotSupportedException("Not supported result status.");
+ default:
+ throw new InvalidOperationException("Operation not supported.");
+ }
+
+ IReadOnlyCollection GetErrorMessages()
+ {
+ var resultErrors = results
+ .Where(result => result.Status == ResultStatus.Error
+ && result.ErrorMessages.Any())
+ .SelectMany(error => error.ErrorMessages).ToList();
+
+ return resultErrors;
+ }
+
+ IReadOnlyCollection GetValidationMessages()
+ {
+ return results
+ .Where(invalids => invalids.ValidationErrors.Any()
+ && invalids.Status == ResultStatus.Invalid)
+ .SelectMany(invalidMessages => invalidMessages.ValidationErrors
+ .Select(x => x.ErrorMessage)).ToList();
+ }
+
+ IReadOnlyCollection GetValidationErrors()
+ {
+ return results
+ .Where(invalids => invalids.ValidationErrors.Any()
+ && invalids.Status == ResultStatus.Invalid)
+ .SelectMany(invalidMessages => invalidMessages.ValidationErrors).ToList();
+ }
+ }
+}
diff --git a/src/Ghanavats.ResultPattern/Ghanavats.ResultPattern.csproj b/src/Ghanavats.ResultPattern/Ghanavats.ResultPattern.csproj
index 587de89..ef04473 100644
--- a/src/Ghanavats.ResultPattern/Ghanavats.ResultPattern.csproj
+++ b/src/Ghanavats.ResultPattern/Ghanavats.ResultPattern.csproj
@@ -12,7 +12,7 @@
result-pattern
git
https://github.com/ghanavat/ResultPattern
- 1.0.2
+ 1.1.0
README.md
LICENSE
diff --git a/src/Ghanavats.ResultPattern/Models/AggregateResultsModel.cs b/src/Ghanavats.ResultPattern/Models/AggregateResultsModel.cs
new file mode 100644
index 0000000..3d9875a
--- /dev/null
+++ b/src/Ghanavats.ResultPattern/Models/AggregateResultsModel.cs
@@ -0,0 +1,9 @@
+using Ghanavats.ResultPattern.Enums;
+
+namespace Ghanavats.ResultPattern.Models;
+
+public class AggregateResultsModel
+{
+ public ResultStatus Status { get; init; }
+ public IReadOnlyCollection Messages { get; init; } = [];
+}
diff --git a/src/Ghanavats.ResultPattern/Result.Void.cs b/src/Ghanavats.ResultPattern/Result.Void.cs
index c31e0e6..3d6fa94 100644
--- a/src/Ghanavats.ResultPattern/Result.Void.cs
+++ b/src/Ghanavats.ResultPattern/Result.Void.cs
@@ -1,4 +1,6 @@
using Ghanavats.ResultPattern.Enums;
+using Ghanavats.ResultPattern.Extensions;
+using Ghanavats.ResultPattern.Models;
namespace Ghanavats.ResultPattern;
@@ -10,15 +12,19 @@ public class Result : Result
{
///
/// Private constructor that is used in this class.
- /// No need to set Status here as its default id OK.
+ /// No need to set Status here as its default is OK.
///
- private Result() { }
+ private Result()
+ {
+ }
///
/// A constructor that accepts
///
///
- private Result(ResultStatus status) : base(status) { }
+ private Result(ResultStatus status) : base(status)
+ {
+ }
///
/// Represents a successful operation without a return type
@@ -47,4 +53,30 @@ private Result(ResultStatus status) : base(status) { }
{
ErrorMessages = [errorMessage]
};
+
+ ///
+ /// Represents the not found result for non-generic scenarios
+ ///
+ /// Result with NotFound status
+ public new static Result NotFound() => new(ResultStatus.NotFound);
+
+ ///
+ /// To gather all non-success results of type Result (non-generic) into a single object.
+ ///
+ /// Indicates whether a full ValidationError collection should be aggregated.
+ /// Default is false.
+ /// Array of all Results (non-generic)
+ /// A Read-Only Collection of Aggregate Results Model
+ public static IReadOnlyCollection Aggregate(bool includeValidationErrors = false,
+ params Result[] results)
+ {
+ return results
+ .GroupBy(result => result.Status)
+ .Skip(results.Count(x => x.Status is ResultStatus.Ok or ResultStatus.NotFound))
+ .Select(whatIWant => new AggregateResultsModel
+ {
+ Status = whatIWant.Key,
+ Messages = results.GetMessages(whatIWant.Key, includeValidationErrors)
+ }).ToList().AsReadOnly();
+ }
}
diff --git a/src/Ghanavats.ResultPattern/Result.cs b/src/Ghanavats.ResultPattern/Result.cs
index bdf4b6a..ac3fd86 100644
--- a/src/Ghanavats.ResultPattern/Result.cs
+++ b/src/Ghanavats.ResultPattern/Result.cs
@@ -1,6 +1,9 @@
-using System.Text.Json.Serialization;
+using System.Runtime.CompilerServices;
+using System.Text.Json.Serialization;
using Ghanavats.ResultPattern.Enums;
+[assembly: InternalsVisibleTo(assemblyName: "Ghanavats.ResultPattern.Tests")]
+
namespace Ghanavats.ResultPattern;
///
@@ -10,6 +13,15 @@ namespace Ghanavats.ResultPattern;
///
public class Result
{
+ ///
+ /// A constructor that accepts .
+ ///
+ /// Constructor parameter of type
+ public Result(T data)
+ {
+ Data = data;
+ }
+
///
/// A constructor that accepts .
/// It is used internally in this class and Result.Void
@@ -19,24 +31,15 @@ internal Result(ResultStatus status)
{
Status = status;
}
-
+
///
/// Default protected constructor.
///
///
- /// It is used in Result.Void to return an instance of Success status without needing to pass any extra types.
+ /// It is used in Result.Void to return an instance of
+ /// Success status without needing to pass any extra types.
///
protected internal Result() { }
-
- ///
- /// A constructor that accepts .
- /// It is used internally by this class.
- ///
- /// Constructor parameter of type
- private Result(T data)
- {
- Data = data;
- }
///
/// A constructor that accepts .
@@ -44,8 +47,9 @@ private Result(T data)
///
/// Constructor parameter of type
/// Constructor parameter of type
- private Result(T data, string successMessage) : this(data)
+ internal Result(T data, string successMessage)
{
+ Data = data;
SuccessMessage = successMessage;
}
@@ -62,7 +66,7 @@ private Result(T data, string successMessage) : this(data)
public T? Data { get; set; }
///
- /// Use this property to accurately determine the exact status of the Result
+ /// Use this property to accurately determine the exact Result status
///
[JsonInclude]
public ResultStatus Status { get; protected set; } = ResultStatus.Ok;
@@ -137,7 +141,7 @@ public static Result Invalid(IEnumerable validationErrors)
}
///
- /// An operator to automatically convert the return type in a method to the type being returned
+ /// An operator to automatically convert the generic T type to Result of type T
///
/// The return data
public static implicit operator Result(T data) => new(data);
diff --git a/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyFeatureService.cs b/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyFeatureService.cs
new file mode 100644
index 0000000..d3cebbc
--- /dev/null
+++ b/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyFeatureService.cs
@@ -0,0 +1,34 @@
+namespace Ghanavats.ResultPattern.Tests.DummyData;
+
+public class DummyFeatureService
+{
+ public Result DoSomething()
+ {
+ var response = new DummyResponseModel
+ {
+ DummyFieldOne = "Some data"
+ };
+
+ return response;
+ }
+
+ public DummyResponseModel DoSomethingImplicitConversionOfResultGenericToInnerType()
+ {
+ var response = new DummyResponseModel
+ {
+ DummyFieldOne = "Test Field Data"
+ };
+
+ return Result.Success(response);
+ }
+
+ public Result DoSomethingImplicitConversionOfNonGenericResultToGenericResultWithDefault()
+ {
+ _ = new DummyResponseModel
+ {
+ DummyFieldOne = "DataNotGoingToBeUsed"
+ };
+
+ return Result.Success();
+ }
+}
diff --git a/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyResponseModel.cs b/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyResponseModel.cs
new file mode 100644
index 0000000..353f306
--- /dev/null
+++ b/tests/Ghanavats.ResultPattern.Tests/DummyData/DummyResponseModel.cs
@@ -0,0 +1,6 @@
+namespace Ghanavats.ResultPattern.Tests.DummyData;
+
+public class DummyResponseModel
+{
+ public string DummyFieldOne { get; set; } = string.Empty;
+}
diff --git a/tests/Ghanavats.ResultPattern.Tests/Ghanavats.ResultPattern.Tests.csproj b/tests/Ghanavats.ResultPattern.Tests/Ghanavats.ResultPattern.Tests.csproj
new file mode 100644
index 0000000..72b3319
--- /dev/null
+++ b/tests/Ghanavats.ResultPattern.Tests/Ghanavats.ResultPattern.Tests.csproj
@@ -0,0 +1,24 @@
+
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/Ghanavats.ResultPattern.Tests/ResultTests.cs b/tests/Ghanavats.ResultPattern.Tests/ResultTests.cs
new file mode 100644
index 0000000..e0ca21a
--- /dev/null
+++ b/tests/Ghanavats.ResultPattern.Tests/ResultTests.cs
@@ -0,0 +1,467 @@
+using FluentValidation;
+using FluentValidation.Results;
+using Ghanavats.ResultPattern.Enums;
+using Ghanavats.ResultPattern.Extensions;
+using Ghanavats.ResultPattern.Tests.DummyData;
+using Shouldly;
+
+namespace Ghanavats.ResultPattern.Tests;
+
+public class ResultTests
+{
+ [Fact]
+ public void ResultConstructorWithData_ShouldBeInitialised_WithCorrectValueAndStatus()
+ {
+ //Arrange/Act
+ var sut = new Result(1234);
+
+ //Assert
+ sut.ShouldNotBeNull();
+ sut.Status.ShouldBe(ResultStatus.Ok);
+ }
+
+ [Fact]
+ public void ResultConstructorWithStatus_ShouldBeInitialised_WithCorrectValueAndStatus()
+ {
+ //Arrange/Act
+ var sut = new Result(ResultStatus.Ok);
+
+ //Assert
+ sut.ShouldNotBeNull();
+ sut.IsSuccess.ShouldBeTrue();
+ sut.Status.ShouldBe(ResultStatus.Ok);
+ }
+
+ [Fact]
+ public void ResultSuccessWithDataAndSuccessMessage_ShouldCorrectlySetStatusWithData()
+ {
+ //arrange/act
+ var actual = Result.Success("SomeValue", "Successfully executed.");
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Ok);
+ actual.Data.ShouldNotBeNull();
+ actual.Data.GetType().Name.ShouldBe("String");
+ actual.IsSuccess.ShouldBeTrue();
+ actual.SuccessMessage.ShouldBe("Successfully executed.");
+ }
+
+ [Fact]
+ public void ResultSuccessWithData_ShouldCorrectlySetStatusWithData()
+ {
+ //arrange/act
+ var actual = Result.Success(1234);
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Ok);
+ actual.Data.ShouldBe(1234);
+ actual.IsSuccess.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void ResultNonGenericSuccessWithDataAndSuccessMessage_ShouldCorrectlySetStatusWithData()
+ {
+ //arrange/act
+ var actual = Result.Success();
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Ok);
+ actual.Data.ShouldBeNull();
+ actual.IsSuccess.ShouldBeTrue();
+ }
+
+ [Fact]
+ public void ResultError_ShouldCorrectlySetStatusWithNullData()
+ {
+ //arrange/act
+ var actual = Result.Error("Something wrong happened.");
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ErrorMessages.ShouldNotBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Error);
+ actual.Data.ShouldBeNull();
+ actual.IsSuccess.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void ResultNonGenericInvalid_ShouldCorrectlySetStatusAndValidationErrors()
+ {
+ //arrange/act
+ var expectedValidationResult = new ValidationResult
+ {
+ Errors = [
+ new ValidationFailure
+ {
+ ErrorMessage = "Test validation failure message.",
+ ErrorCode = "ErrorCodeTest",
+ PropertyName = "TestProperty",
+ Severity = Severity.Error
+ }
+ ]
+ };
+
+ var actual = Result.Invalid(expectedValidationResult.PopulateValidationErrors());
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ValidationErrors.ShouldNotBeEmpty();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Invalid);
+ actual.Data.ShouldBeNull();
+ actual.IsSuccess.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void ResultInvalid_ShouldCorrectlySetStatusAndValidationErrors()
+ {
+ //arrange/act
+ var expectedValidationResult = new ValidationResult
+ {
+ Errors = [
+ new ValidationFailure
+ {
+ ErrorMessage = "Test validation failure message.",
+ ErrorCode = "ErrorCodeTest",
+ PropertyName = "DummyFieldOne",
+ Severity = Severity.Error
+ }
+ ]
+ };
+
+ var actual = Result.Invalid(expectedValidationResult.PopulateValidationErrors());
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ValidationErrors.ShouldNotBeEmpty();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Invalid);
+ actual.Data.ShouldBeNull();
+ actual.IsSuccess.ShouldBeFalse();
+
+ foreach (var item in actual.ValidationErrors)
+ {
+ item.ErrorMessage.ShouldNotBeNullOrEmpty();
+ item.ErrorCode.ShouldNotBeNullOrEmpty();
+ }
+ }
+
+ [Fact]
+ public void ResultNotFound_ShouldCorrectlySetStatus()
+ {
+ //arrange/act
+ var actual = Result.NotFound();
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.Status.ShouldBe(ResultStatus.NotFound);
+ actual.Data.ShouldBeNull();
+ actual.IsSuccess.ShouldBeFalse();
+ }
+
+ [Fact]
+ public void Result_ShouldAggregateAllResults_WhenItIsCalledWithMultipleResults_AndIncludeValidationErrorsIsFalse()
+ {
+ //arrange
+ var result1 = Result.Invalid([new ValidationError("Validation error Jan", "3434", ValidationErrorType.Error)]);
+ var result2 = Result.Invalid([new ValidationError("Validation error Feb", "5554", ValidationErrorType.Error)]);
+ var result3 = Result.Invalid([new ValidationError("Validation error March", "6678", ValidationErrorType.Error)]);
+
+ var result4 = Result.Error("Failure Error 234");
+ var result5 = Result.Error("Failure Error 2676");
+ var result6 = Result.Error("Failure Error 3009");
+
+ //act
+ var actual = Result.Aggregate(false, result1, result2, result3, result4, result5, result6);
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldNotBeEmpty();
+ actual.Count.ShouldBe(2);
+
+ actual.ToList().Where(x => x.Status == ResultStatus.Error).ShouldNotBeEmpty();
+ actual.ToList().Where(x => x.Status == ResultStatus.Invalid).ShouldNotBeEmpty();
+
+ foreach (var item in actual.ToList())
+ {
+ item.Messages.ShouldNotBeEmpty();
+ item.Messages.ShouldBeAssignableTo>();
+ }
+ }
+
+ [Fact]
+ public void Result_ShouldAggregateAllResults_WhenItIsCalledWithMultipleResults_AndIncludeValidationErrorsIsTrue()
+ {
+ //arrange
+ var result1 = Result.Invalid([new ValidationError("Validation error April", "666", ValidationErrorType.Error)]);
+ var result2 = Result.Invalid([new ValidationError("Validation error May", "777", ValidationErrorType.Error)]);
+ var result3 = Result.Invalid([new ValidationError("Validation error June", "888", ValidationErrorType.Error)]);
+
+ var result4 = Result.Error("Failure Error 999");
+ var result5 = Result.Error("Failure Error 101010");
+ var result6 = Result.Error("Failure Error 111111");
+
+ //act
+ var actual = Result.Aggregate(true, result1, result2, result3, result4, result5, result6);
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldNotBeEmpty();
+ actual.Count.ShouldBe(2);
+
+ actual.ToList().Where(x => x.Status == ResultStatus.Error).ShouldNotBeEmpty();
+ actual.ToList().Where(x => x.Status == ResultStatus.Invalid).ShouldNotBeEmpty();
+
+ var validationErrors = actual.Where(x => x.Status == ResultStatus.Invalid)
+ .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+
+ validationErrors.ShouldNotBeEmpty();
+
+ foreach (var item in validationErrors)
+ {
+ item.ShouldBeAssignableTo();
+
+ var validationError = item as ValidationError;
+ validationError.ShouldNotBeNull();
+ validationError.ValidationErrorType.ShouldBe(ValidationErrorType.Error);
+ validationError.ErrorMessage.ShouldNotBeEmpty();
+ validationError.ErrorMessage.ShouldContain("Validation error");
+ }
+
+ var errors = actual.Where(x => x.Status == ResultStatus.Error)
+ .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+
+ errors.ShouldNotBeEmpty();
+
+ foreach (var item in errors)
+ {
+ item.ShouldBeAssignableTo();
+
+ var errorMessage = item as string;
+ errorMessage.ShouldNotBeNull();
+ }
+ }
+
+ [Fact]
+ public void Result_ShouldIgnoreAggregatingNoneOkNotFoundStatuses()
+ {
+ //arrange
+ var result1 = Result.Success();
+ var result2 = Result.NotFound();
+
+ //act
+ var action = Result.Aggregate(true, result1, result2);
+
+ //assert
+ action.ShouldNotBeNull();
+ action.ShouldBeEmpty();
+ }
+
+ [Fact]
+ public void Result_ShouldIgnoreAggregatingNoneOkNotFound_WhenMixedWithErrorStatusResults()
+ {
+ //arrange
+ var result1 = Result.Success();
+ var result2 = Result.NotFound();
+ var result3 = Result.Error("Something went wrong");
+
+ //act
+ //Action action = () => Result.Aggregate(true, result1, result2, result3);
+ var actual = Result.Aggregate(true, result1, result2, result3);
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldNotBeEmpty();
+ actual.Count.ShouldBe(1);
+ actual.ToList()[0].Status.ShouldBe(ResultStatus.Error);
+ }
+
+ [Fact]
+ public void Result_ImplicitOperator_ShouldCorrectlyConvertTheDataTypeToResultOfTypeImplicitly()
+ {
+ //arrange
+ var sut = new DummyFeatureService();
+
+ //act
+ var actual = sut.DoSomething();
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldBeOfType>();
+ actual.IsSuccess.ShouldBeTrue();
+ actual.Data.ShouldNotBeNull();
+ }
+
+ [Fact]
+ public void Result_ImplicitOperator_ShouldCorrectlyConvertTheResultOfGenericTypeToTheInnerType()
+ {
+ //arrange
+ var sut = new DummyFeatureService();
+
+ //act
+ var actual = sut.DoSomethingImplicitConversionOfResultGenericToInnerType();
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldBeOfType();
+ }
+
+ [Fact]
+ public void Result_ImplicitOperator_ShouldCorrectlyConvertNonGenericResultToGenericResultWithDefaultState()
+ {
+ //arrange
+ var sut = new DummyFeatureService();
+
+ //act
+ var actual = sut.DoSomethingImplicitConversionOfNonGenericResultToGenericResultWithDefault();
+
+ //assert
+ actual.ShouldNotBeNull();
+ actual.ShouldBeOfType>();
+ actual.IsSuccess.ShouldBeTrue();
+ actual.Data.ShouldBeNull();
+ actual.ErrorMessages.ShouldBeEmpty();
+ actual.ValidationErrors.ShouldBeEmpty();
+ actual.SuccessMessage.ShouldBeEmpty();
+ actual.Status.ShouldBe(ResultStatus.Ok);
+ }
+
+ // [Fact]
+ // public void ResultOfTypeString_ShouldAggregateAllResults_WhenItIsCalledWithMultipleResults_AndIncludeValidationErrorsIsFalse()
+ // {
+ // //arrange
+ // var result1 = Result.Invalid([new ValidationError("Validation error April", "666", ValidationErrorType.Error)]);
+ // var result2 = Result.Invalid([new ValidationError("Validation error May", "777", ValidationErrorType.Error)]);
+ //
+ // var result4 = Result.Error("Failure Error 999");
+ // var result5 = Result.Error("Failure Error 101010");
+ //
+ // //act
+ // var actual = Result.Aggregate(false, result1, result2, result4, result5);
+ //
+ // //assert
+ // var actualList = actual.ToList();
+ // actualList.ShouldNotBeEmpty();
+ // actualList.Where(x => x.Status == ResultStatus.Error).ShouldNotBeEmpty();
+ // actualList.Where(x => x.Status == ResultStatus.Invalid).ShouldNotBeEmpty();
+ //
+ // foreach (var item in actualList)
+ // {
+ // item.TypeName.ShouldNotBeNull();
+ // item.TypeName.GetType().Name.ShouldBe("String");
+ // }
+ //
+ // var validationErrors = actual.Where(x => x.Status == ResultStatus.Invalid)
+ // .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+ //
+ // validationErrors.ShouldNotBeEmpty();
+ //
+ // foreach (var item in validationErrors)
+ // {
+ // item.ShouldBeAssignableTo();
+ //
+ // var validationError = item as string;
+ // validationError.ShouldNotBeNull();
+ // validationError.ShouldContain("Validation error");
+ // }
+ //
+ // var errors = actual.Where(x => x.Status == ResultStatus.Error)
+ // .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+ //
+ // errors.ShouldNotBeEmpty();
+ //
+ // foreach (var item in errors)
+ // {
+ // item.ShouldBeAssignableTo();
+ //
+ // var errorMessage = item as string;
+ // errorMessage.ShouldNotBeNull();
+ // }
+ // }
+ //
+ // [Fact]
+ // public void ResultOfComplexType_ShouldAggregateAllResults_WhenItIsCalledWithMultipleResults_AndIncludeValidationErrorsIsFalse()
+ // {
+ // //arrange
+ // var result1 = Result.Invalid([new ValidationError("Validation error June", "668787", ValidationErrorType.Error)]);
+ // var result2 = Result.Invalid([new ValidationError("Validation error July", "4847", ValidationErrorType.Error)]);
+ //
+ // var result4 = Result.Error("Failure Error 89393");
+ // var result5 = Result.Error("Failure Error 111112");
+ //
+ // //act
+ // var actual = Result.Aggregate(false, result1, result2, result4, result5);
+ //
+ // //assert
+ // var actualList = actual.ToList();
+ // actualList.ShouldNotBeEmpty();
+ // actualList.Where(x => x.Status == ResultStatus.Error).ShouldNotBeEmpty();
+ // actualList.Where(x => x.Status == ResultStatus.Invalid).ShouldNotBeEmpty();
+ //
+ // foreach (var item in actualList)
+ // {
+ // item.TypeName.ShouldNotBeNull();
+ // item.TypeName.ShouldBe(nameof(DummyResponseModel));
+ // }
+ //
+ // var validationErrors = actual.Where(x => x.Status == ResultStatus.Invalid)
+ // .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+ //
+ // validationErrors.ShouldNotBeEmpty();
+ //
+ // foreach (var item in validationErrors)
+ // {
+ // item.ShouldBeAssignableTo();
+ //
+ // var validationError = item as string;
+ // validationError.ShouldNotBeNull();
+ // validationError.ShouldContain("Validation error");
+ // }
+ //
+ // var errors = actual.Where(x => x.Status == ResultStatus.Error)
+ // .SelectMany(y => y.Messages.Select(x => x)).ToList().AsReadOnly();
+ //
+ // errors.ShouldNotBeEmpty();
+ //
+ // foreach (var item in errors)
+ // {
+ // item.ShouldBeAssignableTo();
+ //
+ // var errorMessage = item as string;
+ // errorMessage.ShouldNotBeNull();
+ // }
+ // }
+
+ // [Fact]
+ // public void Result_ShouldAggregateAllSuccessResults()
+ // {
+ // //arrange
+ // var result1 = Result.Success();
+ // var result2 = Result.Success(new DummyResponseModel
+ // {
+ // DummyFieldOne = "SomeData"
+ // },
+ // "Success message two.");
+ //
+ // //act
+ // var actual = Result.Aggregate(false, result1, result2);
+ //
+ // //assert
+ // var actualList = actual.ToList();
+ // actualList.ShouldNotBeEmpty();
+ // actualList.Where(x => x.Status == ResultStatus.Ok).ShouldNotBeEmpty();
+ //
+ // foreach (var item in actualList)
+ // {
+ // item.TypeName.ShouldNotBeNull();
+ // item.TypeName.ShouldBe(nameof(DummyResponseModel));
+ // item.Messages.ShouldNotBeEmpty();
+ // }
+ // }
+}