extensions rework pt1
.NET Test / .NET tests (push) Successful in 2m0s

This commit is contained in:
2026-07-13 22:20:47 +04:00
parent 46487a4794
commit 0e7986996f
30 changed files with 1476 additions and 160 deletions
@@ -0,0 +1,173 @@
namespace Raliway.Tests.ReflectionHelper;
// --- Test types ---
file sealed record EquatableRecord(string Name, int Value);
file sealed class NonEquatableClass
{
public string Name { get; }
public NonEquatableClass(string name) => Name = name;
public override bool Equals(object? obj) => obj is NonEquatableClass other && Name == other.Name;
public override int GetHashCode() => Name.GetHashCode();
}
file readonly struct EquatableStruct : IEquatable<EquatableStruct>
{
public int Value { get; }
public EquatableStruct(int value) => Value = value;
public bool Equals(EquatableStruct other) => Value == other.Value;
public override bool Equals(object? obj) => obj is EquatableStruct s && Equals(s);
public override int GetHashCode() => Value;
}
// --- IsEqual tests ---
public class IsEqual_Tests
{
public class ValueType_IEquatable
{
[Fact] public void Equal_values() =>
Just.Railway.ReflectionHelper.IsEqual(42, 42).ShouldBeTrue();
[Fact] public void Different_values() =>
Just.Railway.ReflectionHelper.IsEqual(42, 43).ShouldBeFalse();
[Fact] public void Null_args() =>
Just.Railway.ReflectionHelper.IsEqual(default(int), default(int)).ShouldBeTrue();
}
public class NullableValueType_IEquatable
{
[Fact] public void Both_have_same_value() =>
Just.Railway.ReflectionHelper.IsEqual<int?>(42, 42).ShouldBeTrue();
[Fact] public void Different_values() =>
Just.Railway.ReflectionHelper.IsEqual<int?>(42, 43).ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<int?>(null, null).ShouldBeTrue();
[Fact] public void One_null_one_value() =>
Just.Railway.ReflectionHelper.IsEqual<int?>(42, null).ShouldBeFalse();
[Fact] public void Null_and_value_reversed() =>
Just.Railway.ReflectionHelper.IsEqual<int?>(null, 42).ShouldBeFalse();
}
public class ReferenceType_IEquatable_String
{
[Fact] public void Equal_strings() =>
Just.Railway.ReflectionHelper.IsEqual("hello", "hello").ShouldBeTrue();
[Fact] public void Different_strings() =>
Just.Railway.ReflectionHelper.IsEqual("hello", "world").ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<string>(null, null).ShouldBeTrue();
[Fact] public void Left_null_right_value() =>
Just.Railway.ReflectionHelper.IsEqual<string>(null, "hello").ShouldBeFalse();
[Fact] public void Left_value_right_null() =>
Just.Railway.ReflectionHelper.IsEqual("hello", null).ShouldBeFalse();
}
public class ReferenceType_IEquatable_Error
{
[Fact] public void Equal_errors() =>
Just.Railway.ReflectionHelper.IsEqual(
Error.New("type", "msg"),
Error.New("type", "msg")
).ShouldBeTrue();
[Fact] public void Different_errors() =>
Just.Railway.ReflectionHelper.IsEqual(
Error.New("type_a", "msg"),
Error.New("type_b", "msg")
).ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<Error>(null, null).ShouldBeTrue();
[Fact] public void One_null() =>
Just.Railway.ReflectionHelper.IsEqual(Error.New("type", "msg"), null).ShouldBeFalse();
}
public class RecordType_IEquatable
{
[Fact] public void Equal_records() =>
Just.Railway.ReflectionHelper.IsEqual(
new EquatableRecord("a", 1),
new EquatableRecord("a", 1)
).ShouldBeTrue();
[Fact] public void Different_records() =>
Just.Railway.ReflectionHelper.IsEqual(
new EquatableRecord("a", 1),
new EquatableRecord("b", 2)
).ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<EquatableRecord>(null, null).ShouldBeTrue();
}
public class NonIEquatable_Fallback
{
[Fact] public void Equal_by_Equals_override() =>
Just.Railway.ReflectionHelper.IsEqual(
new NonEquatableClass("a"),
new NonEquatableClass("a")
).ShouldBeTrue();
[Fact] public void Not_equal() =>
Just.Railway.ReflectionHelper.IsEqual(
new NonEquatableClass("a"),
new NonEquatableClass("b")
).ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<NonEquatableClass>(null, null).ShouldBeTrue();
[Fact] public void Left_null() =>
Just.Railway.ReflectionHelper.IsEqual<NonEquatableClass>(null, new NonEquatableClass("a"))
.ShouldBeFalse();
}
public class CustomEquatableStruct
{
[Fact] public void Equal() =>
Just.Railway.ReflectionHelper.IsEqual(
new EquatableStruct(1),
new EquatableStruct(1)
).ShouldBeTrue();
[Fact] public void Not_equal() =>
Just.Railway.ReflectionHelper.IsEqual(
new EquatableStruct(1),
new EquatableStruct(2)
).ShouldBeFalse();
}
public class CustomNullableEquatableStruct
{
[Fact] public void Both_have_same_value() =>
Just.Railway.ReflectionHelper.IsEqual<EquatableStruct?>(
new EquatableStruct(1),
new EquatableStruct(1)
).ShouldBeTrue();
[Fact] public void Different_values() =>
Just.Railway.ReflectionHelper.IsEqual<EquatableStruct?>(
new EquatableStruct(1),
new EquatableStruct(2)
).ShouldBeFalse();
[Fact] public void Both_null() =>
Just.Railway.ReflectionHelper.IsEqual<EquatableStruct?>(null, null).ShouldBeTrue();
[Fact] public void One_null() =>
Just.Railway.ReflectionHelper.IsEqual<EquatableStruct?>(new EquatableStruct(1), null)
.ShouldBeFalse();
}
}
+185
View File
@@ -0,0 +1,185 @@
namespace Raliway.Tests.Results;
public class Bugs
{
#region BUG-1: Result.Equals asymmetry
[Fact]
public void Result_Success_ShouldNotEqual_Default()
{
// default(Result) has State=Bottom — it's uninitialized.
// Equals should throw because one operand is Bottom.
var success = Result.Success();
Result bottom = default;
// Should throw ResultNotInitializedException, NOT return true
Should.Throw<ResultNotInitializedException>(() => success.Equals(bottom));
}
[Fact]
public void Result_Default_ShouldNotEqual_Success()
{
Result bottom = default;
var success = Result.Success();
Should.Throw<ResultNotInitializedException>(() => bottom.Equals(success));
}
[Fact]
public void Result_Success_EqualsOperator_WithDefault()
{
var success = Result.Success();
Result bottom = default;
Should.Throw<ResultNotInitializedException>(() => success == bottom);
}
[Fact]
public void Result_Error_Equals_WithBottom_ShouldThrow()
{
var error = Result.Failure("test");
Result bottom = default;
Should.Throw<ResultNotInitializedException>(() => error.Equals(bottom));
}
[Fact]
public void Result_Bottom_Equals_WithSuccess_ShouldThrow()
{
Result bottom = default;
var success = Result.Success();
Should.Throw<ResultNotInitializedException>(() => bottom.Equals(success));
}
#endregion
#region BUG-2: Result<T>.Equals asymmetry
[Fact]
public void ResultOfT_Success_ShouldNotEqual_Default()
{
var success = Result.Success(42);
Result<int> bottom = default;
Should.Throw<ResultNotInitializedException>(() => success.Equals(bottom));
}
[Fact]
public void ResultOfT_Error_ShouldNotEqual_Default()
{
var error = Result.Failure<int>("test");
Result<int> bottom = default;
Should.Throw<ResultNotInitializedException>(() => error.Equals(bottom));
}
[Fact]
public void ResultOfT_Default_ShouldNotEqual_Success()
{
Result<int> bottom = default;
var success = Result.Success(42);
Should.Throw<ResultNotInitializedException>(() => bottom.Equals(success));
}
#endregion
#region BUG-3: Task.WhenAny Merge faulted task
[Fact]
public async Task Merge_Tasks_WhenOneFaults_ShouldReturnError_NotThrow()
{
var tasks = new Task<Result>[]
{
Task.FromResult(Result.Success()),
Task.Run(() =>
{
throw new InvalidOperationException("boom");
#pragma warning disable CS0162 // Unreachable code
return Result.Success();
#pragma warning restore CS0162
}),
};
// Should not throw AggregateException escaping the Result error channel
var result = await tasks.Merge();
result.IsFailure.ShouldBeTrue();
}
[Fact]
public async Task Merge_Tasks_WhenOneIsCanceled_ShouldReturnError_NotThrow()
{
var cts = new CancellationTokenSource();
cts.Cancel();
var canceledTask = Task.FromCanceled<Result>(cts.Token);
var tasks = new Task<Result>[]
{
Task.FromResult(Result.Success()),
canceledTask,
};
// Should not throw — canceled tasks have null Exception
var result = await tasks.Merge();
result.IsFailure.ShouldBeTrue();
}
#endregion
#region BUG-4: Task.WhenAny Merge<T> canceled task + O(n²)
[Fact]
public async Task MergeOfT_Tasks_WhenOneIsCanceled_ShouldReturnError_NotThrow()
{
var cts = new CancellationTokenSource();
cts.Cancel();
var canceledTask = Task.FromCanceled<Result<int>>(cts.Token);
var tasks = new Task<Result<int>>[]
{
Task.FromResult(Result.Success(1)),
canceledTask,
};
// Should not crash on canceled task (task.Exception is null → null ref in implicit operator)
var result = await tasks.Merge();
result.IsFailure.ShouldBeTrue();
}
[Fact]
public async Task MergeOfT_Tasks_WhenOneFaults_ShouldReturnError_NotThrow()
{
var tasks = new Task<Result<int>>[]
{
Task.FromResult(Result.Success(1)),
Task.Run(() =>
{
throw new InvalidOperationException("boom");
#pragma warning disable CS0162 // Unreachable code
return Result.Success(2);
#pragma warning restore CS0162
}),
};
var result = await tasks.Merge();
result.IsFailure.ShouldBeTrue();
}
#endregion
#region BUG-5: Error.New null type
[Fact]
public void Error_New_WithNullType_ShouldThrowArgumentNullException()
{
// Null type should throw immediately at construction, not crash later in CompareTo/IsSimilarTo
Should.Throw<ArgumentNullException>(() => Error.New(null!, "some message"));
}
#endregion
}
+2 -1
View File
@@ -37,6 +37,7 @@ public class GeneralUsage
var result = Result.Success()
.Append(() => Result.Failure<int>(error))
.Append("test")
.Tap(onSuccess: (i, s) => true.ShouldBeFalse("should not reach this path"))
.Map((i, s) =>
{
true.ShouldBeFalse("should not reach this path");
@@ -85,7 +86,7 @@ public class GeneralUsage
return "";
}
);
result.ShouldBe("TEST_1;SOME");
}
+558
View File
@@ -0,0 +1,558 @@
namespace Raliway.Tests.TryExtensions;
public class RunTests
{
// ═══════════════════════════════════════════════════════════
// Action (void) — sync
// ═══════════════════════════════════════════════════════════
public class Action_Void
{
[Fact]
public void No_args_success()
{
var result = Try.Run(() => { });
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public void No_args_exception()
{
var result = Try.Run(new Action(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
result.Error!.Type.ShouldBe("System.InvalidOperationException");
}
[Fact]
public void One_arg_success()
{
var captured = 0;
var result = Try.Run<int>(x => captured = x, 42);
result.IsSuccess.ShouldBeTrue();
captured.ShouldBe(42);
}
[Fact]
public void One_arg_exception()
{
var result = Try.Run(new Action<int>(_ => throw new InvalidOperationException("boom")), 42);
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public void Five_args_success()
{
var captured = "";
var result = Try.Run<int, string, bool, double, char>(
(a, b, c, d, e) => captured = $"{a}{b}{c}{d}{e}",
1, "x", true, 3.14, 'z');
result.IsSuccess.ShouldBeTrue();
captured.ShouldBe("1xTrue3.14z");
}
[Fact]
public void Five_args_exception()
{
var result = Try.Run(
new Action<int, string, bool, double, char>((_, _, _, _, _) => throw new InvalidOperationException("boom")),
1, "x", true, 3.14, 'z');
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// Func<Result> — sync
// ═══════════════════════════════════════════════════════════
public class Func_Result
{
[Fact]
public void No_args_returns_success()
{
var result = Try.Run(() => Result.Success());
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public void No_args_returns_failure()
{
var error = Error.New("nope");
var result = Try.Run(() => Result.Failure(error));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public void No_args_exception_caught()
{
var result = Try.Run(new Func<Result>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public void One_arg_passes_through()
{
var result = Try.Run<int>(x => Result.Success(x), 42);
result.IsSuccess.ShouldBeTrue();
}
}
// ═══════════════════════════════════════════════════════════
// Func<T> — sync
// ═══════════════════════════════════════════════════════════
public class Func_T
{
[Fact]
public void No_args_success()
{
var result = Try.Run(() => 42);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public void No_args_exception()
{
var result = Try.Run(new Func<int>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public void One_arg_success()
{
var result = Try.Run(x => $"val:{x}", 42);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe("val:42");
}
[Fact]
public void One_arg_exception()
{
var result = Try.Run(new Func<int, string>(_ => throw new InvalidOperationException("boom")), 42);
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public void Null_value_is_preserved()
{
var result = Try.Run(() => (string?)null);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBeNull();
}
}
// ═══════════════════════════════════════════════════════════
// Func<Result<T>> — sync
// ═══════════════════════════════════════════════════════════
public class Func_ResultT
{
[Fact]
public void No_args_returns_success()
{
var result = Try.Run(() => Result.Success(42));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public void No_args_returns_failure()
{
var error = Error.New("nope");
var result = Try.Run(() => Result.Failure<int>(error));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public void No_args_exception_caught()
{
var result = Try.Run(new Func<Result<int>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public void One_arg_passes_through()
{
var result = Try.Run(x => Result.Success($"val:{x}"), 42);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe("val:42");
}
}
// ═══════════════════════════════════════════════════════════
// Task async — Action (void)
// ═══════════════════════════════════════════════════════════
public class Task_Action
{
[Fact]
public async Task No_args_success()
{
var result = await Try.Run(() => Task.CompletedTask);
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public async Task No_args_exception()
{
var result = await Try.Run(() => Task.FromException(new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public async Task One_arg_success()
{
var captured = 0;
var result = await Try.Run(x => { captured = x; return Task.CompletedTask; }, 42);
result.IsSuccess.ShouldBeTrue();
captured.ShouldBe(42);
}
}
// ═══════════════════════════════════════════════════════════
// Task async — Func<Task<Result>>
// ═══════════════════════════════════════════════════════════
public class Task_Func_Result
{
[Fact]
public async Task No_args_returns_success()
{
var result = await Try.Run(() => Task.FromResult(Result.Success()));
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public async Task No_args_returns_failure()
{
var error = Error.New("nope");
var result = await Try.Run(() => Task.FromResult(Result.Failure(error)));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public async Task No_args_exception_caught()
{
var result = await Try.Run(new Func<Task<Result>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// Task async — Func<Task<T>>
// ═══════════════════════════════════════════════════════════
public class Task_Func_T
{
[Fact]
public async Task No_args_success()
{
var result = await Try.Run(() => Task.FromResult(42));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public async Task No_args_exception()
{
var result = await Try.Run(new Func<Task<int>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// Task async — Func<Task<Result<T>>>
// ═══════════════════════════════════════════════════════════
public class Task_Func_ResultT
{
[Fact]
public async Task No_args_returns_success()
{
var result = await Try.Run(() => Task.FromResult(Result.Success(42)));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public async Task No_args_returns_failure()
{
var error = Error.New("nope");
var result = await Try.Run(() => Task.FromResult(Result.Failure<int>(error)));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public async Task No_args_exception_caught()
{
var result = await Try.Run(new Func<Task<Result<int>>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// ValueTask async — Action (void)
// ═══════════════════════════════════════════════════════════
public class ValueTask_Action
{
[Fact]
public async Task No_args_success()
{
var result = await Try.RunValueTask(() => ValueTask.CompletedTask);
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public async Task No_args_exception()
{
var result = await Try.RunValueTask(() => ValueTask.FromException(new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
[Fact]
public async Task One_arg_success()
{
var captured = 0;
var result = await Try.RunValueTask(x => { captured = x; return ValueTask.CompletedTask; }, 42);
result.IsSuccess.ShouldBeTrue();
captured.ShouldBe(42);
}
}
// ═══════════════════════════════════════════════════════════
// ValueTask async — Func<ValueTask<Result>>
// ═══════════════════════════════════════════════════════════
public class ValueTask_Func_Result
{
[Fact]
public async Task No_args_returns_success()
{
var result = await Try.RunValueTask(() => ValueTask.FromResult(Result.Success()));
result.IsSuccess.ShouldBeTrue();
}
[Fact]
public async Task No_args_returns_failure()
{
var error = Error.New("nope");
var result = await Try.RunValueTask(() => ValueTask.FromResult(Result.Failure(error)));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public async Task No_args_exception_caught()
{
var result = await Try.RunValueTask(new Func<ValueTask<Result>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// ValueTask async — Func<ValueTask<T>>
// ═══════════════════════════════════════════════════════════
public class ValueTask_Func_T
{
[Fact]
public async Task No_args_success()
{
var result = await Try.RunValueTask(() => ValueTask.FromResult(42));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public async Task No_args_exception()
{
var result = await Try.RunValueTask<int>(() => throw new InvalidOperationException("boom"));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// ValueTask async — Func<ValueTask<Result<T>>>
// ═══════════════════════════════════════════════════════════
public class ValueTask_Func_ResultT
{
[Fact]
public async Task No_args_returns_success()
{
var result = await Try.RunValueTask(() => ValueTask.FromResult(Result.Success(42)));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe(42);
}
[Fact]
public async Task No_args_returns_failure()
{
var error = Error.New("nope");
var result = await Try.RunValueTask(() => ValueTask.FromResult(Result.Failure<int>(error)));
result.IsFailure.ShouldBeTrue();
result.Error.ShouldBe(error);
}
[Fact]
public async Task No_args_exception_caught()
{
var result = await Try.RunValueTask(new Func<ValueTask<Result<int>>>(() => throw new InvalidOperationException("boom")));
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("boom");
}
}
// ═══════════════════════════════════════════════════════════
// AggregateException unwrapping
// ═══════════════════════════════════════════════════════════
public class AggregateException_Unwrapping
{
[Fact]
public void Single_inner_exception_is_unwrapped()
{
var result = Try.Run(new Func<int>(() => throw new AggregateException(new InvalidOperationException("inner"))));
result.IsFailure.ShouldBeTrue();
result.Error!.Type.ShouldBe("System.InvalidOperationException");
result.Error!.Message.ShouldBe("inner");
}
[Fact]
public void Multiple_inner_exceptions_become_ManyErrors()
{
var result = Try.Run(new Func<int>(() => throw new AggregateException(
new InvalidOperationException("first"),
new ArgumentException("second"))));
result.IsFailure.ShouldBeTrue();
result.Error!.Type.ShouldBe("many_errors");
var many = (ManyErrors)result.Error!;
many.Count.ShouldBe(2);
}
}
// ═══════════════════════════════════════════════════════════
// Async exception in task body (not Task.FromException)
// ═══════════════════════════════════════════════════════════
public class Async_Exception_In_Body
{
[Fact]
public async Task Task_throws_in_async_body()
{
var result = await Try.Run(async () =>
{
await Task.Yield();
throw new InvalidOperationException("async boom");
});
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("async boom");
}
[Fact]
public async Task ValueTask_throws_in_async_body()
{
var result = await Try.RunValueTask(async () =>
{
await Task.Yield();
throw new InvalidOperationException("async boom vt");
});
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("async boom vt");
}
[Fact]
public async Task Task_FuncT_throws_in_async_body()
{
var result = await Try.Run<int>(async () =>
{
await Task.Yield();
throw new InvalidOperationException("async boom");
});
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("async boom");
}
[Fact]
public async Task ValueTask_ResultT_throws_in_async_body()
{
var result = await Try.RunValueTask<int>(async () =>
{
await Task.Yield();
throw new InvalidOperationException("async boom vt");
});
result.IsFailure.ShouldBeTrue();
result.Error!.Message.ShouldBe("async boom vt");
}
}
// ═══════════════════════════════════════════════════════════
// Edge cases
// ═══════════════════════════════════════════════════════════
public class Edge_Cases
{
[Fact]
public void Reference_type_null_success_value_is_preserved()
{
var result = Try.Run(() => Result.Success<string?>(null));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBeNull();
}
[Fact]
public void Large_ValueTuple_is_preserved()
{
var result = Try.Run(() => (1, 2, 3, 4, 5));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe((1, 2, 3, 4, 5));
}
[Fact]
public async Task Async_large_ValueTuple_is_preserved()
{
var result = await Try.Run(() => Task.FromResult((1, 2, 3, 4, 5)));
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe((1, 2, 3, 4, 5));
}
[Fact]
public void Three_args_all_passed_correctly()
{
var result = Try.Run(
(a, b, c) => $"{a}-{b}-{c}",
1, "two", true);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe("1-two-True");
}
[Fact]
public async Task Task_with_three_args()
{
var result = await Try.Run(
(a, b, c) => Task.FromResult($"{a}-{b}-{c}"),
1, "two", true);
result.IsSuccess.ShouldBeTrue();
result.Value.ShouldBe("1-two-True");
}
}
}