Skip to content

Commit

Permalink
Added tests for filtering in combination with paging. (#2317)
Browse files Browse the repository at this point in the history
  • Loading branch information
PascalSenn committed Sep 26, 2020
1 parent a127779 commit 5b478a9
Show file tree
Hide file tree
Showing 38 changed files with 371 additions and 168 deletions.
Expand Up @@ -4,6 +4,7 @@
using HotChocolate.Resolvers;
using HotChocolate.Types;
using System.Linq;
using HotChocolate.Types.Relay;

namespace HotChocolate.Data.Filters
{
Expand All @@ -26,7 +27,8 @@ public FilterVisitorTestBase()

protected IRequestExecutor CreateSchema<TEntity, T>(
TEntity[] entities,
FilterConvention? convention = null)
FilterConvention? convention = null,
bool withPaging = false)
where TEntity : class
where T : FilterInputType<TEntity>
{
Expand All @@ -37,13 +39,23 @@ public FilterVisitorTestBase()
ISchemaBuilder builder = SchemaBuilder.New()
.AddConvention<IFilterConvention>(convention)
.AddFiltering()
.AddQueryType(c => c
.Name("Query")
.Field("root")
.Resolver(resolver)
.UseFiltering<T>());

ISchema? schema = builder.Create();
.AddQueryType(
c =>
{
IObjectFieldDescriptor field = c
.Name("Query")
.Field("root")
.Resolver(resolver);
if (withPaging)
{
field.UsePaging<ObjectType<TEntity>>();
}
field.UseFiltering<T>();
});

ISchema schema = builder.Create();

return schema.MakeExecutable();
}
Expand Down
@@ -0,0 +1,67 @@
using System.Threading.Tasks;
using HotChocolate.Data.Filters;
using HotChocolate.Execution;
using Snapshooter;
using Snapshooter.Xunit;
using Xunit;

namespace HotChocolate.Data.Filters
{
public class FilteringAndPaging
{
private static readonly Foo[] _fooEntities =
{
new Foo { Bar = true },
new Foo { Bar = false }
};

private readonly SchemaCache _cache = new SchemaCache();

[Fact]
public async Task Create_BooleanEqual_Expression()
{
// arrange
IRequestExecutor tester = _cache.CreateSchema<Foo, FooFilterType>(_fooEntities, true);

// act
// assert
IExecutionResult res1 = await tester.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery("{ root(where: { bar: { eq: true}}){ nodes { bar } }}")
.Create());

res1.ToJson().MatchSnapshot(new SnapshotNameExtension("true"));

IExecutionResult res2 = await tester.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery("{ root(where: { bar: { eq: false}}){ nodes { bar }}}")
.Create());

res2.ToJson().MatchSnapshot(new SnapshotNameExtension("false"));
}

public class Foo
{
public int Id { get; set; }

public bool Bar { get; set; }
}

public class FooNullable
{
public int Id { get; set; }

public bool? Bar { get; set; }
}

public class FooFilterType
: FilterInputType<Foo>
{
}

public class FooNullableFilterType
: FilterInputType<FooNullable>
{
}
}
}
Expand Up @@ -4,23 +4,21 @@

