Skip to content

Commit c189d4f

Browse files
Added methods to Result and Result<T> for obtaining underlying exceptions, and methods to Failure and Failure<T> to allow casting to other Result and Result<T> types. This also includes overloads for async programming. (#72)
1 parent 19bb8ed commit c189d4f

11 files changed

+811
-46
lines changed

OnixLabs.Core.UnitTests/ResultExtensionTests.cs

+210
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,170 @@ namespace OnixLabs.Core.UnitTests;
2020

2121
public sealed class ResultExtensionTests
2222
{
23+
[Fact(DisplayName = "Result Success.GetExceptionOrDefaultAsync should produce the expected result.")]
24+
public async Task ResultSuccessGetExceptionOrDefaultAsyncShouldProduceExpectedResult()
25+
{
26+
// Given
27+
Result result = Result.Success();
28+
29+
// When
30+
Exception? actual = await Task.FromResult(result).GetExceptionOrDefaultAsync();
31+
32+
// Then
33+
Assert.Null(actual);
34+
}
35+
36+
[Fact(DisplayName = "Result Success.GetExceptionOrDefaultAsync with default value should produce the expected result.")]
37+
public async Task ResultSuccessGetExceptionOrDefaultAsyncWithDefaultValueShouldProduceExpectedResult()
38+
{
39+
// Given
40+
Exception expected = new("failure");
41+
Result result = Result.Success();
42+
43+
// When
44+
Exception actual = await Task.FromResult(result).GetExceptionOrDefaultAsync(expected);
45+
46+
// Then
47+
Assert.Equal(expected, actual);
48+
}
49+
50+
[Fact(DisplayName = "Result Success.GetExceptionOrThrowAsync should produce the expected result.")]
51+
public async Task ResultSuccessGetExceptionOrThrowAsyncShouldProduceExpectedResult()
52+
{
53+
// Given
54+
Result result = Result.Success();
55+
56+
// When
57+
Exception exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Task.FromResult(result).GetExceptionOrThrowAsync());
58+
59+
// Then
60+
Assert.Equal("The current result is not in a Failure state.", exception.Message);
61+
}
62+
63+
[Fact(DisplayName = "Result Failure.GetExceptionOrDefaultAsync should produce the expected result.")]
64+
public async Task ResultFailureGetExceptionOrDefaultAsyncShouldProduceExpectedResult()
65+
{
66+
// Given
67+
Exception expected = new("failure");
68+
Result result = Result.Failure(expected);
69+
70+
// When
71+
Exception? actual = await Task.FromResult(result).GetExceptionOrDefaultAsync();
72+
73+
// Then
74+
Assert.Equal(expected, actual);
75+
}
76+
77+
[Fact(DisplayName = "Result Failure.GetExceptionOrDefaultAsync with default value should produce the expected result.")]
78+
public async Task ResultFailureGetExceptionOrDefaultAsyncWithDefaultValueShouldProduceExpectedResult()
79+
{
80+
// Given
81+
Exception expected = new("failure");
82+
Result result = Result.Failure(expected);
83+
84+
// When
85+
Exception actual = await Task.FromResult(result).GetExceptionOrDefaultAsync(new Exception("unexpected exception"));
86+
87+
// Then
88+
Assert.Equal(expected, actual);
89+
}
90+
91+
[Fact(DisplayName = "Result Failure.GetExceptionOrThrowAsync should produce the expected result.")]
92+
public async Task ResultFailureGetExceptionOrThrowAsyncShouldProduceExpectedResult()
93+
{
94+
// Given
95+
Exception expected = new("failure");
96+
Result result = Result.Failure(expected);
97+
98+
// When
99+
Exception actual = await Task.FromResult(result).GetExceptionOrThrowAsync();
100+
101+
// Then
102+
Assert.Equal(expected, actual);
103+
}
104+
105+
[Fact(DisplayName = "Result<T> Success.GetExceptionOrDefaultAsync should produce the expected result.")]
106+
public async Task ResultOfTSuccessGetExceptionOrDefaultAsyncShouldProduceExpectedResult()
107+
{
108+
// Given
109+
Result<int> result = Result<int>.Success(123);
110+
111+
// When
112+
Exception? actual = await Task.FromResult(result).GetExceptionOrDefaultAsync();
113+
114+
// Then
115+
Assert.Null(actual);
116+
}
117+
118+
[Fact(DisplayName = "Result<T> Success.GetExceptionOrDefaultAsync with default value should produce the expected result.")]
119+
public async Task ResultOfTSuccessGetExceptionOrDefaultAsyncWithDefaultValueShouldProduceExpectedResult()
120+
{
121+
// Given
122+
Exception expected = new("failure");
123+
Result<int> result = Result<int>.Success(123);
124+
125+
// When
126+
Exception actual = await Task.FromResult(result).GetExceptionOrDefaultAsync(expected);
127+
128+
// Then
129+
Assert.Equal(expected, actual);
130+
}
131+
132+
[Fact(DisplayName = "Result<T> Success.GetExceptionOrThrowAsync should produce the expected result.")]
133+
public async Task ResultOfTSuccessGetExceptionOrThrowAsyncShouldProduceExpectedResult()
134+
{
135+
// Given
136+
Result<int> result = Result<int>.Success(123);
137+
138+
// When
139+
Exception exception = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Task.FromResult(result).GetExceptionOrThrowAsync());
140+
141+
// Then
142+
Assert.Equal("The current result is not in a Failure<T> state.", exception.Message);
143+
}
144+
145+
[Fact(DisplayName = "Result<T> Failure.GetExceptionOrDefaultAsync should produce the expected result.")]
146+
public async Task ResultOfTFailureGetExceptionOrDefaultAsyncShouldProduceExpectedResult()
147+
{
148+
// Given
149+
Exception expected = new("failure");
150+
Result<int> result = Result<int>.Failure(expected);
151+
152+
// When
153+
Exception? actual = await Task.FromResult(result).GetExceptionOrDefaultAsync();
154+
155+
// Then
156+
Assert.Equal(expected, actual);
157+
}
158+
159+
[Fact(DisplayName = "Result<T> Failure.GetExceptionOrDefaultAsync with default value should produce the expected result.")]
160+
public async Task ResultOfTFailureGetExceptionOrDefaultAsyncWithDefaultValueShouldProduceExpectedResult()
161+
{
162+
// Given
163+
Exception expected = new("failure");
164+
Result<int> result = Result<int>.Failure(expected);
165+
166+
// When
167+
Exception actual = await Task.FromResult(result).GetExceptionOrDefaultAsync(new Exception("unexpected exception"));
168+
169+
// Then
170+
Assert.Equal(expected, actual);
171+
}
172+
173+
[Fact(DisplayName = "Result<T> Failure.GetExceptionOrThrowAsync should produce the expected result.")]
174+
public async Task ResultOfTFailureGetExceptionOrThrowAsyncShouldProduceExpectedResult()
175+
{
176+
// Given
177+
Exception expected = new("failure");
178+
Result<int> result = Result<int>.Failure(expected);
179+
180+
// When
181+
Exception actual = await Task.FromResult(result).GetExceptionOrThrowAsync();
182+
183+
// Then
184+
Assert.Equal(expected, actual);
185+
}
186+
23187
[Fact(DisplayName = "Result.GetValueOrDefaultAsync should return the result value when the result is Success")]
24188
public async Task ResultGetValueOrDefaultAsyncShouldReturnResultValueWhenResultIsSuccess()
25189
{
@@ -68,6 +232,7 @@ public async Task ResultGetValueOrDefaultAsyncWithDefaultValueShouldReturnResult
68232

69233
// When
70234
int actualNumber = await numberTask.GetValueOrDefaultAsync(456);
235+
// ReSharper disable once VariableCanBeNotNullable
71236
string? actualText = await textTask.GetValueOrDefaultAsync("xyz");
72237

73238
// Then
@@ -843,4 +1008,49 @@ public async Task ResultOfTFailureThrowAsyncShouldThrowException()
8431008
// When / Then
8441009
await Assert.ThrowsAsync<Exception>(async () => await Task.FromResult(result).ThrowAsync());
8451010
}
1011+
1012+
[Fact(DisplayName = "Result Failure.ToTypedResultAsync should produce the expected result.")]
1013+
public async Task ResultFailureToTypedResultAsyncShouldProduceExpectedResult()
1014+
{
1015+
// Given
1016+
Exception exception = new("failure");
1017+
Failure result = Result.Failure(exception);
1018+
1019+
// When
1020+
Result<int> actual = await Task.FromResult(result).ToTypedResultAsync<int>();
1021+
1022+
// Then
1023+
Assert.IsType<Failure<int>>(actual);
1024+
Assert.Equal("System.Exception: failure", actual.GetExceptionOrThrow().ToString());
1025+
}
1026+
1027+
[Fact(DisplayName = "Result<T> Failure.ToTypedResultAsync should produce the expected result.")]
1028+
public async Task ResultOfTFailureToTypedResultAsyncShouldProduceExpectedResult()
1029+
{
1030+
// Given
1031+
Exception exception = new("failure");
1032+
Failure<string> result = Result<string>.Failure(exception);
1033+
1034+
// When
1035+
Result<int> actual = await Task.FromResult(result).ToTypedResultAsync<string, int>();
1036+
1037+
// Then
1038+
Assert.IsType<Failure<int>>(actual);
1039+
Assert.Equal("System.Exception: failure", actual.GetExceptionOrThrow().ToString());
1040+
}
1041+
1042+
[Fact(DisplayName = "Result<T> Failure.ToUntypedResultAsync should produce the expected result.")]
1043+
public async Task ResultOfTFailureToUntypedResultAsyncShouldProduceExpectedResult()
1044+
{
1045+
// Given
1046+
Exception exception = new("failure");
1047+
Failure<string> result = Result<string>.Failure(exception);
1048+
1049+
// When
1050+
Result actual = await Task.FromResult(result).ToUntypedResultAsync();
1051+
1052+
// Then
1053+
Assert.IsType<Failure>(actual);
1054+
Assert.Equal("System.Exception: failure", actual.GetExceptionOrThrow().ToString());
1055+
}
8461056
}

