From f00e0f3d649aaeb809c151ef41c648f0a8f3660a Mon Sep 17 00:00:00 2001 From: Michael Staib Date: Mon, 1 Jul 2024 23:01:31 +0100 Subject: [PATCH] Added Tests --- .../Core/src/Abstractions/Executable~1.cs | 8 +- .../AsyncEnumerableExecutableTests.cs | 161 ++++++++ .../QueryableExecutableTests.cs | 363 ++++++++++++++++++ 3 files changed, 531 insertions(+), 1 deletion(-) create mode 100644 src/HotChocolate/Core/test/Abstractions.Tests/AsyncEnumerableExecutableTests.cs create mode 100644 src/HotChocolate/Core/test/Abstractions.Tests/QueryableExecutableTests.cs diff --git a/src/HotChocolate/Core/src/Abstractions/Executable~1.cs b/src/HotChocolate/Core/src/Abstractions/Executable~1.cs index e6f8fd88dc8..594630dd4c2 100644 --- a/src/HotChocolate/Core/src/Abstractions/Executable~1.cs +++ b/src/HotChocolate/Core/src/Abstractions/Executable~1.cs @@ -14,28 +14,34 @@ namespace HotChocolate; /// public abstract class Executable : IExecutable { + /// public abstract object Source { get; } + /// public abstract ValueTask FirstOrDefaultAsync(CancellationToken cancellationToken = default); async ValueTask IExecutable.FirstOrDefaultAsync(CancellationToken cancellationToken) => await FirstOrDefaultAsync(cancellationToken); - + /// public abstract ValueTask SingleOrDefaultAsync(CancellationToken cancellationToken = default); async ValueTask IExecutable.SingleOrDefaultAsync(CancellationToken cancellationToken) => await SingleOrDefaultAsync(cancellationToken); + /// public abstract ValueTask> ToListAsync(CancellationToken cancellationToken = default); async ValueTask IExecutable.ToListAsync(CancellationToken cancellationToken) => await ToListAsync(cancellationToken); + /// public virtual string Print() => Source.ToString() ?? Source.GetType().FullName ?? Source.GetType().Name; + /// public sealed override string ToString() => Print(); + /// public abstract IAsyncEnumerable ToAsyncEnumerable(CancellationToken cancellationToken = default); async IAsyncEnumerable IExecutable.ToAsyncEnumerable( diff --git a/src/HotChocolate/Core/test/Abstractions.Tests/AsyncEnumerableExecutableTests.cs b/src/HotChocolate/Core/test/Abstractions.Tests/AsyncEnumerableExecutableTests.cs new file mode 100644 index 00000000000..1d1c0ec8465 --- /dev/null +++ b/src/HotChocolate/Core/test/Abstractions.Tests/AsyncEnumerableExecutableTests.cs @@ -0,0 +1,161 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; + +namespace HotChocolate; + +public static class AsyncEnumerableExecutableTests +{ + [Fact] + public static async Task EmptyAsyncEnumerable_FirstOrDefault_Null() + { + // arrange + IAsyncEnumerable query = new EmptyAsyncEnumerable(); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task ListAsyncEnumerable_FirstOrDefault_A() + { + // arrange + IAsyncEnumerable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task EmptyAsyncEnumerable_SingleOrDefault_Null() + { + // arrange + IAsyncEnumerable query = new EmptyAsyncEnumerable(); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task ListAsyncEnumerable_SingleOrDefault_A() + { + // arrange + IAsyncEnumerable query = new ListAsyncEnumerable(["a"]); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task ListAsyncEnumerable_SingleOrDefault_Throw() + { + // arrange + IAsyncEnumerable query = new ListAsyncEnumerable(["a", "b"]); + + // act + async Task Error() => await Executable.From(query).SingleOrDefaultAsync(); + + // assert + await Assert.ThrowsAsync(Error); + } + + [Fact] + public static async Task ListAsyncEnumerable_ToListAsync() + { + // arrange + IAsyncEnumerable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = await Executable.From(query).ToListAsync(); + + // assert + Assert.Collection( + result, + r => Assert.Equal("a", r), + r => Assert.Equal("b", r)); + } + + [Fact] + public static async Task ListAsyncEnumerable_ToAsyncEnumerable() + { + // arrange + IAsyncEnumerable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = Executable.From(query).ToAsyncEnumerable(); + + // assert + await foreach (var item in result) + { + Assert.True(item is "a" or "b"); + } + } + + private class EmptyAsyncEnumerable : IQueryable, IAsyncEnumerable + { + public Type ElementType + => throw new NotSupportedException(); + + public Expression Expression + => throw new NotSupportedException(); + + public IQueryProvider Provider + => throw new NotSupportedException(); + + public IEnumerator GetEnumerator() + => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() + => throw new NotSupportedException(); + + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + await new ValueTask(); + yield break; + } + } + + private class ListAsyncEnumerable(string[] items) : IQueryable, IAsyncEnumerable + { + public Type ElementType + => throw new NotSupportedException(); + + public Expression Expression + => throw new NotSupportedException(); + + public IQueryProvider Provider + => throw new NotSupportedException(); + + public IEnumerator GetEnumerator() + => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() + => throw new NotSupportedException(); + + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + await new ValueTask(); + + foreach (var item in items) + { + yield return item; + } + } + } +} diff --git a/src/HotChocolate/Core/test/Abstractions.Tests/QueryableExecutableTests.cs b/src/HotChocolate/Core/test/Abstractions.Tests/QueryableExecutableTests.cs new file mode 100644 index 00000000000..10f13e0901a --- /dev/null +++ b/src/HotChocolate/Core/test/Abstractions.Tests/QueryableExecutableTests.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using CookieCrumble; + +namespace HotChocolate; + +public static class QueryableExecutableTests +{ + [Fact] + public static void Queryable_Is_Null_Throws_ArgumentNullException() + { + Assert.Throws(() => Executable.From(((IQueryable)null)!)); + } + + [Fact] + public static void Queryable_Print() + { + // arrange + var query = Array.Empty().AsQueryable(); + + // act + var executable = Executable.From(query, _ => "foo"); + + // assert + Assert.Equal("foo", executable.Print()); + } + + [Fact] + public static void Queryable_Print_Snapshot() + { + // arrange + var query = Array.Empty().AsQueryable(); + + // act + var executable = Executable.From(query); + + // assert + executable.Print() + .MatchInlineSnapshot( + """ + System.String[] + """); + } + + [Fact] + public static void Queryable_Source_Is_Queryable() + { + // arrange + var query = Array.Empty().AsQueryable(); + var executable = Executable.From(query); + + // act + var source = executable.Source; + + // assert + Assert.Equal(query, source); + } + + [Fact] + public static void Queryable_Source_T_Is_Queryable() + { + // arrange + var query = Array.Empty().AsQueryable(); + var executable = Executable.From(query); + + // act + // ReSharper disable once RedundantCast + var source = ((IQueryableExecutable)executable).Source; + + // assert + Assert.Equal(query, source); + } + + [Fact] + public static void Queryable_IsInMemory_True() + { + // arrange + var query = Array.Empty().AsQueryable(); + var executable = Executable.From(query); + + // act + var isInMemory = executable.IsInMemory; + + // assert + Assert.True(isInMemory); + } + + [Fact] + public static async Task EmptyList_FirstOrDefault_Null() + { + // arrange + var query = Array.Empty().AsQueryable(); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task List_FirstOrDefault_A() + { + // arrange + var query = new[] { "a", "b" }; + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task EmptyAsyncEnumerable_FirstOrDefault_Null() + { + // arrange + IQueryable query = new EmptyAsyncEnumerable(); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task ListAsyncEnumerable_FirstOrDefault_A() + { + // arrange + IQueryable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task EmptyList_SingleOrDefault_Null() + { + // arrange + var query = Array.Empty().AsQueryable(); + + // act + var result = await Executable.From(query).SingleOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task List_SingleOrDefault_A() + { + // arrange + var query = new[] { "a" }; + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task List_SingleOrDefault_Throw() + { + // arrange + var query = new[] { "a", "b" }; + + // act + async Task Error() => await Executable.From(query).SingleOrDefaultAsync(); + + // assert + await Assert.ThrowsAsync(Error); + } + + [Fact] + public static async Task EmptyAsyncEnumerable_SingleOrDefault_Null() + { + // arrange + IQueryable query = new EmptyAsyncEnumerable(); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Null(result); + } + + [Fact] + public static async Task ListAsyncEnumerable_SingleOrDefault_A() + { + // arrange + IQueryable query = new ListAsyncEnumerable(["a"]); + + // act + var result = await Executable.From(query).FirstOrDefaultAsync(); + + // assert + Assert.Equal("a", result); + } + + [Fact] + public static async Task ListAsyncEnumerable_SingleOrDefault_Throw() + { + // arrange + IQueryable query = new ListAsyncEnumerable(["a", "b"]); + + // act + async Task Error() => await Executable.From(query).SingleOrDefaultAsync(); + + // assert + await Assert.ThrowsAsync(Error); + } + + [Fact] + public static async Task List_ToListAsync() + { + // arrange + var query = new[] { "a", "b" }.AsQueryable(); + + // act + var result = await Executable.From(query).ToListAsync(); + + // assert + Assert.Collection( + result, + r => Assert.Equal("a", r), + r => Assert.Equal("b", r)); + } + + [Fact] + public static async Task ListAsyncEnumerable_ToListAsync() + { + // arrange + IQueryable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = await Executable.From(query).ToListAsync(); + + // assert + Assert.Collection( + result, + r => Assert.Equal("a", r), + r => Assert.Equal("b", r)); + } + + [Fact] + public static async Task List_ToAsyncEnumerable() + { + // arrange + var query = new[] { "a", "b" }.AsQueryable(); + + // act + var result = Executable.From(query).ToAsyncEnumerable(); + + // assert + await foreach (var item in result) + { + Assert.True(item is "a" or "b"); + } + } + + [Fact] + public static async Task ListAsyncEnumerable_ToAsyncEnumerable() + { + // arrange + IQueryable query = new ListAsyncEnumerable(["a", "b"]); + + // act + var result = Executable.From(query).ToAsyncEnumerable(); + + // assert + await foreach (var item in result) + { + Assert.True(item is "a" or "b"); + } + } + + [Fact] + public static async Task List_WithSource() + { + // arrange + var query = new[] { "a", "b" }.AsQueryable(); + var executableA = Executable.From(query); + + // act + var executableB = executableA.WithSource(new[] { "a", "b", "c" }.AsQueryable()); + + // assert + Assert.NotEqual(executableA, executableB); + + Assert.Collection( + await executableA.ToListAsync(), + r => Assert.Equal("a", r), + r => Assert.Equal("b", r)); + + Assert.Collection( + await executableB.ToListAsync(), + r => Assert.Equal("a", r), + r => Assert.Equal("b", r), + r => Assert.Equal("c", r)); + } + + private class EmptyAsyncEnumerable : IQueryable, IAsyncEnumerable + { + public Type ElementType + => throw new NotSupportedException(); + + public Expression Expression + => throw new NotSupportedException(); + + public IQueryProvider Provider + => throw new NotSupportedException(); + + public IEnumerator GetEnumerator() + => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() + => throw new NotSupportedException(); + + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + await new ValueTask(); + yield break; + } + } + + private class ListAsyncEnumerable(string[] items) : IQueryable, IAsyncEnumerable + { + public Type ElementType + => throw new NotSupportedException(); + + public Expression Expression + => throw new NotSupportedException(); + + public IQueryProvider Provider + => throw new NotSupportedException(); + + public IEnumerator GetEnumerator() + => throw new NotSupportedException(); + + IEnumerator IEnumerable.GetEnumerator() + => throw new NotSupportedException(); + + public async IAsyncEnumerator GetAsyncEnumerator(CancellationToken cancellationToken = default) + { + await new ValueTask(); + + foreach (var item in items) + { + yield return item; + } + } + } +}