Skip to content

Commit

Permalink
Refactor Error and Result classes to improve error handling and add c…
Browse files Browse the repository at this point in the history
…onstructors with messages
  • Loading branch information
iPazooki committed Jan 31, 2025
1 parent 7f589a7 commit 8b04f75
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 12 deletions.
36 changes: 35 additions & 1 deletion DomainValidation.UnitTests/ResultGenericTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
public class ResultGenericTests
{
[Fact]
public void ResultWithValue_Success_ReturnsValue()
public void ResultWithValue_Success_NoError_ReturnsValue()
{
var result = new Result<int>(42, true, Error.None);
Assert.True(result.IsSuccess);
Expand Down Expand Up @@ -33,4 +33,38 @@ public void ImplicitConversionToResult_Success_ReturnsResult()
Assert.True(result.IsSuccess);
Assert.Equal(42, result.Value);
}

[Fact]
public void ResultWithValueAndMessage_Success_ReturnsValue()
{
var result = new Result<int>(42, true, "Operation successful");
Assert.True(result.IsSuccess);
Assert.Equal(42, result.Value);
Assert.Empty(result.Errors);
}

[Fact]
public void ResultWithValueAndMessage_Failure_ReturnsErrors()
{
var result = new Result<int>(0, false, "Operation failed");
Assert.False(result.IsSuccess);
Assert.Contains(result.Errors, e => e.Message == "Operation failed");
}

[Fact]
public void ResultWithValue_Success_ReturnsValue()
{
var result = new Result<int>(42, true);
Assert.True(result.IsSuccess);
Assert.Equal(42, result.Value);
Assert.Empty(result.Errors);
}

[Fact]
public void ResultWithValue_Failure_ReturnsErrors()
{
var result = new Result<int>(0, false);
Assert.False(result.IsSuccess);
Assert.False(result.Errors.Any());
}
}
39 changes: 36 additions & 3 deletions DomainValidation.UnitTests/ResultTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ public void Result_Failure_ReturnsIsSuccessFalse()
[Fact]
public void Result_SuccessWithErrors_ThrowsInvalidOperationException()
{
Assert.Throws<InvalidOperationException>(() => new Result(true, new Error("Some error", "Something went wrong")));
Assert.Throws<InvalidOperationException>(
() => new Result(true, new Error("Some error", "Something went wrong")));
}

[Fact]
Expand Down Expand Up @@ -51,7 +52,7 @@ public void ResultGeneric_Failure_ThrowsInvalidOperationException()
public void ResultGeneric_SuccessWithNullValue_ThrowsNullReferenceException()
{
var result = Result.Success<string?>(null);

Assert.True(result.IsSuccess);
Assert.Throws<NullReferenceException>(() => result.Value);
}
Expand All @@ -63,4 +64,36 @@ public void ResultGeneric_ImplicitConversionToResult_Success_ReturnsResult()
Assert.True(result.IsSuccess);
Assert.Equal(42, result.Value);
}
}

[Fact]
public void Result_ConstructorWithMessage_Success_ReturnsIsSuccessTrue()
{
var result = new Result(true, "Operation successful");
Assert.True(result.IsSuccess);
Assert.Empty(result.Errors);
}

[Fact]
public void Result_ConstructorWithoutMessage_Success_ReturnsIsSuccessTrue()
{
var result = new Result(true);
Assert.True(result.IsSuccess);
Assert.Empty(result.Errors);
}

[Fact]
public void Result_ConstructorWithMessage_Failure_ReturnsIsSuccessFalse()
{
var result = new Result(false, "Operation failed");
Assert.False(result.IsSuccess);
Assert.Contains(result.Errors, e => e.Message == "Operation failed");
}

[Fact]
public void Result_ConstructorWithoutMessage_Failure_ReturnsIsSuccessFalse()
{
var result = new Result(false);
Assert.False(result.IsSuccess);
Assert.False(result.Errors.Any());
}
}
6 changes: 3 additions & 3 deletions DomainValidation/DomainValidation.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
<PackageIcon>exception-result.jpg</PackageIcon>
<RepositoryType>git</RepositoryType>
<PackageTags>domain-validation; validation; exception-handling</PackageTags>
<Version>2.0.0</Version>
<Version>2.1.0</Version>
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
<PackageId>$(AssemblyName).NET</PackageId>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<RepositoryUrl>https://github.com/iPazooki/DomainValidation</RepositoryUrl>
<AssemblyVersion>2.0.0</AssemblyVersion>
<FileVersion>2.0.0</FileVersion>
<AssemblyVersion>2.1.0</AssemblyVersion>
<FileVersion>2.1.0</FileVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