OnixLabs.Core.UnitTests/ResultGenericTests.cs

+112
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,88 @@ public void ResultFailureGetHashCodeShouldProduceExpectedResult()
351351
Assert.Equal(expected, actual);
352352
}
353353

354+
[Fact(DisplayName = "Result Success.GetExceptionOrDefault should produce the expected result.")]
355+
public void ResultSuccessGetExceptionOrDefaultShouldProduceExpectedResult()
356+
{
357+
// Given
358+
Result<int> result = Result<int>.Success(123);
359+
360+
// When
361+
Exception? actual = result.GetExceptionOrDefault();
362+
363+
// Then
364+
Assert.Null(actual);
365+
}
366+
367+
[Fact(DisplayName = "Result Success.GetExceptionOrDefault with default value should produce the expected result.")]
368+
public void ResultSuccessGetExceptionOrDefaultWithDefaultValueShouldProduceExpectedResult()
369+
{
370+
// Given
371+
Exception expected = new("failure");
372+
Result<int> result = Result<int>.Success(123);
373+
374+
// When
375+
Exception actual = result.GetExceptionOrDefault(expected);
376+
377+
// Then
378+
Assert.Equal(expected, actual);
379+
}
380+
381+
[Fact(DisplayName = "Result Success.GetExceptionOrThrow should produce the expected result.")]
382+
public void ResultSuccessGetExceptionOrThrowShouldProduceExpectedResult()
383+
{
384+
// Given
385+
Result<int> result = Result<int>.Success(123);
386+
387+
// When
388+
Exception exception = Assert.Throws<InvalidOperationException>(() => result.GetExceptionOrThrow());
389+
390+
// Then
391+
Assert.Equal("The current result is not in a Failure<T> state.", exception.Message);
392+
}
393+
394+
[Fact(DisplayName = "Result Failure.GetExceptionOrDefault should produce the expected result.")]
395+
public void ResultFailureGetExceptionOrDefaultShouldProduceExpectedResult()
396+
{
397+
// Given
398+
Exception expected = new("failure");
399+
Result<int> result = Result<int>.Failure(expected);
400+
401+
// When
402+
Exception? actual = result.GetExceptionOrDefault();
403+
404+
// Then
405+
Assert.Equal(expected, actual);
406+
}
407+
408+
[Fact(DisplayName = "Result Failure.GetExceptionOrDefault with default value should produce the expected result.")]
409+
public void ResultFailureGetExceptionOrDefaultWithDefaultValueShouldProduceExpectedResult()
410+
{
411+
// Given
412+
Exception expected = new("failure");
413+
Result<int> result = Result<int>.Failure(expected);
414+
415+
// When
416+
Exception actual = result.GetExceptionOrDefault(new Exception("unexpected exception"));
417+
418+
// Then
419+
Assert.Equal(expected, actual);
420+
}
421+
422+
[Fact(DisplayName = "Result Failure.GetExceptionOrThrow should produce the expected result.")]
423+
public void ResultFailureGetExceptionOrThrowShouldProduceExpectedResult()
424+
{
425+
// Given
426+
Exception expected = new("failure");
427+
Result<int> result = Result<int>.Failure(expected);
428+
429+
// When
430+
Exception actual = result.GetExceptionOrThrow();
431+
432+
// Then
433+
Assert.Equal(expected, actual);
434+
}
435+
354436
[Fact(DisplayName = "Result Success.GetValueOrDefault should produce the expected result.")]
355437
public void ResultSuccessGetValueOrDefaultShouldProduceExpectedResult()
356438
{
@@ -683,6 +765,36 @@ public void ResultFailureToStringShouldProduceExpectedResult()
683765
Assert.Equal("System.Exception: failure", textString);
684766
}
685767

