refactor: remove static methods from generic type and move to non-generic static class.

This commit is contained in:
Stevan Freeborn
2026-03-30 15:03:01 -05:00
parent 1012decef0
commit 1d14bd2261
10 changed files with 258 additions and 155 deletions
+29 -14
View File
@@ -30,10 +30,10 @@ dotnet add package StevanFreeborn.Results
using StevanFreeborn.Results; using StevanFreeborn.Results;
// Create a successful result // Create a successful result
Result<Unit, Error> ok = Result<Unit, Error>.Ok(default); Result<Unit, Error> ok = Result.Ok<Unit, Error>(default);
// Create a failed result // Create a failed result
Result<Unit, Error> fail = Result<Unit, Error>.Fail(new Error("NotFound", "User not found")); Result<Unit, Error> fail = Result.Fail<Unit, Error>(new Error("NotFound", "User not found"));
// Work with results that have values // Work with results that have values
Result<int, Error> divisionResult = Divide(10, 2); Result<int, Error> divisionResult = Divide(10, 2);
@@ -69,11 +69,11 @@ Result<User, DomainError> GetUser(int id)
{ {
if (id <= 0) if (id <= 0)
{ {
return Result<User, DomainError>.Fail(new DomainError("InvalidId", "User ID must be positive")); return Result.Fail<User, DomainError>(new DomainError("InvalidId", "User ID must be positive"));
} }
// ... fetch user // ... fetch user
return Result<User, DomainError>.Ok(user); return Result.Ok<User, DomainError>(user);
} }
// Chain with custom errors // Chain with custom errors
@@ -122,14 +122,29 @@ Result<Unit, Error> operation = DoSomething();
## Result Types ## Result Types
### `Result` (Static Factory Class)
The static `Result` class provides factory methods for creating result instances:
```csharp
// Create successful result
Result<int, Error> ok = Result.Ok<int, Error>(42);
// Create failed result
Result<int, Error> fail = Result.Fail<int, Error>(new Error("Failed", "Something went wrong"));
// Wrap a function that may throw
Result<string, Error> result = Result.Try<string, Error>(() => File.ReadAllText("file.txt"));
```
### `Result<T, TError>` ### `Result<T, TError>`
The main Result type with generic type parameters for both value and error: The main Result type with generic type parameters for both value and error:
```csharp ```csharp
// Creation // Creation
Result<int, Error> ok = Result<int, Error>.Ok(42); Result<int, Error> ok = Result.Ok<int, Error>(42);
Result<int, Error> fail = Result<int, Error>.Fail(new Error("Invalid", "Invalid input")); Result<int, Error> fail = Result.Fail<int, Error>(new Error("Invalid", "Invalid input"));
// Check status // Check status
if (result.IsSuccess) { /* ... */ } if (result.IsSuccess) { /* ... */ }
@@ -147,8 +162,8 @@ Error error = result.Error;
For operations that don't return a value, use `Unit`: For operations that don't return a value, use `Unit`:
```csharp ```csharp
Result<Unit, Error> ok = Result<Unit, Error>.Ok(default); Result<Unit, Error> ok = Result.Ok<Unit, Error>(default);
Result<Unit, Error> fail = Result<Unit, Error>.Fail(new Error("Failed", "Something went wrong")); Result<Unit, Error> fail = Result.Fail<Unit, Error>(new Error("Failed", "Something went wrong"));
``` ```
## Functional Operations ## Functional Operations
@@ -158,8 +173,8 @@ Result<Unit, Error> fail = Result<Unit, Error>.Fail(new Error("Failed", "Somethi
Transforms the value if success, propagates the error if failure. Transforms the value if success, propagates the error if failure.
```csharp ```csharp
Result<int, Error> ok = Result<int, Error>.Ok(5); Result<int, Error> ok = Result.Ok<int, Error>(5);
Result<string, Error> mapped = ok.Map(x => x.ToString()); // Result<string, Error>.Ok("5") Result<string, Error> mapped = ok.Map(x => x.ToString()); // Result.Ok<string, Error>("5")
``` ```
### MapError ### MapError
@@ -167,7 +182,7 @@ Result<string, Error> mapped = ok.Map(x => x.ToString()); // Result<string, Erro
Transforms the error if failure, propagates the value if success. Transforms the error if failure, propagates the value if success.
```csharp ```csharp
Result<int, Error> fail = Result<int, Error>.Fail(new Error("NotFound", "Not found")); Result<int, Error> fail = Result.Fail<int, Error>(new Error("NotFound", "Not found"));
Result<int, Error> mapped = fail.MapError(e => new Error("Unexpected", e.Message)); Result<int, Error> mapped = fail.MapError(e => new Error("Unexpected", e.Message));
``` ```
@@ -212,10 +227,10 @@ Wraps a function that may throw an exception in a Result.
```csharp ```csharp
// Simple usage with default error handler // Simple usage with default error handler
Result<string, Error> result = Result<string, Error>.Try(() => File.ReadAllText("file.txt")); Result<string, Error> result = Result.Try<string, Error>(() => File.ReadAllText("file.txt"));
// Custom error handler // Custom error handler
Result<string, Error> result = Result<string, Error>.Try( Result<string, Error> result = Result.Try<string, Error>(
() => File.ReadAllText("file.txt"), () => File.ReadAllText("file.txt"),
ex => new Error("ReadError", ex.Message) ex => new Error("ReadError", ex.Message)
); );
@@ -241,7 +256,7 @@ string result = await result.MatchAsync(
); );
// Async Try // Async Try
Result<string, Error> result = await ResultAsyncExtensions.TryAsync( Result<string, Error> result = await Result.TryAsync<string, Error>(
() => HttpClient.GetStringAsync("https://api.example.com") () => HttpClient.GetStringAsync("https://api.example.com")
); );
``` ```
@@ -1,6 +1,5 @@
[*.cs] [*.cs]
dotnet_diagnostic.CA1000.severity = none
dotnet_diagnostic.CA1031.severity = none dotnet_diagnostic.CA1031.severity = none
dotnet_diagnostic.CA1510.severity = none dotnet_diagnostic.CA1510.severity = none
dotnet_diagnostic.CA2225.severity = none dotnet_diagnostic.CA2225.severity = none
+78 -64
View File
@@ -1,6 +1,79 @@
namespace StevanFreeborn.Results namespace StevanFreeborn.Results
{ {
/// <summary>
/// Static factory methods for creating result instances.
/// </summary>
public static class Result
{
/// <summary>
/// Creates a successful result with the specified value.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TError">The type of the error.</typeparam>
/// <param name="value">The value.</param>
/// <returns>A successful <see cref="Result{T, TError}"/>.</returns>
public static Result<T, TError> Ok<T, TError>(T value) where TError : IError
{
return new Result<T, TError>(true, value, default);
}
/// <summary>
/// Creates a failed result with the specified error.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TError">The type of the error.</typeparam>
/// <param name="error">The error.</param>
/// <returns>A failed <see cref="Result{T, TError}"/>.</returns>
public static Result<T, TError> Fail<T, TError>(TError error) where TError : IError
{
return new Result<T, TError>(false, default!, error);
}
/// <summary>
/// Executes the specified function and wraps the result in a <see cref="Result{T, TError}"/>.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TError">The type of the error.</typeparam>
/// <param name="func">The function to execute.</param>
/// <returns>A successful result with the return value if no exception is thrown; otherwise, a failed result.</returns>
public static Result<T, TError> Try<T, TError>(Func<T> func) where TError : IError
{
return Try(func, ex => (TError)(IError)new Error("UnexpectedError", ex.Message));
}
/// <summary>
/// Executes the specified function and wraps the result in a <see cref="Result{T, TError}"/> using the specified error handler.
/// </summary>
/// <typeparam name="T">The type of the value.</typeparam>
/// <typeparam name="TError">The type of the error.</typeparam>
/// <param name="func">The function to execute.</param>
/// <param name="errorHandler">The function to convert exceptions to errors.</param>
/// <returns>A successful result with the return value if no exception is thrown; otherwise, a failed result.</returns>
/// <exception cref="ArgumentNullException">Thrown when func or errorHandler is null.</exception>
public static Result<T, TError> Try<T, TError>(Func<T> func, Func<Exception, TError> errorHandler) where TError : IError
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
if (errorHandler is null)
{
throw new ArgumentNullException(nameof(errorHandler));
}
try
{
return Ok<T, TError>(func());
}
catch (Exception ex)
{
return Fail<T, TError>(errorHandler(ex));
}
}
}
/// <summary> /// <summary>
/// Represents a result with a value and error type, used for operations that either succeed with a value or fail with an error. /// Represents a result with a value and error type, used for operations that either succeed with a value or fail with an error.
/// </summary> /// </summary>
@@ -42,33 +115,13 @@ namespace StevanFreeborn.Results
_error = error; _error = error;
} }
/// <summary>
/// Creates a successful result with the specified value.
/// </summary>
/// <param name="value">The value.</param>
/// <returns>A successful <see cref="Result{T, TError}"/>.</returns>
public static Result<T, TError> Ok(T value)
{
return new Result<T, TError>(true, value, default);
}
/// <summary>
/// Creates a failed result with the specified error.
/// </summary>
/// <param name="error">The error.</param>
/// <returns>A failed <see cref="Result{T, TError}"/>.</returns>
public static Result<T, TError> Fail(TError error)
{
return new Result<T, TError>(false, default!, error);
}
/// <summary> /// <summary>
/// Implicitly converts a value to a successful <see cref="Result{T, TError}"/>. /// Implicitly converts a value to a successful <see cref="Result{T, TError}"/>.
/// </summary> /// </summary>
/// <param name="value">The value to convert.</param> /// <param name="value">The value to convert.</param>
public static implicit operator Result<T, TError>(T value) public static implicit operator Result<T, TError>(T value)
{ {
return Ok(value); return Result.Ok<T, TError>(value);
} }
/// <summary> /// <summary>
@@ -77,7 +130,7 @@ namespace StevanFreeborn.Results
/// <param name="error">The error to convert.</param> /// <param name="error">The error to convert.</param>
public static implicit operator Result<T, TError>(TError error) public static implicit operator Result<T, TError>(TError error)
{ {
return Fail(error); return Result.Fail<T, TError>(error);
} }
/// <summary> /// <summary>
@@ -94,7 +147,7 @@ namespace StevanFreeborn.Results
throw new ArgumentNullException(nameof(mapper)); throw new ArgumentNullException(nameof(mapper));
} }
return IsSuccess ? Result<TNew, TError>.Ok(mapper(Value)) : Result<TNew, TError>.Fail(Error); return IsSuccess ? Result.Ok<TNew, TError>(mapper(Value)) : Result.Fail<TNew, TError>(Error);
} }
/// <summary> /// <summary>
@@ -110,7 +163,7 @@ namespace StevanFreeborn.Results
throw new ArgumentNullException(nameof(mapper)); throw new ArgumentNullException(nameof(mapper));
} }
return IsFailure ? Result<T, TNewError>.Fail(mapper(Error)) : Result<T, TNewError>.Ok(Value); return IsFailure ? Result.Fail<T, TNewError>(mapper(Error)) : Result.Ok<T, TNewError>(Value);
} }
/// <summary> /// <summary>
@@ -127,7 +180,7 @@ namespace StevanFreeborn.Results
throw new ArgumentNullException(nameof(binder)); throw new ArgumentNullException(nameof(binder));
} }
return IsSuccess ? binder(Value) : Result<TNew, TError>.Fail(Error); return IsSuccess ? binder(Value) : Result.Fail<TNew, TError>(Error);
} }
/// <summary> /// <summary>
@@ -220,45 +273,6 @@ namespace StevanFreeborn.Results
} }
return this; return this;
} }
/// <summary>
/// Executes the specified function and wraps the result in a <see cref="Result{T, TError}"/>.
/// </summary>
/// <param name="func">The function to execute.</param>
/// <returns>A successful result with the return value if no exception is thrown; otherwise, a failed result.</returns>
public static Result<T, TError> Try(Func<T> func)
{
return Try(func, ex => (TError)(IError)new Error("UnexpectedError", ex.Message));
}
/// <summary>
/// Executes the specified function and wraps the result in a <see cref="Result{T, TError}"/> using the specified error handler.
/// </summary>
/// <param name="func">The function to execute.</param>
/// <param name="errorHandler">The function to convert exceptions to errors.</param>
/// <returns>A successful result with the return value if no exception is thrown; otherwise, a failed result.</returns>
/// <exception cref="ArgumentNullException">Thrown when func or errorHandler is null.</exception>
public static Result<T, TError> Try(Func<T> func, Func<Exception, TError> errorHandler)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
if (errorHandler is null)
{
throw new ArgumentNullException(nameof(errorHandler));
}
try
{
return Ok(func());
}
catch (Exception ex)
{
return Fail(errorHandler(ex));
}
}
} }
} }
@@ -56,7 +56,7 @@ namespace StevanFreeborn.Results
throw new ArgumentNullException(nameof(mapper)); throw new ArgumentNullException(nameof(mapper));
} }
return result.IsFailure ? await mapper(result.Error).ConfigureAwait(false) : Result<T, TNewError>.Ok(result.Value); return result.IsFailure ? await mapper(result.Error).ConfigureAwait(false) : Result.Ok<T, TNewError>(result.Value);
} }
/// <summary> /// <summary>
@@ -177,11 +177,11 @@ namespace StevanFreeborn.Results
try try
{ {
await action().ConfigureAwait(false); await action().ConfigureAwait(false);
return Result<Unit, Error>.Ok(default); return Result.Ok<Unit, Error>(default);
} }
catch (Exception ex) catch (Exception ex)
{ {
return Result<Unit, Error>.Fail(errorHandler(ex)); return Result.Fail<Unit, Error>(errorHandler(ex));
} }
} }
@@ -208,7 +208,7 @@ namespace StevanFreeborn.Results
throw new ArgumentNullException(nameof(binder)); throw new ArgumentNullException(nameof(binder));
} }
return result.IsSuccess ? await binder(result.Value).ConfigureAwait(false) : Result<TNew, TError>.Fail(result.Error); return result.IsSuccess ? await binder(result.Value).ConfigureAwait(false) : Result.Fail<TNew, TError>(result.Error);
} }
/// <summary> /// <summary>
@@ -278,11 +278,11 @@ namespace StevanFreeborn.Results
try try
{ {
return Result<T, Error>.Ok(await func().ConfigureAwait(false)); return Result.Ok<T, Error>(await func().ConfigureAwait(false));
} }
catch (Exception ex) catch (Exception ex)
{ {
return Result<T, Error>.Fail(errorHandler(ex)); return Result.Fail<T, Error>(errorHandler(ex));
} }
} }
} }
+1 -1
View File
@@ -3,6 +3,6 @@
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally> <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageVersion Include="TUnit" Version="0.73.0" /> <PackageVersion Include="TUnit" Version="1.23.7" />
</ItemGroup> </ItemGroup>
</Project> </Project>
@@ -0,0 +1,3 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
</Project>
@@ -6,6 +6,7 @@ public class ErrorTests
public async Task Constructor_WhenCalled_ItShouldSetCode() public async Task Constructor_WhenCalled_ItShouldSetCode()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
await Assert.That(error.Code).IsEqualTo("code"); await Assert.That(error.Code).IsEqualTo("code");
} }
@@ -13,6 +14,7 @@ public class ErrorTests
public async Task Constructor_WhenCalled_ItShouldSetMessage() public async Task Constructor_WhenCalled_ItShouldSetMessage()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
await Assert.That(error.Message).IsEqualTo("message"); await Assert.That(error.Message).IsEqualTo("message");
} }
@@ -21,6 +23,7 @@ public class ErrorTests
{ {
var metadata = new Dictionary<string, object> { { "key", "value" } }; var metadata = new Dictionary<string, object> { { "key", "value" } };
var error = new Error("code", "message", metadata); var error = new Error("code", "message", metadata);
await Assert.That(error.Metadata!.Count).IsEqualTo(1); await Assert.That(error.Metadata!.Count).IsEqualTo(1);
await Assert.That(error.Metadata["key"]).IsEqualTo("value"); await Assert.That(error.Metadata["key"]).IsEqualTo("value");
} }
@@ -29,7 +32,8 @@ public class ErrorTests
public async Task Constructor_WhenCalledWithNullMetadata_ItShouldSetMetadataToNull() public async Task Constructor_WhenCalledWithNullMetadata_ItShouldSetMetadataToNull()
{ {
var error = new Error("code", "message", null); var error = new Error("code", "message", null);
await Assert.That(error.Metadata!).IsNull();
await Assert.That(error.Metadata).IsNull();
} }
[Test] [Test]
@@ -37,6 +41,7 @@ public class ErrorTests
{ {
var error1 = new Error("code", "message"); var error1 = new Error("code", "message");
var error2 = new Error("code", "message"); var error2 = new Error("code", "message");
await Assert.That(error1).IsEqualTo(error2); await Assert.That(error1).IsEqualTo(error2);
} }
@@ -45,6 +50,7 @@ public class ErrorTests
{ {
var error1 = new Error("code1", "message"); var error1 = new Error("code1", "message");
var error2 = new Error("code2", "message"); var error2 = new Error("code2", "message");
await Assert.That(error1 == error2).IsFalse(); await Assert.That(error1 == error2).IsFalse();
} }
@@ -52,6 +58,7 @@ public class ErrorTests
public async Task Error_WhenCreated_ItShouldImplementIError() public async Task Error_WhenCreated_ItShouldImplementIError()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
await Assert.That(error).IsAssignableTo<IError>(); await Assert.That(error).IsAssignableTo<IError>();
} }
} }
@@ -7,42 +7,49 @@ public class ResultAsyncExtensionsTests
[Test] [Test]
public async Task MapAsync_WhenResultIsSuccess_ItShouldInvokeAsyncAction() public async Task MapAsync_WhenResultIsSuccess_ItShouldInvokeAsyncAction()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var invoked = false; var invoked = false;
await result.MapAsync(_ => { invoked = true; return Task.CompletedTask; }); await result.MapAsync(_ => { invoked = true; return Task.CompletedTask; });
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
[Test] [Test]
public async Task MapAsync_WhenResultIsFailure_ItShouldSkipAsyncAction() public async Task MapAsync_WhenResultIsFailure_ItShouldSkipAsyncAction()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var invoked = false; var invoked = false;
await result.MapAsync(_ => { invoked = true; return Task.CompletedTask; }); await result.MapAsync(_ => { invoked = true; return Task.CompletedTask; });
await Assert.That(invoked).IsFalse(); await Assert.That(invoked).IsFalse();
} }
[Test] [Test]
public async Task MapAsync_WhenCalled_ItShouldReturnOriginalResult() public async Task MapAsync_WhenCalled_ItShouldReturnOriginalResult()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var mapped = await result.MapAsync(_ => Task.CompletedTask); var mapped = await result.MapAsync(_ => Task.CompletedTask);
await Assert.That(mapped.IsSuccess).IsTrue(); await Assert.That(mapped.IsSuccess).IsTrue();
} }
[Test] [Test]
public async Task BindAsync_WhenResultIsSuccess_ItShouldInvokeBinderAndReturnResult() public async Task BindAsync_WhenResultIsSuccess_ItShouldInvokeBinderAndReturnResult()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var bound = await result.BindAsync(_ => Task.FromResult(Result<Unit, Error>.Fail(new Error("new_error", "new message")))); var bound = await result.BindAsync(_ => Task.FromResult(Result.Fail<Unit, Error>(new Error("new_error", "new message"))));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
} }
[Test] [Test]
public async Task BindAsync_WhenResultIsFailure_ItShouldReturnOriginalResult() public async Task BindAsync_WhenResultIsFailure_ItShouldReturnOriginalResult()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var bound = await result.BindAsync(_ => Task.FromResult(Result<Unit, Error>.Ok(default))); var bound = await result.BindAsync(_ => Task.FromResult(Result.Ok<Unit, Error>(default)));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
await Assert.That(bound.Error.Code).IsEqualTo("code"); await Assert.That(bound.Error.Code).IsEqualTo("code");
} }
@@ -50,16 +57,18 @@ public class ResultAsyncExtensionsTests
[Test] [Test]
public async Task MatchAsync_WhenResultIsSuccess_ItShouldReturnOnSuccessValue() public async Task MatchAsync_WhenResultIsSuccess_ItShouldReturnOnSuccessValue()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var matched = await result.MatchAsync(_ => Task.FromResult("success"), _ => Task.FromResult("failure")); var matched = await result.MatchAsync(_ => Task.FromResult("success"), _ => Task.FromResult("failure"));
await Assert.That(matched).IsEqualTo("success"); await Assert.That(matched).IsEqualTo("success");
} }
[Test] [Test]
public async Task MatchAsync_WhenResultIsFailure_ItShouldReturnOnFailureValue() public async Task MatchAsync_WhenResultIsFailure_ItShouldReturnOnFailureValue()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var matched = await result.MatchAsync(_ => Task.FromResult("success"), e => Task.FromResult(e.Code)); var matched = await result.MatchAsync(_ => Task.FromResult("success"), e => Task.FromResult(e.Code));
await Assert.That(matched).IsEqualTo("code"); await Assert.That(matched).IsEqualTo("code");
} }
@@ -67,6 +76,7 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenActionSucceeds_ItShouldReturnOk() public async Task TryAsync_WhenActionSucceeds_ItShouldReturnOk()
{ {
var result = await ResultAsyncExtensions.TryAsync(() => Task.CompletedTask); var result = await ResultAsyncExtensions.TryAsync(() => Task.CompletedTask);
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
} }
@@ -74,6 +84,7 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenActionThrowsException_ItShouldReturnFailWithUnexpectedError() public async Task TryAsync_WhenActionThrowsException_ItShouldReturnFailWithUnexpectedError()
{ {
var result = await ResultAsyncExtensions.TryAsync(() => throw new InvalidOperationException("test error")); var result = await ResultAsyncExtensions.TryAsync(() => throw new InvalidOperationException("test error"));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError"); await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError");
} }
@@ -82,8 +93,10 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenActionThrowsException_ItShouldUseCustomError() public async Task TryAsync_WhenActionThrowsException_ItShouldUseCustomError()
{ {
var result = await ResultAsyncExtensions.TryAsync( var result = await ResultAsyncExtensions.TryAsync(
() => throw new InvalidOperationException("test"), () => throw new InvalidOperationException("test"),
ex => new Error("custom", ex.Message)); ex => new Error("custom", ex.Message)
);
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("custom"); await Assert.That(result.Error.Code).IsEqualTo("custom");
} }
@@ -91,8 +104,9 @@ public class ResultAsyncExtensionsTests
[Test] [Test]
public async Task MapAsync_WhenResultIsSuccess_ItShouldTransformValue() public async Task MapAsync_WhenResultIsSuccess_ItShouldTransformValue()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var mapped = await result.MapAsync(async n => (await Task.FromResult(n)).ToString(CultureInfo.InvariantCulture)); var mapped = await result.MapAsync(async n => (await Task.FromResult(n)).ToString(CultureInfo.InvariantCulture));
await Assert.That(mapped.IsSuccess).IsTrue(); await Assert.That(mapped.IsSuccess).IsTrue();
await Assert.That(mapped.Value).IsEqualTo("5"); await Assert.That(mapped.Value).IsEqualTo("5");
} }
@@ -101,8 +115,9 @@ public class ResultAsyncExtensionsTests
public async Task MapAsync_WhenResultIsFailure_ItShouldPropagateError() public async Task MapAsync_WhenResultIsFailure_ItShouldPropagateError()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<int, Error>.Fail(error); var result = Result.Fail<int, Error>(error);
var mapped = await result.MapAsync(async n => (await Task.FromResult(n)).ToString(CultureInfo.InvariantCulture)); var mapped = await result.MapAsync(async n => (await Task.FromResult(n)).ToString(CultureInfo.InvariantCulture));
await Assert.That(mapped.IsFailure).IsTrue(); await Assert.That(mapped.IsFailure).IsTrue();
await Assert.That(mapped.Error).IsEqualTo(error); await Assert.That(mapped.Error).IsEqualTo(error);
} }
@@ -110,8 +125,9 @@ public class ResultAsyncExtensionsTests
[Test] [Test]
public async Task BindAsync_WhenResultIsSuccess_ItShouldChainToBinderResult() public async Task BindAsync_WhenResultIsSuccess_ItShouldChainToBinderResult()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var bound = await result.BindAsync(async n => await Task.FromResult(Result<string, Error>.Ok(n.ToString(CultureInfo.InvariantCulture)))); var bound = await result.BindAsync(async n => await Task.FromResult(Result.Ok<string, Error>(n.ToString(CultureInfo.InvariantCulture))));
await Assert.That(bound.IsSuccess).IsTrue(); await Assert.That(bound.IsSuccess).IsTrue();
await Assert.That(bound.Value).IsEqualTo("5"); await Assert.That(bound.Value).IsEqualTo("5");
} }
@@ -120,8 +136,9 @@ public class ResultAsyncExtensionsTests
public async Task BindAsync_WhenResultIsFailure_ItShouldPropagateError() public async Task BindAsync_WhenResultIsFailure_ItShouldPropagateError()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<int, Error>.Fail(error); var result = Result.Fail<int, Error>(error);
var bound = await result.BindAsync(async n => await Task.FromResult(Result<string, Error>.Ok(n.ToString(CultureInfo.InvariantCulture)))); var bound = await result.BindAsync(async n => await Task.FromResult(Result.Ok<string, Error>(n.ToString(CultureInfo.InvariantCulture))));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
await Assert.That(bound.Error).IsEqualTo(error); await Assert.That(bound.Error).IsEqualTo(error);
} }
@@ -129,20 +146,24 @@ public class ResultAsyncExtensionsTests
[Test] [Test]
public async Task MatchAsyncGeneric_WhenResultIsSuccess_ItShouldReturnOnSuccessValue() public async Task MatchAsyncGeneric_WhenResultIsSuccess_ItShouldReturnOnSuccessValue()
{ {
var result = Result<string, Error>.Ok("test"); var result = Result.Ok<string, Error>("test");
var matched = await result.MatchAsync( var matched = await result.MatchAsync(
v => Task.FromResult(v.Length), v => Task.FromResult(v.Length),
_ => Task.FromResult(0)); _ => Task.FromResult(0)
);
await Assert.That(matched).IsEqualTo(4); await Assert.That(matched).IsEqualTo(4);
} }
[Test] [Test]
public async Task MatchAsyncGeneric_WhenResultIsFailure_ItShouldReturnOnFailureValue() public async Task MatchAsyncGeneric_WhenResultIsFailure_ItShouldReturnOnFailureValue()
{ {
var result = Result<string, Error>.Fail(new Error("code", "message")); var result = Result.Fail<string, Error>(new Error("code", "message"));
var matched = await result.MatchAsync( var matched = await result.MatchAsync(
v => Task.FromResult(v.Length), v => Task.FromResult(v.Length),
_ => Task.FromResult(-1)); _ => Task.FromResult(-1)
);
await Assert.That(matched).IsEqualTo(-1); await Assert.That(matched).IsEqualTo(-1);
} }
@@ -150,6 +171,7 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenFuncSucceeds_ItShouldReturnOkWithValue() public async Task TryAsync_WhenFuncSucceeds_ItShouldReturnOkWithValue()
{ {
var result = await ResultAsyncExtensions.TryAsync(async () => await Task.FromResult(42)); var result = await ResultAsyncExtensions.TryAsync(async () => await Task.FromResult(42));
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
await Assert.That(result.Value).IsEqualTo(42); await Assert.That(result.Value).IsEqualTo(42);
} }
@@ -158,6 +180,7 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenFuncThrowsException_ItShouldReturnFailWithUnexpectedError() public async Task TryAsync_WhenFuncThrowsException_ItShouldReturnFailWithUnexpectedError()
{ {
var result = await ResultAsyncExtensions.TryAsync<int>(() => throw new InvalidOperationException("test error")); var result = await ResultAsyncExtensions.TryAsync<int>(() => throw new InvalidOperationException("test error"));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError"); await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError");
} }
@@ -166,8 +189,10 @@ public class ResultAsyncExtensionsTests
public async Task TryAsync_WhenFuncThrowsException_ItShouldUseCustomError() public async Task TryAsync_WhenFuncThrowsException_ItShouldUseCustomError()
{ {
var result = await ResultAsyncExtensions.TryAsync<int>( var result = await ResultAsyncExtensions.TryAsync<int>(
() => throw new InvalidOperationException("test"), () => throw new InvalidOperationException("test"),
ex => new Error("custom", ex.Message)); ex => new Error("custom", ex.Message)
);
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("custom"); await Assert.That(result.Error.Code).IsEqualTo("custom");
} }
@@ -7,7 +7,8 @@ public class ResultTTests
[Test] [Test]
public async Task Ok_WhenCalledWithValue_ItShouldReturnSuccessResult() public async Task Ok_WhenCalledWithValue_ItShouldReturnSuccessResult()
{ {
var result = Result<string, Error>.Ok("test"); var result = Result.Ok<string, Error>("test");
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
await Assert.That(result.IsFailure).IsFalse(); await Assert.That(result.IsFailure).IsFalse();
await Assert.That(result.Value).IsEqualTo("test"); await Assert.That(result.Value).IsEqualTo("test");
@@ -17,24 +18,25 @@ public class ResultTTests
public async Task Fail_WhenCalled_ItShouldReturnFailureResult() public async Task Fail_WhenCalled_ItShouldReturnFailureResult()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<string, Error>.Fail(error); var result = Result.Fail<string, Error>(error);
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.IsSuccess).IsFalse(); await Assert.That(result.IsSuccess).IsFalse();
await Assert.That(result.Error).IsEqualTo(error); await Assert.That(result.Error).IsEqualTo(error);
} }
[Test] [Test]
public async Task Value_WhenResultIsFailure_ItShouldThrowInvalidOperationException() public void Value_WhenResultIsFailure_ItShouldThrowInvalidOperationException()
{ {
var result = Result<string, Error>.Fail(new Error("code", "message")); var result = Result.Fail<string, Error>(new Error("code", "message"));
Assert.Throws<InvalidOperationException>(() => _ = result.Value); Assert.Throws<InvalidOperationException>(() => _ = result.Value);
} }
[Test] [Test]
public async Task Error_WhenResultIsSuccess_ItShouldThrowInvalidOperationException() public void Error_WhenResultIsSuccess_ItShouldThrowInvalidOperationException()
{ {
var result = Result<string, Error>.Ok("test"); var result = Result.Ok<string, Error>("test");
Assert.Throws<InvalidOperationException>(() => _ = result.Error); Assert.Throws<InvalidOperationException>(() => _ = result.Error);
} }
@@ -43,6 +45,7 @@ public class ResultTTests
public async Task ImplicitConversion_FromValue_ItShouldReturnSuccessResult() public async Task ImplicitConversion_FromValue_ItShouldReturnSuccessResult()
{ {
Result<string, Error> result = "test"; Result<string, Error> result = "test";
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
await Assert.That(result.Value).IsEqualTo("test"); await Assert.That(result.Value).IsEqualTo("test");
} }
@@ -52,6 +55,7 @@ public class ResultTTests
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
Result<string, Error> result = error; Result<string, Error> result = error;
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error).IsEqualTo(error); await Assert.That(result.Error).IsEqualTo(error);
} }
@@ -59,8 +63,9 @@ public class ResultTTests
[Test] [Test]
public async Task Map_WhenResultIsSuccess_ItShouldTransformValue() public async Task Map_WhenResultIsSuccess_ItShouldTransformValue()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var mapped = result.Map(n => n.ToString(CultureInfo.InvariantCulture)); var mapped = result.Map(n => n.ToString(CultureInfo.InvariantCulture));
await Assert.That(mapped.IsSuccess).IsTrue(); await Assert.That(mapped.IsSuccess).IsTrue();
await Assert.That(mapped.Value).IsEqualTo("5"); await Assert.That(mapped.Value).IsEqualTo("5");
} }
@@ -69,8 +74,9 @@ public class ResultTTests
public async Task Map_WhenResultIsFailure_ItShouldPropagateError() public async Task Map_WhenResultIsFailure_ItShouldPropagateError()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<int, Error>.Fail(error); var result = Result.Fail<int, Error>(error);
var mapped = result.Map(n => n.ToString(CultureInfo.InvariantCulture)); var mapped = result.Map(n => n.ToString(CultureInfo.InvariantCulture));
await Assert.That(mapped.IsFailure).IsTrue(); await Assert.That(mapped.IsFailure).IsTrue();
await Assert.That(mapped.Error).IsEqualTo(error); await Assert.That(mapped.Error).IsEqualTo(error);
} }
@@ -79,8 +85,9 @@ public class ResultTTests
public async Task MapError_WhenResultIsFailure_ItShouldTransformError() public async Task MapError_WhenResultIsFailure_ItShouldTransformError()
{ {
var originalError = new Error("original", "original message"); var originalError = new Error("original", "original message");
var result = Result<int, Error>.Fail(originalError); var result = Result.Fail<int, Error>(originalError);
var transformed = result.MapError(e => new Error("transformed", e.Message)); var transformed = result.MapError(e => new Error("transformed", e.Message));
await Assert.That(transformed.IsFailure).IsTrue(); await Assert.That(transformed.IsFailure).IsTrue();
await Assert.That(transformed.Error.Code).IsEqualTo("transformed"); await Assert.That(transformed.Error.Code).IsEqualTo("transformed");
} }
@@ -88,8 +95,9 @@ public class ResultTTests
[Test] [Test]
public async Task MapError_WhenResultIsSuccess_ItShouldReturnUnchanged() public async Task MapError_WhenResultIsSuccess_ItShouldReturnUnchanged()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var mapped = result.MapError(e => new Error("transformed", e.Message)); var mapped = result.MapError(e => new Error("transformed", e.Message));
await Assert.That(mapped.IsSuccess).IsTrue(); await Assert.That(mapped.IsSuccess).IsTrue();
await Assert.That(mapped.Value).IsEqualTo(5); await Assert.That(mapped.Value).IsEqualTo(5);
} }
@@ -97,8 +105,9 @@ public class ResultTTests
[Test] [Test]
public async Task Bind_WhenResultIsSuccess_ItShouldChainToBinderResult() public async Task Bind_WhenResultIsSuccess_ItShouldChainToBinderResult()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var bound = result.Bind(n => Result<string, Error>.Ok(n.ToString(CultureInfo.InvariantCulture))); var bound = result.Bind(n => Result.Ok<string, Error>(n.ToString(CultureInfo.InvariantCulture)));
await Assert.That(bound.IsSuccess).IsTrue(); await Assert.That(bound.IsSuccess).IsTrue();
await Assert.That(bound.Value).IsEqualTo("5"); await Assert.That(bound.Value).IsEqualTo("5");
} }
@@ -107,8 +116,9 @@ public class ResultTTests
public async Task Bind_WhenResultIsFailure_ItShouldPropagateError() public async Task Bind_WhenResultIsFailure_ItShouldPropagateError()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<int, Error>.Fail(error); var result = Result.Fail<int, Error>(error);
var bound = result.Bind(n => Result<string, Error>.Ok(n.ToString(CultureInfo.InvariantCulture))); var bound = result.Bind(n => Result.Ok<string, Error>(n.ToString(CultureInfo.InvariantCulture)));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
await Assert.That(bound.Error).IsEqualTo(error); await Assert.That(bound.Error).IsEqualTo(error);
} }
@@ -116,8 +126,9 @@ public class ResultTTests
[Test] [Test]
public async Task Bind_WhenResultIsSuccess_ItShouldReturnBinderFailureResult() public async Task Bind_WhenResultIsSuccess_ItShouldReturnBinderFailureResult()
{ {
var result = Result<int, Error>.Ok(5); var result = Result.Ok<int, Error>(5);
var bound = result.Bind(_ => Result<string, Error>.Fail(new Error("bind_error", "bound failed"))); var bound = result.Bind(_ => Result.Fail<string, Error>(new Error("bind_error", "bound failed")));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
await Assert.That(bound.Error.Code).IsEqualTo("bind_error"); await Assert.That(bound.Error.Code).IsEqualTo("bind_error");
} }
@@ -125,25 +136,29 @@ public class ResultTTests
[Test] [Test]
public async Task Match_WhenResultIsSuccess_ItShouldReturnOnSuccessValue() public async Task Match_WhenResultIsSuccess_ItShouldReturnOnSuccessValue()
{ {
var result = Result<string, Error>.Ok("test"); var result = Result.Ok<string, Error>("test");
var matched = result.Match(v => v.Length, _ => 0); var matched = result.Match(v => v.Length, _ => 0);
await Assert.That(matched).IsEqualTo(4); await Assert.That(matched).IsEqualTo(4);
} }
[Test] [Test]
public async Task Match_WhenResultIsFailure_ItShouldReturnOnFailureValue() public async Task Match_WhenResultIsFailure_ItShouldReturnOnFailureValue()
{ {
var result = Result<string, Error>.Fail(new Error("code", "message")); var result = Result.Fail<string, Error>(new Error("code", "message"));
var matched = result.Match(v => v.Length, e => -1); var matched = result.Match(v => v.Length, e => -1);
await Assert.That(matched).IsEqualTo(-1); await Assert.That(matched).IsEqualTo(-1);
} }
[Test] [Test]
public async Task Match_WhenResultIsSuccess_ItShouldInvokeOnSuccess() public async Task Match_WhenResultIsSuccess_ItShouldInvokeOnSuccess()
{ {
var result = Result<string, Error>.Ok("test"); var result = Result.Ok<string, Error>("test");
var invoked = false; var invoked = false;
result.Match(v => { invoked = true; }, _ => { }); result.Match(v => { invoked = true; }, _ => { });
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
@@ -151,16 +166,19 @@ public class ResultTTests
public async Task Match_WhenResultIsFailure_ItShouldInvokeOnFailure() public async Task Match_WhenResultIsFailure_ItShouldInvokeOnFailure()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<string, Error>.Fail(error); var result = Result.Fail<string, Error>(error);
var invoked = false; var invoked = false;
result.Match(_ => { }, e => { invoked = true; }); result.Match(_ => { }, e => { invoked = true; });
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
[Test] [Test]
public async Task Try_WhenFuncSucceeds_ItShouldReturnOkWithValue() public async Task Try_WhenFuncSucceeds_ItShouldReturnOkWithValue()
{ {
var result = Result<int, Error>.Try(() => 42); var result = Result.Try<int, Error>(() => 42);
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
await Assert.That(result.Value).IsEqualTo(42); await Assert.That(result.Value).IsEqualTo(42);
} }
@@ -168,7 +186,8 @@ public class ResultTTests
[Test] [Test]
public async Task Try_WhenFuncThrowsException_ItShouldReturnFailWithUnexpectedError() public async Task Try_WhenFuncThrowsException_ItShouldReturnFailWithUnexpectedError()
{ {
var result = Result<int, Error>.Try(() => throw new InvalidOperationException("test error")); var result = Result.Try<int, Error>(() => throw new InvalidOperationException("test error"));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError"); await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError");
} }
@@ -176,7 +195,8 @@ public class ResultTTests
[Test] [Test]
public async Task Try_WhenFuncThrowsException_ItShouldUseCustomError() public async Task Try_WhenFuncThrowsException_ItShouldUseCustomError()
{ {
var result = Result<int, Error>.Try(() => throw new InvalidOperationException("test"), ex => new Error("custom", ex.Message)); var result = Result.Try<int, Error>(() => throw new InvalidOperationException("test"), ex => new Error("custom", ex.Message));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("custom"); await Assert.That(result.Error.Code).IsEqualTo("custom");
} }
@@ -5,7 +5,8 @@ public class ResultTests
[Test] [Test]
public async Task Ok_WhenCalled_ItShouldReturnSuccessResult() public async Task Ok_WhenCalled_ItShouldReturnSuccessResult()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
await Assert.That(result.IsFailure).IsFalse(); await Assert.That(result.IsFailure).IsFalse();
} }
@@ -14,16 +15,17 @@ public class ResultTests
public async Task Fail_WhenCalled_ItShouldReturnFailureResult() public async Task Fail_WhenCalled_ItShouldReturnFailureResult()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<Unit, Error>.Fail(error); var result = Result.Fail<Unit, Error>(error);
await Assert.That(result.IsSuccess).IsFalse(); await Assert.That(result.IsSuccess).IsFalse();
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error).IsEqualTo(error); await Assert.That(result.Error).IsEqualTo(error);
} }
[Test] [Test]
public async Task Error_WhenResultIsSuccess_ItShouldThrowInvalidOperationException() public void Error_WhenResultIsSuccess_ItShouldThrowInvalidOperationException()
{ {
Assert.Throws<InvalidOperationException>(() => _ = Result<Unit, Error>.Ok(default).Error); Assert.Throws<InvalidOperationException>(() => _ = Result.Ok<Unit, Error>(default).Error);
} }
[Test] [Test]
@@ -31,6 +33,7 @@ public class ResultTests
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
Result<Unit, Error> result = error; Result<Unit, Error> result = error;
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error).IsEqualTo(error); await Assert.That(result.Error).IsEqualTo(error);
} }
@@ -38,42 +41,49 @@ public class ResultTests
[Test] [Test]
public async Task Map_WhenResultIsSuccess_ItShouldInvokeAction() public async Task Map_WhenResultIsSuccess_ItShouldInvokeAction()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var invoked = false; var invoked = false;
result.Map(_ => invoked = true); result.Map(_ => invoked = true);
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
[Test] [Test]
public async Task Map_WhenResultIsFailure_ItShouldSkipAction() public async Task Map_WhenResultIsFailure_ItShouldSkipAction()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var invoked = false; var invoked = false;
result.Map(_ => invoked = true); result.Map(_ => invoked = true);
await Assert.That(invoked).IsFalse(); await Assert.That(invoked).IsFalse();
} }
[Test] [Test]
public async Task Map_WhenCalled_ItShouldReturnOriginalResult() public async Task Map_WhenCalled_ItShouldReturnOriginalResult()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var mapped = result.Map(_ => { }); var mapped = result.Map(_ => { });
await Assert.That(mapped.IsSuccess).IsTrue(); await Assert.That(mapped.IsSuccess).IsTrue();
} }
[Test] [Test]
public async Task Bind_WhenResultIsSuccess_ItShouldInvokeBinder() public async Task Bind_WhenResultIsSuccess_ItShouldInvokeBinder()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var bound = result.Bind(_ => Result<Unit, Error>.Ok(default)); var bound = result.Bind(_ => Result.Ok<Unit, Error>(default));
await Assert.That(bound.IsSuccess).IsTrue(); await Assert.That(bound.IsSuccess).IsTrue();
} }
[Test] [Test]
public async Task Bind_WhenResultIsFailure_ItShouldReturnOriginalResult() public async Task Bind_WhenResultIsFailure_ItShouldReturnOriginalResult()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var bound = result.Bind(_ => Result<Unit, Error>.Ok(default)); var bound = result.Bind(_ => Result.Ok<Unit, Error>(default));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
await Assert.That(bound.Error).IsEqualTo(result.Error); await Assert.That(bound.Error).IsEqualTo(result.Error);
} }
@@ -81,33 +91,38 @@ public class ResultTests
[Test] [Test]
public async Task Bind_WhenResultIsSuccess_ItShouldReturnBinderResult() public async Task Bind_WhenResultIsSuccess_ItShouldReturnBinderResult()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var bound = result.Bind(_ => Result<Unit, Error>.Fail(new Error("new_error", "new message"))); var bound = result.Bind(_ => Result.Fail<Unit, Error>(new Error("new_error", "new message")));
await Assert.That(bound.IsFailure).IsTrue(); await Assert.That(bound.IsFailure).IsTrue();
} }
[Test] [Test]
public async Task Match_WhenResultIsSuccess_ItShouldReturnOnSuccessValue() public async Task Match_WhenResultIsSuccess_ItShouldReturnOnSuccessValue()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var matched = result.Match(_ => "success", _ => "failure"); var matched = result.Match(_ => "success", _ => "failure");
await Assert.That(matched).IsEqualTo("success"); await Assert.That(matched).IsEqualTo("success");
} }
[Test] [Test]
public async Task Match_WhenResultIsFailure_ItShouldReturnOnFailureValue() public async Task Match_WhenResultIsFailure_ItShouldReturnOnFailureValue()
{ {
var result = Result<Unit, Error>.Fail(new Error("code", "message")); var result = Result.Fail<Unit, Error>(new Error("code", "message"));
var matched = result.Match(_ => "success", e => e.Code); var matched = result.Match(_ => "success", e => e.Code);
await Assert.That(matched).IsEqualTo("code"); await Assert.That(matched).IsEqualTo("code");
} }
[Test] [Test]
public async Task Match_WhenResultIsSuccess_ItShouldInvokeOnSuccess() public async Task Match_WhenResultIsSuccess_ItShouldInvokeOnSuccess()
{ {
var result = Result<Unit, Error>.Ok(default); var result = Result.Ok<Unit, Error>(default);
var invoked = false; var invoked = false;
result.Match(_ => invoked = true, _ => { }); result.Match(_ => invoked = true, _ => { });
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
@@ -115,23 +130,27 @@ public class ResultTests
public async Task Match_WhenResultIsFailure_ItShouldInvokeOnFailure() public async Task Match_WhenResultIsFailure_ItShouldInvokeOnFailure()
{ {
var error = new Error("code", "message"); var error = new Error("code", "message");
var result = Result<Unit, Error>.Fail(error); var result = Result.Fail<Unit, Error>(error);
var invoked = false; var invoked = false;
result.Match(_ => { }, e => invoked = true); result.Match(_ => { }, e => invoked = true);
await Assert.That(invoked).IsTrue(); await Assert.That(invoked).IsTrue();
} }
[Test] [Test]
public async Task Try_WhenActionSucceeds_ItShouldReturnOk() public async Task Try_WhenActionSucceeds_ItShouldReturnOk()
{ {
var result = Result<Unit, Error>.Try(() => default(Unit)); var result = Result.Try<Unit, Error>(() => default);
await Assert.That(result.IsSuccess).IsTrue(); await Assert.That(result.IsSuccess).IsTrue();
} }
[Test] [Test]
public async Task Try_WhenActionThrowsException_ItShouldReturnFailWithUnexpectedError() public async Task Try_WhenActionThrowsException_ItShouldReturnFailWithUnexpectedError()
{ {
var result = Result<Unit, Error>.Try(() => throw new Exception("test error")); var result = Result.Try<Unit, Error>(() => throw new Exception("test error"));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError"); await Assert.That(result.Error.Code).IsEqualTo("UnexpectedError");
} }
@@ -139,7 +158,8 @@ public class ResultTests
[Test] [Test]
public async Task Try_WhenActionThrowsException_ItShouldUseCustomError() public async Task Try_WhenActionThrowsException_ItShouldUseCustomError()
{ {
var result = Result<Unit, Error>.Try(() => throw new Exception("test"), ex => new Error("custom", ex.Message)); var result = Result.Try<Unit, Error>(() => throw new Exception("test"), ex => new Error("custom", ex.Message));
await Assert.That(result.IsFailure).IsTrue(); await Assert.That(result.IsFailure).IsTrue();
await Assert.That(result.Error.Code).IsEqualTo("custom"); await Assert.That(result.Error.Code).IsEqualTo("custom");
} }