Expand Down
6 changes: 3 additions & 3 deletions DomainValidation/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ namespace DomainValidation;
/// <summary>
/// Represents an error that occurred during the execution of a program.
/// </summary>
/// <param name="Code">The error code.</param>
/// <param name="Message">The error message.</param>
/// <param name="Code">The error code. Default is an empty null.</param>
/// <param name="LineNumber">The line number where the error occurred. Default is 0.</param>
/// <param name="MemberName">The name of the member where the error occurred. Default is an empty string.</param>
/// <param name="FilePath">The file path where the error occurred. Default is an empty string.</param>
public record Error(
string Code,
string Message,
string? Code = null,
[CallerLineNumber] int LineNumber = 0,
[CallerMemberName] string MemberName = "",
[CallerFilePath] string FilePath = "")
{
/// <summary>
/// Represents an instance of the <see cref="Error"/> class that indicates no error occurred.
/// </summary>
public static readonly Error None = new(string.Empty, string.Empty);
public static readonly Error None = new(string.Empty);
}
24 changes: 23 additions & 1 deletion DomainValidation/Result.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,27 @@ public Result(bool isSuccess, params Error[] errors)
IsSuccess = isSuccess;
Errors = errors.All(e => !string.IsNullOrEmpty(e.Code) && !string.IsNullOrEmpty(e.Message))
? errors
: Enumerable.Empty<Error>();
: [];
break;
}
}

/// <summary>
/// Initializes a new instance of the <see cref="Result"/> class.
/// </summary>
/// <param name="isSuccess">A value indicating whether the operation succeeded.</param>
/// <param name="message">The message associated with the result.</param>
public Result(bool isSuccess, string? message)
{
switch (isSuccess)
{
case true:
IsSuccess = isSuccess;
Errors = [];
break;
case false:
IsSuccess = isSuccess;
Errors = !string.IsNullOrEmpty(message) ? [new Error(message)] : [];
break;
}
}
Expand Down Expand Up @@ -62,6 +82,8 @@ public Result(bool isSuccess, params Error[] errors)
/// <returns>A new instance of the <see cref="Result"/> class that indicates failure.</returns>
public static Result Failure(params Error[] errors) => new(false, errors);

public static Result Failure(string message) => new(false, message);

/// <summary>
/// Creates a new instance of the <see cref="Result{TValue}"/> class that indicates failure.
/// </summary>
Expand Down
17 changes: 16 additions & 1 deletion DomainValidation/ResultGeneric.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
/// Represents the result of a domain validation that can either succeed or fail, with an optional value.
/// </summary>
/// <typeparam name="TValue">The type of the value.</typeparam>
public class Result<TValue> : Result
public sealed class Result<TValue> : Result
{
private readonly TValue? _value;

Expand All @@ -15,6 +15,21 @@ public class Result<TValue> : Result
/// <param name="isSuccess">A value indicating whether the operation succeeded.</param>
/// <param name="errors">The errors that occurred, if any.</param>
public Result(TValue? value, bool isSuccess, params Error[] errors) : base(isSuccess, errors) => _value = value;

/// <summary>
/// Initializes a new instance of the <see cref="Result{TValue}"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="isSuccess">A value indicating whether the operation succeeded.</param>
public Result(TValue? value, bool isSuccess) : base(isSuccess, string.Empty) => _value = value;

/// <summary>
/// Initializes a new instance of the <see cref="Result{TValue}"/> class.
/// </summary>
/// <param name="value">The value.</param>
/// <param name="isSuccess">A value indicating whether the operation succeeded.</param>
/// <param name="message">The message associated with the result.</param>
public Result(TValue? value, bool isSuccess, string message) : base(isSuccess, message) => _value = value;

/// <summary>
/// Gets the value of the result.
Expand Down

0 comments on commit 8b04f75

Please sign in to comment.