768+
[Fact(DisplayName = "Result Failure.ToTypedResult should produce the expected result.")]
769+
public void ResultFailureToTypedResultShouldProduceExpectedResult()
770+
{
771+
// Given
772+
Exception exception = new("failure");
773+
Failure<string> result = Result<string>.Failure(exception);
774+
775+
// When
776+
Result<int> actual = result.ToTypedResult<int>();
777+
778+
// Then
779+
Assert.IsType<Failure<int>>(actual);
780+
Assert.Equal("System.Exception: failure", actual.GetExceptionOrThrow().ToString());
781+
}
782+
783+
[Fact(DisplayName = "Result Failure.ToUntypedResult should produce the expected result.")]
784+
public void ResultFailureToUntypedResultShouldProduceExpectedResult()
785+
{
786+
// Given
787+
Exception exception = new("failure");
788+
Failure<string> result = Result<string>.Failure(exception);
789+
790+
// When
791+
Result actual = result.ToUntypedResult();
792+
793+
// Then
794+
Assert.IsType<Failure>(actual);
795+
Assert.Equal("System.Exception: failure", actual.GetExceptionOrThrow().ToString());
796+
}
797+
686798
[Fact(DisplayName = "Result Success.Dispose should dispose of the underlying value.")]
687799
public void ResultSuccessDisposeShouldDisposeUnderlyingValue()
688800
{

0 commit comments

Comments
 (0)