Skip to content

Commit

Permalink
Tweak test that verifies exception when sync validation goes async (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
DamianEdwards authored Apr 22, 2024
1 parent 72ff543 commit 9eb92f5
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 19 deletions.
17 changes: 7 additions & 10 deletions src/MiniValidation/MiniValidator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ private static bool TryValidateImpl<TTarget>(TTarget target, IServiceProvider? s
catch (Exception)
{
#if NET6_0_OR_GREATER
// Always observe the ValueTask
_ = validateTask.AsTask().GetAwaiter().GetResult();
#else
_ = validateTask.GetAwaiter().GetResult();
Expand Down Expand Up @@ -435,6 +436,7 @@ private static async Task<bool> TryValidateImpl(
}
catch (Exception)
{
// Always observe the ValueTask
_ = await validateTask.ConfigureAwait(false);
throw;
}
Expand Down Expand Up @@ -465,6 +467,7 @@ private static async Task<bool> TryValidateImpl(
}
catch (Exception)
{
// Always observe the ValueTask
_ = await validateTask.ConfigureAwait(false);
throw;
}
Expand All @@ -482,7 +485,8 @@ private static async Task<bool> TryValidateImpl(
}
catch (Exception)
{
await validateTask.ConfigureAwait(false);
// Always observe the ValueTask
_ = await validateTask.ConfigureAwait(false);
throw;
}

Expand Down Expand Up @@ -518,15 +522,7 @@ private static async Task<bool> TryValidateImpl(
validationContext.DisplayName = validationContext.ObjectType.Name;

var validateTask = validatable.ValidateAsync(validationContext);
try
{
ThrowIfAsyncNotAllowed(validateTask.IsCompleted, allowAsync);
}
catch (Exception)
{
_ = await validateTask.ConfigureAwait(false);
throw;
}
ThrowIfAsyncNotAllowed(validateTask.IsCompleted, allowAsync);

var validatableResults = await validateTask.ConfigureAwait(false);
if (validatableResults is not null)
Expand Down Expand Up @@ -591,6 +587,7 @@ private static async Task<bool> TryValidateEnumerable(
}
catch (Exception)
{
// Always observe the ValueTask
_ = await validateTask.ConfigureAwait(false);
throw;
}
Expand Down
23 changes: 16 additions & 7 deletions tests/MiniValidation.UnitTests/Recursion.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace MiniValidation.UnitTests;
using Microsoft.Extensions.DependencyInjection;

namespace MiniValidation.UnitTests;

public class Recursion
{
Expand Down Expand Up @@ -412,21 +414,28 @@ public void Invalid_When_AsyncValidatableOnlyChild_Is_Invalid_Allowing_SyncOverA
Assert.Equal($"{nameof(TestTypeWithAsyncChild.NeedsAsync)}.{nameof(TestAsyncValidatableChildType.TwentyOrMore)}", errors.Keys.First());
}

[Fact(Skip = "Unreliable on CI, can reproduce on dev machine with loop count 100+. See https://github.com/xunit/xunit/issues/2917")]
[Fact]
public void Throws_InvalidOperationException_When_Polymorphic_AsyncValidatableOnlyChild_Is_Invalid_Without_Allowing_SyncOverAsync()
{
var thingToValidate = new TestValidatableType
{
PocoChild = new TestAsyncValidatableChildType { TwentyOrMore = 12 }
};

for (int i = 0; i < 10000; i++)
Assert.Throws<InvalidOperationException>(() =>
{
Assert.Throws<InvalidOperationException>(() =>
var tcs = new TaskCompletionSource<object?>();
var serviceProvider = new ServiceCollection().AddSingleton<Task>(tcs.Task).BuildServiceProvider();
try
{
var result = MiniValidator.TryValidate(thingToValidate, out var errors);
});
}
var result = MiniValidator.TryValidate(thingToValidate, serviceProvider, out var errors);
}
finally
{
tcs.SetResult(null);
}
});
}

[Fact]
Expand Down
10 changes: 8 additions & 2 deletions tests/MiniValidation.UnitTests/TestTypes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace MiniValidation.UnitTests;
using Microsoft.Extensions.DependencyInjection;

namespace MiniValidation.UnitTests;

class TestType
{
Expand Down Expand Up @@ -182,7 +184,11 @@ class TestAsyncValidatableChildType : TestChildType, IAsyncValidatableObject

public async Task<IEnumerable<ValidationResult>> ValidateAsync(ValidationContext validationContext)
{
await Task.Yield();
var taskToAwait = validationContext.GetService<Task>();
if (taskToAwait is not null)
{
await taskToAwait;
}

List<ValidationResult>? result = null;
if (TwentyOrMore < 20)
Expand Down

0 comments on commit 9eb92f5

Please sign in to comment.