namespace HotChocolate.Data.Filters
{
public class SchemaCache : FilterVisitorTestBase, IDisposable
public class SchemaCache
: FilterVisitorTestBase
, IDisposable
{
private readonly ConcurrentDictionary<(Type, Type, object), IRequestExecutor> _cache =
new ConcurrentDictionary<(Type, Type, object), IRequestExecutor>();

public SchemaCache()
: base()
{

}

public IRequestExecutor CreateSchema<T, TType>(T[] entites)
public IRequestExecutor CreateSchema<T, TType>(T[] entities, bool withPaging = false)
where T : class
where TType : FilterInputType<T>
{
(Type, Type, T[] entites) key = (typeof(T), typeof(TType), entites);
return _cache.GetOrAdd(key, (k) => base.CreateSchema<T, TType>(entites));
(Type, Type, T[] entites) key = (typeof(T), typeof(TType), entities);
return _cache.GetOrAdd(
key,
k => base.CreateSchema<T, TType>(entities, withPaging: withPaging));
}

public void Dispose()
Expand Down
@@ -0,0 +1,11 @@
{
"data": {
"root": {
"nodes": [
{
"bar": false
}
]
}
}
}
@@ -0,0 +1,11 @@
{
"data": {
"root": {
"nodes": [
{
"bar": true
}
]
}
}
}
Expand Up @@ -5,6 +5,7 @@
using HotChocolate.Execution.Configuration;
using HotChocolate.Resolvers;
using HotChocolate.Types;
using HotChocolate.Types.Relay;
using Microsoft.Extensions.DependencyInjection;

namespace HotChocolate.Data.Filters
Expand Down Expand Up @@ -42,7 +43,8 @@ public class FilterVisitorTestBase

protected IRequestExecutor CreateSchema<TEntity, T>(
TEntity[] entities,
FilterConvention? convention = null)
FilterConvention? convention = null,
bool withPaging = false)
where TEntity : class
where T : FilterInputType<TEntity>
{
Expand All @@ -54,47 +56,60 @@ public class FilterVisitorTestBase
.AddConvention<IFilterConvention>(convention)
.AddFiltering()
.AddQueryType(
c => c
.Name("Query")
.Field("root")
.Resolver(resolver)
.Use(next => async context =>
c =>
{
IObjectFieldDescriptor field = c
.Name("Query")
.Field("root")
.Resolver(resolver)
.Use(
next => async context =>
{
await next(context);
if (context.Result is IQueryable<TEntity> queryable)
{
try
{
context.ContextData["sql"] = queryable.ToQueryString();
}
catch (Exception)
{
context.ContextData["sql"] =
"EF Core 3.1 does not support ToQueryString";
}
}
});
if (withPaging)
{
await next(context);
field.UsePaging<ObjectType<TEntity>>();
}
if (context.Result is IQueryable<TEntity> queryable)
{
try
{
context.ContextData["sql"] = queryable.ToQueryString();
}
catch (Exception)
{
context.ContextData["sql"] =
"EF Core 3.1 does not support ToQuerString offically";
}
}
})
.UseFiltering<T>());
field.UseFiltering<T>();
});

ISchema? schema = builder.Create();
ISchema schema = builder.Create();

return new ServiceCollection()
.Configure<RequestExecutorFactoryOptions>(Schema.DefaultName, o => o.Schema = schema)
.Configure<RequestExecutorFactoryOptions>(
Schema.DefaultName,
o => o.Schema = schema)
.AddGraphQL()
.UseRequest(next => async context =>
{
await next(context);
if (context.Result is IReadOnlyQueryResult result &&
context.ContextData.TryGetValue("sql", out var queryString))
.UseRequest(
next => async context =>
{
context.Result =
QueryResultBuilder
.FromResult(result)
.SetContextData("sql", queryString)
.Create();
}
})
await next(context);
if (context.Result is IReadOnlyQueryResult result &&
context.ContextData.TryGetValue("sql", out var queryString))
{
context.Result =
QueryResultBuilder
.FromResult(result)
.SetContextData("sql", queryString)
.Create();
}
})
.UseDefaultPipeline()
.Services
.BuildServiceProvider()
Expand Down
@@ -0,0 +1,65 @@
using System.Threading.Tasks;
using HotChocolate.Data.Filters;
using HotChocolate.Execution;
using Xunit;

namespace HotChocolate.Data.Filters
{
public class FilteringAndPaging
{
private static readonly Foo[] _fooEntities =
{
new Foo { Bar = true },
new Foo { Bar = false }
};

private readonly SchemaCache _cache = new SchemaCache();

[Fact]
public async Task Create_BooleanEqual_Expression()
{
// arrange
IRequestExecutor tester = _cache.CreateSchema<Foo, FooFilterType>(_fooEntities, true);

// act
IExecutionResult res1 = await tester.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery("{ root(where: { bar: { eq: true}}){ nodes { bar } }}")
.Create());

IExecutionResult res2 = await tester.ExecuteAsync(
QueryRequestBuilder.New()
.SetQuery("{ root(where: { bar: { eq: false}}){ nodes { bar }}}")
.Create());

// assert
res1.MatchSqlSnapshot("true");
res2.MatchSqlSnapshot("false");
}


public class Foo
{
public int Id { get; set; }

public bool Bar { get; set; }
}

public class FooNullable
{
public int Id { get; set; }

public bool? Bar { get; set; }
}

public class FooFilterType
: FilterInputType<Foo>
{
}

public class FooNullableFilterType
: FilterInputType<FooNullable>
{
}
}
}

0 comments on commit 5b478a9

Please sign in to comment.