Skip to content

Commit 15a6158

Browse files
authored
Merge pull request #601 from yringler/test-new-extensions
Test new extensions
2 parents c7522ab + 08f09e7 commit 15a6158

File tree

8 files changed

+370
-0
lines changed

8 files changed

+370
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Xunit;
2+
using FluentAssertions;
3+
4+
namespace CSharpFunctionalExtensions.Tests.MaybeTests.Extensions;
5+
6+
public class BindOptionalTests
7+
{
8+
[Fact]
9+
public void BindOptional_with_value_returns_value()
10+
{
11+
Maybe<int> maybe = 42;
12+
var result = maybe.BindOptional(i => Result.Success(i.ToString()));
13+
result.IsSuccess.Should().BeTrue();
14+
result.Value.HasValue.Should().BeTrue();
15+
result.Value.Value.Should().Be("42");
16+
}
17+
18+
[Fact]
19+
public void BindOptional_missing_value_returns_missing()
20+
{
21+
Maybe<int> maybe = Maybe<int>.None;
22+
var result = maybe.BindOptional(i => Result.Success(i.ToString()));
23+
result.IsSuccess.Should().BeTrue();
24+
result.Value.HasValue.Should().BeFalse();
25+
}
26+
27+
[Fact]
28+
public void BindOptional_with_error_returns_error()
29+
{
30+
Maybe<int> maybe = 42;
31+
var result = maybe.BindOptional(i => Result.Failure<string>("Something went wrong"));
32+
result.IsSuccess.Should().BeFalse();
33+
result.Error.Should().Be("Something went wrong");
34+
}
35+
36+
[Fact]
37+
public void BindOptional_E_with_value_returns_value()
38+
{
39+
Maybe<int> maybe = 42;
40+
var result = maybe.BindOptional(i => Result.Success<string, string>(i.ToString()));
41+
result.IsSuccess.Should().BeTrue();
42+
result.Value.HasValue.Should().BeTrue();
43+
result.Value.Value.Should().Be("42");
44+
}
45+
46+
[Fact]
47+
public void BindOptional_E_missing_value_returns_missing()
48+
{
49+
Maybe<int> maybe = Maybe<int>.None;
50+
var result = maybe.BindOptional(i => Result.Success<string, string>(i.ToString()));
51+
result.IsSuccess.Should().BeTrue();
52+
result.Value.HasValue.Should().BeFalse();
53+
}
54+
55+
[Fact]
56+
public void BindOptional_E_with_error_returns_error()
57+
{
58+
Maybe<int> maybe = 42;
59+
var result = maybe.BindOptional(i => Result.Failure<string, string>("Something went wrong"));
60+
result.IsSuccess.Should().BeFalse();
61+
result.Error.Should().Be("Something went wrong");
62+
}
63+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace CSharpFunctionalExtensions.Tests.MaybeTests.Extensions;
5+
6+
public class OptionalTests
7+
{
8+
[Fact]
9+
public void Optional_AbsentResult_ShouldBeSuccessWithNoValue()
10+
{
11+
var absent = Maybe<Result<string, string>>.None.Optional();
12+
13+
absent.IsSuccess.Should().BeTrue();
14+
absent.Value.HasNoValue.Should().BeTrue();
15+
}
16+
17+
[Fact]
18+
public void Optional_PresentFailedResult_ShouldBeFailureWithError()
19+
{
20+
var presentFailed = Maybe.From(Result.Failure<string, string>("oops")).Optional();
21+
22+
presentFailed.IsFailure.Should().BeTrue();
23+
presentFailed.Error.Should().Be("oops");
24+
}
25+
26+
[Fact]
27+
public void Optional_PresentSuccessResult_ShouldBeSuccessWithValue()
28+
{
29+
var presentSuccess = Maybe.From(Result.Success<string, string>("yay")).Optional();
30+
31+
presentSuccess.IsSuccess.Should().BeTrue();
32+
presentSuccess.Value.Value.Should().Be("yay");
33+
}
34+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
using FluentAssertions;
2+
using FluentAssertions.Common;
3+
using Xunit;
4+
5+
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions;
6+
7+
public class BindOptionalTests
8+
{
9+
[Fact]
10+
public void BindOptional_returns_result_value()
11+
{
12+
var present = Result.Success<Maybe<string>>("yay")
13+
.BindOptional(x => Result.Success<int>(1));
14+
15+
present.Value.Should().Be(1);
16+
}
17+
18+
[Fact]
19+
public void BindOptional_with_nothing_is_success()
20+
{
21+
var absent = Result.Success<Maybe<string>>(Maybe<string>.None)
22+
.BindOptional(x => Result.Success<int>(1));
23+
absent.IsSuccess.Should().BeTrue();
24+
absent.Value.HasNoValue.Should().BeTrue();
25+
}
26+
27+
[Fact]
28+
public void BindOptional_initial_failure_bind_not_run()
29+
{
30+
var initialFailure = Result.Failure<Maybe<string>>("nay")
31+
.BindOptional(x => Result.Success<int>(1));
32+
33+
initialFailure.IsFailure.Should().BeTrue();
34+
initialFailure.Error.IsSameOrEqualTo("nay");
35+
}
36+
37+
[Fact]
38+
public void BindOptional_bind_fails_is_error()
39+
{
40+
var bindFailure = Result.Success<Maybe<string>>("yay")
41+
.BindOptional(x => Result.Failure<string>("nay"));
42+
43+
bindFailure.IsFailure.Should().BeTrue();
44+
bindFailure.Error.IsSameOrEqualTo("nay");
45+
}
46+
47+
[Fact]
48+
public void BindOptional_E_returns_result_value()
49+
{
50+
var present = Result.Success<Maybe<string>, string>("yay")
51+
.BindOptional(x => Result.Success<int, string>(1));
52+
53+
present.Value.Should().Be(1);
54+
}
55+
56+
[Fact]
57+
public void BindOptional_E_with_nothing_is_success()
58+
{
59+
var absent = Result.Success<Maybe<string>, string>(Maybe<string>.None)
60+
.BindOptional(x => Result.Success<int, string>(1));
61+
absent.IsSuccess.Should().BeTrue();
62+
absent.Value.HasNoValue.Should().BeTrue();
63+
}
64+
65+
[Fact]
66+
public void BindOptional_E_initial_failure_bind_not_run()
67+
{
68+
var initialFailure = Result.Failure<Maybe<string>, string>("nay")
69+
.BindOptional(x => Result.Success<int, string>(1));
70+
71+
initialFailure.IsFailure.Should().BeTrue();
72+
initialFailure.Error.IsSameOrEqualTo("nay");
73+
}
74+
75+
[Fact]
76+
public void BindOptional_E_bind_fails_is_error()
77+
{
78+
var bindFailure = Result.Success<Maybe<string>, string>("yay")
79+
.BindOptional(x => Result.Failure<string, string>("nay"));
80+
81+
bindFailure.IsFailure.Should().BeTrue();
82+
bindFailure.Error.IsSameOrEqualTo("nay");
83+
}
84+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions
5+
{
6+
public class EnsureNotTests
7+
{
8+
[Fact]
9+
public void EnsureNot_passes_when_test_fails()
10+
{
11+
var result = Result.Success(5);
12+
var newResult = result.EnsureNot(x => x > 10, "Value should be less than or equal to 10");
13+
14+
newResult.IsSuccess.Should().BeTrue();
15+
newResult.Value.Should().Be(5);
16+
}
17+
18+
[Fact]
19+
public void EnsureNot_Fails_with_error_when_test_passes()
20+
{
21+
var result = Result.Success(15);
22+
var newResult = result.EnsureNot(x => x > 10, "Value should be less than or equal to 10");
23+
24+
newResult.IsFailure.Should().BeTrue();
25+
newResult.Error.Should().Be("Value should be less than or equal to 10");
26+
}
27+
28+
[Fact]
29+
public void EnsureNot_E_passes_when_test_fails()
30+
{
31+
var result = Result.Success<int, string>(5);
32+
var newResult = result.EnsureNot(x => x > 10, "Value should be less than or equal to 10");
33+
34+
newResult.IsSuccess.Should().BeTrue();
35+
newResult.Value.Should().Be(5);
36+
}
37+
38+
[Fact]
39+
public void EnsureNot_E_Fails_with_error_when_test_passes()
40+
{
41+
var result = Result.Success<int, string>(15);
42+
var newResult = result.EnsureNot(x => x > 10, "Value should be less than or equal to 10");
43+
44+
newResult.IsFailure.Should().BeTrue();
45+
newResult.Error.Should().Be("Value should be less than or equal to 10");
46+
}
47+
}
48+
}

CSharpFunctionalExtensions.Tests/ResultTests/Extensions/MapTests.cs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,5 +234,61 @@ public void Map_UnitResult_E_with_context_executes_on_failure_and_passes_correct
234234
actual.Error.Should().Be(E.Value);
235235
FuncExecuted.Should().BeFalse();
236236
}
237+
238+
[Fact]
239+
public void Map_Result_Maybe_from_success()
240+
{
241+
var result = Result.Success<Maybe<int>>(Maybe.From(1))
242+
.Map(v => v * 2);
243+
result.IsSuccess.Should().BeTrue();
244+
result.Value.HasValue.Should().BeTrue();
245+
result.Value.Value.Should().Be(2);
246+
}
247+
248+
[Fact]
249+
public void Map_Result_Maybe_from_failure()
250+
{
251+
var result = Result.Failure<Maybe<int>>("empty")
252+
.Map(v => v * 2);
253+
result.IsFailure.Should().BeTrue();
254+
result.Error.Should().BeEquivalentTo("empty");
255+
}
256+
257+
[Fact]
258+
public void Map_Result_Maybe_from_none()
259+
{
260+
var result = Result.Success(Maybe<int>.None)
261+
.Map(v => v * 2);
262+
result.IsSuccess.Should().BeTrue();
263+
result.Value.HasValue.Should().BeFalse();
264+
}
265+
266+
[Fact]
267+
public void Map_Result_E_Maybe_from_success()
268+
{
269+
var result = Result.Success<Maybe<int>, string>(Maybe.From(1))
270+
.Map(v => v * 2);
271+
result.IsSuccess.Should().BeTrue();
272+
result.Value.HasValue.Should().BeTrue();
273+
result.Value.Value.Should().Be(2);
274+
}
275+
276+
[Fact]
277+
public void Map_Result_E_Maybe_from_failure()
278+
{
279+
var result = Result.Failure<Maybe<int>, string>("error")
280+
.Map(v => v * 2);
281+
result.IsFailure.Should().BeTrue();
282+
result.Error.Should().Be("error");
283+
}
284+
285+
[Fact]
286+
public void Map_Result_E_Maybe_from_none()
287+
{
288+
var result = Result.Success<Maybe<int>, string>(Maybe<int>.None)
289+
.Map(v => v * 2);
290+
result.IsSuccess.Should().BeTrue();
291+
result.Value.HasValue.Should().BeFalse();
292+
}
237293
}
238294
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using FluentAssertions;
2+
using Xunit;
3+
4+
namespace CSharpFunctionalExtensions.Tests.ResultTests.Extensions;
5+
6+
public class RequiredTests
7+
{
8+
[Fact]
9+
public void Required_returns_error_if_missing()
10+
{
11+
var input = Result.Success(Maybe<string>.None);
12+
var error = "Value is required";
13+
14+
var result = input.Required(error);
15+
16+
result.IsFailure.Should().BeTrue();
17+
result.Error.Should().Be(error);
18+
}
19+
20+
[Fact]
21+
public void Required_returns_success_if_present()
22+
{
23+
var expectedValue = "hello";
24+
var input = Result.Success(Maybe.From(expectedValue));
25+
var error = "Value is required";
26+
27+
var result = input.Required(error);
28+
29+
result.IsSuccess.Should().BeTrue();
30+
result.Value.Should().Be(expectedValue);
31+
}
32+
33+
[Fact]
34+
public void Required_E_returns_error_if_missing()
35+
{
36+
var input = Result.Success<Maybe<string>, int>(Maybe<string>.None);
37+
var error = 1;
38+
39+
var result = input.Required(error);
40+
41+
result.IsFailure.Should().BeTrue();
42+
result.Error.Should().Be(error);
43+
}
44+
45+
[Fact]
46+
public void Required_E_returns_success_if_present()
47+
{
48+
var expectedValue = "hello";
49+
var input = Result.Success<Maybe<string>, int>(Maybe.From(expectedValue));
50+
var error = 1;
51+
52+
var result = input.Required(error);
53+
54+
result.IsSuccess.Should().BeTrue();
55+
result.Value.Should().Be(expectedValue);
56+
}
57+
}

CSharpFunctionalExtensions/Maybe/Extensions/BindOptional.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,15 @@ public static Result<Maybe<TOut>, TError> BindOptional<TIn, TOut, TError>(
1313
{
1414
return maybe.Bind(v => Maybe.From(bind(v))).Optional();
1515
}
16+
17+
/// <summary>
18+
/// Convert an optional value into a Result. The result, being optional, is of type Maybe.
19+
/// </summary>
20+
public static Result<Maybe<TOut>> BindOptional<TIn, TOut>(
21+
this Maybe<TIn> maybe,
22+
Func<TIn, Result<TOut>> bind)
23+
{
24+
return maybe.Bind(v => Maybe.From(bind(v))).Optional();
25+
}
1626
}
1727
}

CSharpFunctionalExtensions/Result/Methods/Extensions/BindOptional.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,24 @@ namespace CSharpFunctionalExtensions
44
{
55
public static partial class ResultExtensions
66
{
7+
/// <summary>
8+
/// Change from one result type to another, while staying optional (the return type still contains <see cref="Maybe"/>)
9+
/// Even though it's a Result&lt;Maybe&gt;, the mapping function only has to
10+
/// operate on the innermost value, and is only called if it's present.
11+
/// </summary>
12+
public static Result<Maybe<K>> BindOptional<T, K>(
13+
this Result<Maybe<T>> result,
14+
Func<T, Result<K>> bind)
15+
{
16+
if (result.IsFailure)
17+
return Result.Failure<Maybe<K>>(result.Error);
18+
19+
if (result.Value.HasNoValue)
20+
return Result.Success<Maybe<K>>(Maybe.None);
21+
22+
return bind(result.Value.Value).Map(Maybe.From);
23+
}
24+
725
/// <summary>
826
/// Change from one result type to another, while staying optional (the return type still contains <see cref="Maybe"/>)
927
/// Even though it's a Result&lt;Maybe&gt;, the mapping function only has to

0 commit comments

Comments
 (0)