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(x => captured = x, 42); result.IsSuccess.ShouldBeTrue(); captured.ShouldBe(42); } [Fact] public void One_arg_exception() { var result = Try.Run(new Action(_ => 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( (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((_, _, _, _, _) => throw new InvalidOperationException("boom")), 1, "x", true, 3.14, 'z'); result.IsFailure.ShouldBeTrue(); result.Error!.Message.ShouldBe("boom"); } } // ═══════════════════════════════════════════════════════════ // Func — 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(() => 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(x), 42); result.IsSuccess.ShouldBeTrue(); } } // ═══════════════════════════════════════════════════════════ // Func — 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(() => 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(_ => 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> — 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(error)); result.IsFailure.ShouldBeTrue(); result.Error.ShouldBe(error); } [Fact] public void No_args_exception_caught() { var result = Try.Run(new Func>(() => 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> // ═══════════════════════════════════════════════════════════ 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>(() => throw new InvalidOperationException("boom"))); result.IsFailure.ShouldBeTrue(); result.Error!.Message.ShouldBe("boom"); } } // ═══════════════════════════════════════════════════════════ // Task async — Func> // ═══════════════════════════════════════════════════════════ 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>(() => throw new InvalidOperationException("boom"))); result.IsFailure.ShouldBeTrue(); result.Error!.Message.ShouldBe("boom"); } } // ═══════════════════════════════════════════════════════════ // Task async — Func>> // ═══════════════════════════════════════════════════════════ 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(error))); result.IsFailure.ShouldBeTrue(); result.Error.ShouldBe(error); } [Fact] public async Task No_args_exception_caught() { var result = await Try.Run(new Func>>(() => 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> // ═══════════════════════════════════════════════════════════ 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>(() => throw new InvalidOperationException("boom"))); result.IsFailure.ShouldBeTrue(); result.Error!.Message.ShouldBe("boom"); } } // ═══════════════════════════════════════════════════════════ // ValueTask async — Func> // ═══════════════════════════════════════════════════════════ 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(() => throw new InvalidOperationException("boom")); result.IsFailure.ShouldBeTrue(); result.Error!.Message.ShouldBe("boom"); } } // ═══════════════════════════════════════════════════════════ // ValueTask async — Func>> // ═══════════════════════════════════════════════════════════ 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(error))); result.IsFailure.ShouldBeTrue(); result.Error.ShouldBe(error); } [Fact] public async Task No_args_exception_caught() { var result = await Try.RunValueTask(new Func>>(() => 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(() => 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(() => 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(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(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(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"); } } }