diff --git a/GFramework.Core.Tests/extensions/ResultExtensionsTests.cs b/GFramework.Core.Tests/extensions/ResultExtensionsTests.cs
new file mode 100644
index 0000000..a65a2c4
--- /dev/null
+++ b/GFramework.Core.Tests/extensions/ResultExtensionsTests.cs
@@ -0,0 +1,496 @@
+// Copyright (c) 2025 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using GFramework.Core.Extensions;
+using GFramework.Core.Functional;
+using NUnit.Framework;
+
+namespace GFramework.Core.Tests.Extensions;
+
+///
+/// ResultExtensions 扩展方法测试类
+///
+[TestFixture]
+public class ResultExtensionsTests
+{
+ [Test]
+ public void Combine_Should_Return_Success_List_When_All_Results_Succeed()
+ {
+ var results = new[]
+ {
+ Result.Succeed(1),
+ Result.Succeed(2),
+ Result.Succeed(3)
+ };
+ var combined = results.Combine();
+ Assert.That(combined.IsSuccess, Is.True);
+ var list = combined.Match(succ: v => v, fail: _ => new List());
+ Assert.That(list, Has.Count.EqualTo(3));
+ Assert.That(list, Is.EqualTo(new[] { 1, 2, 3 }));
+ }
+
+ [Test]
+ public void Combine_Should_Return_First_Failure_When_Any_Result_Fails()
+ {
+ var exception = new Exception("Error");
+ var results = new[]
+ {
+ Result.Succeed(1),
+ Result.Fail(exception),
+ Result.Succeed(3)
+ };
+ var combined = results.Combine();
+ Assert.That(combined.IsFaulted, Is.True);
+ Assert.That(combined.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Combine_Should_Handle_Empty_Collection()
+ {
+ var results = Array.Empty>();
+ var combined = results.Combine();
+ Assert.That(combined.IsSuccess, Is.True);
+ var list = combined.Match(succ: v => v, fail: _ => new List());
+ Assert.That(list, Is.Empty);
+ }
+
+ [Test]
+ public void Combine_Should_Throw_ArgumentNullException_When_Collection_Is_Null()
+ {
+ Assert.Throws(() => ((IEnumerable>)null!).Combine());
+ }
+
+ [Test]
+ public void Combine_Should_Preserve_Order_Of_Values()
+ {
+ var results = new[]
+ {
+ Result.Succeed(3),
+ Result.Succeed(1),
+ Result.Succeed(2)
+ };
+ var combined = results.Combine();
+ var list = combined.Match(succ: v => v, fail: _ => new List());
+ Assert.That(list, Is.EqualTo(new[] { 3, 1, 2 }));
+ }
+
+ [Test]
+ public void Combine_Should_Short_Circuit_On_First_Failure()
+ {
+ var callCount = 0;
+ var results = new[]
+ {
+ Result.Succeed(1),
+ Result.Fail(new Exception("Error")),
+ Result.Try(() =>
+ {
+ callCount++;
+ return 3;
+ })
+ };
+ var combined = results.Combine();
+ Assert.That(combined.IsFaulted, Is.True);
+ }
+
+ [Test]
+ public void Map_Should_Transform_Success_Value()
+ {
+ var result = Result.Succeed(42);
+ var mapped = result.Map(x => x.ToString());
+ Assert.That(mapped.IsSuccess, Is.True);
+ Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
+ }
+
+ [Test]
+ public void Map_Should_Propagate_Failure()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var mapped = result.Map(x => x.ToString());
+ Assert.That(mapped.IsFaulted, Is.True);
+ Assert.That(mapped.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Map_Should_Throw_ArgumentNullException_When_Mapper_Is_Null()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.Map(null!));
+ }
+
+ [Test]
+ public void Bind_Should_Chain_Success_Results()
+ {
+ var result = Result.Succeed(42);
+ var bound = result.Bind(x => Result.Succeed(x.ToString()));
+ Assert.That(bound.IsSuccess, Is.True);
+ Assert.That(bound.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
+ }
+
+ [Test]
+ public void Bind_Should_Propagate_Failure()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var bound = result.Bind(x => Result.Succeed(x.ToString()));
+ Assert.That(bound.IsFaulted, Is.True);
+ Assert.That(bound.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Bind_Should_Throw_ArgumentNullException_When_Binder_Is_Null()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.Bind(null!));
+ }
+
+ [Test]
+ public void OnSuccess_Should_Execute_Action_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var executed = false;
+ result.OnSuccess(_ => executed = true);
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void OnSuccess_Should_Not_Execute_Action_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var executed = false;
+ result.OnSuccess(_ => executed = true);
+ Assert.That(executed, Is.False);
+ }
+
+ [Test]
+ public void OnSuccess_Should_Return_Original_Result_For_Chaining()
+ {
+ var result = Result.Succeed(42);
+ var returned = result.OnSuccess(_ => { });
+ Assert.That(returned, Is.EqualTo(result));
+ }
+
+ [Test]
+ public void OnSuccess_Should_Throw_ArgumentNullException_When_Action_Is_Null()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.OnSuccess(null!));
+ }
+
+ [Test]
+ public void OnFailure_Should_Execute_Action_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var executed = false;
+ result.OnFailure(_ => executed = true);
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void OnFailure_Should_Not_Execute_Action_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var executed = false;
+ result.OnFailure(_ => executed = true);
+ Assert.That(executed, Is.False);
+ }
+
+ [Test]
+ public void OnFailure_Should_Return_Original_Result_For_Chaining()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var returned = result.OnFailure(_ => { });
+ Assert.That(returned, Is.EqualTo(result));
+ }
+
+ [Test]
+ public void OnFailure_Should_Throw_ArgumentNullException_When_Action_Is_Null()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ Assert.Throws(() => result.OnFailure(null!));
+ }
+
+ [Test]
+ public void Ensure_Should_Return_Success_When_Predicate_Is_True()
+ {
+ var result = Result.Succeed(42);
+ var ensured = result.Ensure(x => x > 0, "Value must be positive");
+ Assert.That(ensured.IsSuccess, Is.True);
+ }
+
+ [Test]
+ public void Ensure_Should_Return_Failure_When_Predicate_Is_False()
+ {
+ var result = Result.Succeed(-1);
+ var ensured = result.Ensure(x => x > 0, "Value must be positive");
+ Assert.That(ensured.IsFaulted, Is.True);
+ Assert.That(ensured.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Ensure_Should_Propagate_Existing_Failure()
+ {
+ var exception = new Exception("Original error");
+ var result = Result.Fail(exception);
+ var ensured = result.Ensure(x => x > 0, "Value must be positive");
+ Assert.That(ensured.IsFaulted, Is.True);
+ Assert.That(ensured.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Ensure_Should_Create_ArgumentException_With_Message()
+ {
+ var result = Result.Succeed(-1);
+ var ensured = result.Ensure(x => x > 0, "Value must be positive");
+ Assert.That(ensured.Exception.Message, Is.EqualTo("Value must be positive"));
+ }
+
+ [Test]
+ public void Ensure_Should_Throw_ArgumentNullException_When_Predicate_Is_Null()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.Ensure(null!, "Error"));
+ }
+
+ [Test]
+ public void Ensure_Should_Throw_ArgumentException_When_Message_Is_NullOrWhiteSpace()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.Ensure(x => true, ""));
+ Assert.Throws(() => result.Ensure(x => true, " "));
+ }
+
+ [Test]
+ public void Ensure_WithFactory_Should_Return_Success_When_Predicate_Is_True()
+ {
+ var result = Result.Succeed(42);
+ var ensured = result.Ensure(x => x > 0, _ => new InvalidOperationException("Error"));
+ Assert.That(ensured.IsSuccess, Is.True);
+ }
+
+ [Test]
+ public void Ensure_WithFactory_Should_Return_Failure_With_Custom_Exception()
+ {
+ var result = Result.Succeed(-1);
+ var ensured = result.Ensure(x => x > 0, _ => new InvalidOperationException("Custom error"));
+ Assert.That(ensured.IsFaulted, Is.True);
+ Assert.That(ensured.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Ensure_WithFactory_Should_Pass_Value_To_Exception_Factory()
+ {
+ var result = Result.Succeed(-1);
+ var capturedValue = 0;
+ result.Ensure(x => x > 0, v =>
+ {
+ capturedValue = v;
+ return new Exception("Error");
+ });
+ Assert.That(capturedValue, Is.EqualTo(-1));
+ }
+
+ [Test]
+ public void Ensure_WithFactory_Should_Propagate_Existing_Failure()
+ {
+ var exception = new Exception("Original error");
+ var result = Result.Fail(exception);
+ var ensured = result.Ensure(x => x > 0, _ => new InvalidOperationException("Error"));
+ Assert.That(ensured.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Ensure_WithFactory_Should_Throw_ArgumentNullException_When_Parameters_Are_Null()
+ {
+ var result = Result.Succeed(42);
+ Assert.Throws(() => result.Ensure(null!, (Func)(_ => new Exception())));
+ Assert.Throws(() => result.Ensure(x => true, (Func)null!));
+ }
+
+ [Test]
+ public void Try_Should_Return_Success_When_Function_Succeeds()
+ {
+ var result = ResultExtensions.Try(() => 42);
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void Try_Should_Return_Failure_When_Function_Throws()
+ {
+ var result = ResultExtensions.Try(() => throw new InvalidOperationException("Error"));
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Try_Should_Throw_ArgumentNullException_When_Function_Is_Null()
+ {
+ Assert.Throws(() => ResultExtensions.Try(null!));
+ }
+
+ [Test]
+ public async Task TryAsync_Should_Return_Success_When_Async_Function_Succeeds()
+ {
+ var result = await ResultExtensions.TryAsync(async () =>
+ {
+ await Task.Delay(1);
+ return 42;
+ });
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public async Task TryAsync_Should_Return_Failure_When_Async_Function_Throws()
+ {
+ var result = await ResultExtensions.TryAsync(async () =>
+ {
+ await Task.Delay(1);
+ throw new InvalidOperationException("Error");
+ });
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public async Task TryAsync_Should_Handle_Synchronous_Exceptions()
+ {
+ var result = await ResultExtensions.TryAsync(() => throw new InvalidOperationException("Sync error"));
+ Assert.That(result.IsFaulted, Is.True);
+ }
+
+ [Test]
+ public void TryAsync_Should_Throw_ArgumentNullException_When_Function_Is_Null()
+ {
+ Assert.ThrowsAsync(async () => await ResultExtensions.TryAsync(null!));
+ }
+
+ [Test]
+ public void ToNullable_Should_Return_Value_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var nullable = result.ToNullable();
+ Assert.That(nullable, Is.EqualTo(42));
+ }
+
+ [Test]
+ public void ToNullable_Should_Return_Null_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var nullable = result.ToNullable();
+ Assert.That(nullable, Is.Null);
+ }
+
+ [Test]
+ public void ToNullable_Should_Work_With_Value_Types()
+ {
+ var result = Result.Succeed(new DateTime(2025, 1, 1, 0, 0, 0, DateTimeKind.Utc));
+ var nullable = result.ToNullable();
+ Assert.That(nullable, Is.Not.Null);
+ Assert.That(nullable!.Value.Year, Is.EqualTo(2025));
+ Assert.That(nullable!.Value.Kind, Is.EqualTo(DateTimeKind.Utc)); // 验证 DateTimeKind
+ }
+
+
+ [Test]
+ public void ToResult_Should_Return_Success_When_Value_Is_Not_Null()
+ {
+ string value = "test";
+ var result = value.ToResult();
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => ""), Is.EqualTo("test"));
+ }
+
+ [Test]
+ public void ToResult_Should_Return_Failure_When_Value_Is_Null()
+ {
+ string? value = null;
+ var result = value.ToResult();
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void ToResult_Should_Use_Custom_Error_Message()
+ {
+ string? value = null;
+ var result = value.ToResult("Custom error");
+ Assert.That(result.Exception.Message, Does.Contain("Custom error"));
+ }
+
+ [Test]
+ public void ToResult_Should_Return_Success_When_Nullable_Has_Value()
+ {
+ int? value = 42;
+ var result = value.ToResult();
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void ToResult_Should_Return_Failure_When_Nullable_Is_Null()
+ {
+ int? value = null;
+ var result = value.ToResult();
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void ToResult_Should_Use_Custom_Error_Message_For_Nullable()
+ {
+ int? value = null;
+ var result = value.ToResult("Custom nullable error");
+ Assert.That(result.Exception.Message, Does.Contain("Custom nullable error"));
+ }
+
+ [Test]
+ public void Should_Support_Complex_Chaining_With_Multiple_Operations()
+ {
+ var result = Result.Succeed(10)
+ .Map(x => x * 2)
+ .Bind(x => Result.Succeed(x + 5))
+ .Ensure(x => x > 20, "Value must be greater than 20")
+ .OnSuccess(x => Console.WriteLine($"Success: {x}"))
+ .OnFailure(ex => Console.WriteLine($"Error: {ex.Message}"));
+
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(25));
+ }
+
+ [Test]
+ public void Should_Support_OnSuccess_And_OnFailure_Chaining()
+ {
+ var successCount = 0;
+ var failureCount = 0;
+
+ Result.Succeed(42)
+ .OnSuccess(_ => successCount++)
+ .OnFailure(_ => failureCount++);
+
+ Assert.That(successCount, Is.EqualTo(1));
+ Assert.That(failureCount, Is.EqualTo(0));
+ }
+
+ [Test]
+ public void Should_Support_Ensure_Chaining_With_Multiple_Conditions()
+ {
+ var result = Result.Succeed(50)
+ .Ensure(x => x > 0, "Must be positive")
+ .Ensure(x => x < 100, "Must be less than 100")
+ .Ensure(x => x % 2 == 0, "Must be even");
+
+ Assert.That(result.IsSuccess, Is.True);
+ }
+}
\ No newline at end of file
diff --git a/GFramework.Core.Tests/functional/ResultTTests.cs b/GFramework.Core.Tests/functional/ResultTTests.cs
new file mode 100644
index 0000000..7ccbcf1
--- /dev/null
+++ b/GFramework.Core.Tests/functional/ResultTTests.cs
@@ -0,0 +1,603 @@
+// Copyright (c) 2025 GeWuYou
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+using GFramework.Core.Functional;
+using NUnit.Framework;
+
+namespace GFramework.Core.Tests.Functional;
+
+///
+/// Result<A> 泛型类型测试类
+///
+[TestFixture]
+public class ResultTTests
+{
+ [Test]
+ public void Constructor_WithValue_Should_Create_Success_Result()
+ {
+ var result = new Result(42);
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.IsFaulted, Is.False);
+ Assert.That(result.IsBottom, Is.False);
+ }
+
+ [Test]
+ public void Constructor_WithException_Should_Create_Faulted_Result()
+ {
+ var exception = new InvalidOperationException("Test error");
+ var result = new Result(exception);
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.IsSuccess, Is.False);
+ Assert.That(result.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Constructor_WithNullException_Should_Throw_ArgumentNullException()
+ {
+ Assert.Throws(() => new Result(null!));
+ }
+
+ [Test]
+ public void DefaultConstructor_Should_Create_Bottom_State()
+ {
+ var result = new Result();
+ Assert.That(result.IsBottom, Is.True);
+ Assert.That(result.IsSuccess, Is.False);
+ Assert.That(result.IsFaulted, Is.False);
+ }
+
+ [Test]
+ public void ImplicitConversion_Should_Create_Success_Result()
+ {
+ Result result = 42;
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void IsSuccess_Should_Return_True_When_Result_Is_Successful()
+ {
+ var result = Result.Succeed(42);
+ Assert.That(result.IsSuccess, Is.True);
+ }
+
+ [Test]
+ public void IsFaulted_Should_Return_True_When_Result_Is_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ Assert.That(result.IsFaulted, Is.True);
+ }
+
+ [Test]
+ public void IsBottom_Should_Return_True_When_Result_Is_Bottom()
+ {
+ var result = Result.Bottom;
+ Assert.That(result.IsBottom, Is.True);
+ }
+
+ [Test]
+ public void Exception_Should_Return_Exception_When_Faulted()
+ {
+ var exception = new InvalidOperationException("Test");
+ var result = Result.Fail(exception);
+ Assert.That(result.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Exception_Should_Return_InvalidOperationException_When_Bottom()
+ {
+ var result = Result.Bottom;
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Exception_Should_Return_InvalidOperationException_When_Success()
+ {
+ var result = Result.Succeed(42);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Succeed_Should_Create_Successful_Result()
+ {
+ var result = Result.Succeed(42);
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void Success_Should_Create_Successful_Result()
+ {
+ var result = Result.Success(42);
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void Fail_Should_Create_Faulted_Result()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Failure_WithException_Should_Create_Faulted_Result()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Failure(exception);
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Failure_WithMessage_Should_Create_Faulted_Result()
+ {
+ var message = "Test error";
+ var result = Result.Failure(message);
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception.Message, Is.EqualTo(message));
+ }
+
+ [Test]
+ public void Try_Should_Return_Success_When_Function_Succeeds()
+ {
+ var result = Result.Try(() => 42);
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(42));
+ }
+
+ [Test]
+ public void Try_Should_Return_Failure_When_Function_Throws()
+ {
+ var result = Result.Try(() => throw new InvalidOperationException("Error"));
+ Assert.That(result.IsFaulted, Is.True);
+ Assert.That(result.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void IfFail_WithDefaultValue_Should_Return_Value_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var value = result.IfFail(0);
+ Assert.That(value, Is.EqualTo(42));
+ }
+
+ [Test]
+ public void IfFail_WithDefaultValue_Should_Return_Default_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var value = result.IfFail(99);
+ Assert.That(value, Is.EqualTo(99));
+ }
+
+ [Test]
+ public void IfFail_WithFunction_Should_Return_Value_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var value = result.IfFail(_ => 0);
+ Assert.That(value, Is.EqualTo(42));
+ }
+
+ [Test]
+ public void IfFail_WithFunction_Should_Execute_Function_When_Faulted()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var executed = false;
+ var value = result.IfFail(ex =>
+ {
+ executed = true;
+ return 99;
+ });
+ Assert.That(executed, Is.True);
+ Assert.That(value, Is.EqualTo(99));
+ }
+
+ [Test]
+ public void IfFail_WithAction_Should_Execute_Action_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var executed = false;
+ result.IfFail(_ => executed = true);
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void IfSucc_Should_Execute_Action_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var executed = false;
+ result.IfSucc(_ => executed = true);
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void IfSucc_Should_Not_Execute_Action_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var executed = false;
+ result.IfSucc(_ => executed = true);
+ Assert.That(executed, Is.False);
+ }
+
+ [Test]
+ public void Map_Should_Transform_Value_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var mapped = result.Map(x => x.ToString());
+ Assert.That(mapped.IsSuccess, Is.True);
+ Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
+ }
+
+ [Test]
+ public void Map_Should_Propagate_Exception_When_Faulted()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var mapped = result.Map(x => x.ToString());
+ Assert.That(mapped.IsFaulted, Is.True);
+ Assert.That(mapped.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Map_Should_Handle_Null_Result_From_Mapper()
+ {
+ var result = Result.Succeed(42);
+ var mapped = result.Map(_ => null!);
+ Assert.That(mapped.IsSuccess, Is.True);
+ }
+
+ [Test]
+ public void Map_Should_Not_Throw_When_Mapper_Returns_Null()
+ {
+ var result = Result.Succeed("test");
+ Assert.DoesNotThrow(() => result.Map(_ => null));
+ }
+
+ [Test]
+ public void Bind_Should_Chain_Success_Results()
+ {
+ var result = Result.Succeed(42);
+ var bound = result.Bind(x => Result.Succeed(x.ToString()));
+ Assert.That(bound.IsSuccess, Is.True);
+ Assert.That(bound.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
+ }
+
+ [Test]
+ public void Bind_Should_Propagate_First_Failure()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var bound = result.Bind(x => Result.Succeed(x.ToString()));
+ Assert.That(bound.IsFaulted, Is.True);
+ Assert.That(bound.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Bind_Should_Propagate_Second_Failure()
+ {
+ var result = Result.Succeed(42);
+ var exception = new Exception("Bind error");
+ var bound = result.Bind(x => Result.Fail(exception));
+ Assert.That(bound.IsFaulted, Is.True);
+ Assert.That(bound.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public void Bind_Should_Handle_Complex_Chaining()
+ {
+ var result = Result.Succeed(10)
+ .Bind(x => Result.Succeed(x * 2))
+ .Bind(x => Result.Succeed(x + 5));
+ Assert.That(result.IsSuccess, Is.True);
+ Assert.That(result.Match(succ: v => v, fail: _ => 0), Is.EqualTo(25));
+ }
+
+ [Test]
+ public async Task MapAsync_Should_Transform_Value_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var mapped = await result.MapAsync(async x =>
+ {
+ await Task.Delay(1);
+ return x.ToString();
+ });
+ Assert.That(mapped.IsSuccess, Is.True);
+ Assert.That(mapped.Match(succ: v => v, fail: _ => ""), Is.EqualTo("42"));
+ }
+
+ [Test]
+ public async Task MapAsync_Should_Propagate_Exception_When_Faulted()
+ {
+ var exception = new Exception("Error");
+ var result = Result.Fail(exception);
+ var mapped = await result.MapAsync(async x =>
+ {
+ await Task.Delay(1);
+ return x.ToString();
+ });
+ Assert.That(mapped.IsFaulted, Is.True);
+ Assert.That(mapped.Exception, Is.SameAs(exception));
+ }
+
+ [Test]
+ public async Task MapAsync_Should_Handle_Async_Exceptions()
+ {
+ var result = Result.Succeed(42);
+ var mapped = await result.MapAsync(async _ =>
+ {
+ await Task.Delay(1);
+ throw new InvalidOperationException("Async error");
+ });
+ Assert.That(mapped.IsFaulted, Is.True);
+ Assert.That(mapped.Exception, Is.TypeOf());
+ }
+
+ [Test]
+ public void Match_WithFunctions_Should_Execute_Succ_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var value = result.Match(succ: x => x * 2, fail: _ => 0);
+ Assert.That(value, Is.EqualTo(84));
+ }
+
+ [Test]
+ public void Match_WithFunctions_Should_Execute_Fail_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var value = result.Match(succ: x => x, fail: _ => 99);
+ Assert.That(value, Is.EqualTo(99));
+ }
+
+ [Test]
+ public void Match_WithActions_Should_Execute_Succ_When_Success()
+ {
+ var result = Result.Succeed(42);
+ var executed = false;
+ result.Match(succ: _ => executed = true, fail: _ => { });
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void Match_WithActions_Should_Execute_Fail_When_Faulted()
+ {
+ var result = Result.Fail(new Exception("Error"));
+ var executed = false;
+ result.Match(succ: _ => { }, fail: _ => executed = true);
+ Assert.That(executed, Is.True);
+ }
+
+ [Test]
+ public void Match_Should_Handle_Bottom_State_Correctly()
+ {
+ var result = Result.Bottom;
+ var value = result.Match(succ: x => x, fail: _ => 99);
+ Assert.That(value, Is.EqualTo(99));
+ }
+
+ [Test]
+ public void Equals_Should_Return_True_When_Both_Success_With_Same_Value()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Succeed(42);
+ Assert.That(result1.Equals(result2), Is.True);
+ }
+
+ [Test]
+ public void Equals_Should_Return_False_When_Success_Values_Differ()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Succeed(43);
+ Assert.That(result1.Equals(result2), Is.False);
+ }
+
+ [Test]
+ public void Equals_Should_Return_True_When_Both_Faulted_With_Same_Exception()
+ {
+ var result1 = Result.Fail(new InvalidOperationException("Error"));
+ var result2 = Result.Fail(new InvalidOperationException("Error"));
+ Assert.That(result1.Equals(result2), Is.True);
+ }
+
+ [Test]
+ public void Equals_Should_Return_False_When_Exception_Types_Differ()
+ {
+ var result1 = Result.Fail(new InvalidOperationException("Error"));
+ var result2 = Result.Fail(new ArgumentException("Error"));
+ Assert.That(result1.Equals(result2), Is.False);
+ }
+
+ [Test]
+ public void Equals_Should_Return_True_When_Both_Bottom()
+ {
+ var result1 = Result.Bottom;
+ var result2 = new Result();
+ Assert.That(result1.Equals(result2), Is.True);
+ }
+
+ [Test]
+ public void Equals_Should_Return_False_When_States_Differ()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Fail(new Exception("Error"));
+ Assert.That(result1.Equals(result2), Is.False);
+ }
+
+ [Test]
+ public void GetHashCode_Should_Be_Consistent_For_Equal_Results()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Succeed(42);
+ Assert.That(result1.GetHashCode(), Is.EqualTo(result2.GetHashCode()));
+ }
+
+ [Test]
+ public void OperatorEquals_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Succeed(42);
+ var result3 = Result.Succeed(43);
+ Assert.That(result1 == result2, Is.True);
+ Assert.That(result1 == result3, Is.False);
+ }
+
+ [Test]
+ public void OperatorNotEquals_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(42);
+ var result2 = Result.Succeed(43);
+ Assert.That(result1 != result2, Is.True);
+ }
+
+ [Test]
+ public void CompareTo_Should_Order_Bottom_Before_Faulted()
+ {
+ var bottom = Result.Bottom;
+ var faulted = Result.Fail(new Exception("Error"));
+ Assert.That(bottom.CompareTo(faulted), Is.LessThan(0));
+ }
+
+ [Test]
+ public void CompareTo_Should_Order_Faulted_Before_Success()
+ {
+ var faulted = Result.Fail(new Exception("Error"));
+ var success = Result.Succeed(42);
+ Assert.That(faulted.CompareTo(success), Is.LessThan(0));
+ }
+
+ [Test]
+ public void CompareTo_Should_Compare_Success_Values_When_Both_Success()
+ {
+ var result1 = Result.Succeed(10);
+ var result2 = Result.Succeed(20);
+ Assert.That(result1.CompareTo(result2), Is.LessThan(0));
+ }
+
+ [Test]
+ public void CompareTo_Should_Return_Zero_When_Both_Faulted()
+ {
+ var result1 = Result.Fail(new Exception("Error1"));
+ var result2 = Result.Fail(new Exception("Error2"));
+ Assert.That(result1.CompareTo(result2), Is.EqualTo(0));
+ }
+
+ [Test]
+ public void CompareTo_Should_Return_Zero_When_Both_Bottom()
+ {
+ var result1 = Result.Bottom;
+ var result2 = new Result();
+ Assert.That(result1.CompareTo(result2), Is.EqualTo(0));
+ }
+
+ [Test]
+ public void OperatorLessThan_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(10);
+ var result2 = Result.Succeed(20);
+ Assert.That(result1 < result2, Is.True);
+ }
+
+ [Test]
+ public void OperatorLessThanOrEqual_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(10);
+ var result2 = Result.Succeed(10);
+ Assert.That(result1 <= result2, Is.True);
+ }
+
+ [Test]
+ public void OperatorGreaterThan_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(20);
+ var result2 = Result.Succeed(10);
+ Assert.That(result1 > result2, Is.True);
+ }
+
+ [Test]
+ public void OperatorGreaterThanOrEqual_Should_Work_Correctly()
+ {
+ var result1 = Result.Succeed(20);
+ var result2 = Result.Succeed(20);
+ Assert.That(result1 >= result2, Is.True);
+ }
+
+ [Test]
+ public void CompareTo_Should_Handle_NonComparable_Types_Gracefully()
+ {
+ var result1 = Result