Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
315 changes: 307 additions & 8 deletions QueryKit.IntegrationTests/Tests/DatabaseFilteringTests.cs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
namespace QueryKit.IntegrationTests.Tests;

using System.Linq.Expressions;
using Bogus;
using Configuration;
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using SharedTestingHelper.Fakes;
using SharedTestingHelper.Fakes.Author;
using SharedTestingHelper.Fakes.IngredientPreparations;
using SharedTestingHelper.Fakes.Ingredients;
using SharedTestingHelper.Fakes.Recipes;
using WebApiTestProject.Database;
using WebApiTestProject.Entities;
using WebApiTestProject.Entities.Ingredients;
using WebApiTestProject.Entities.Ingredients.Models;
using WebApiTestProject.Entities.Recipes;
using WebApiTestProject.Features;
using Xunit.Abstractions;

public class DatabaseFilteringTests : TestBase
public class DatabaseFilteringTests(ITestOutputHelper testOutputHelper) : TestBase
{
[Fact]
public async Task can_filter_by_string()
Expand All @@ -40,6 +42,179 @@ public async Task can_filter_by_string()
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_boolean()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var faker = new Faker();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithTitle(faker.Lorem.Sentence())
.WithFavorite(true)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithTitle(faker.Lorem.Sentence())
.WithFavorite(false)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""{nameof(TestingPerson.Title)} == "{fakePersonOne.Title}" && {nameof(TestingPerson.Favorite)} == true""";

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_combo_multi_value_pass()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder().Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithFirstName(fakePersonOne.FirstName)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""fullname @=* "{fakePersonOne.FirstName} {fakePersonOne.LastName}" """;
var config = new QueryKitConfiguration(config =>
{
config.DerivedProperty<TestingPerson>(tp => tp.FirstName + " " + tp.LastName).HasQueryName("fullname");
});

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_combo_complex()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithAge(8888)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithFirstName(fakePersonOne.FirstName)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""(fullname @=* "{fakePersonOne.FirstName} {fakePersonOne.LastName}") && age >= {fakePersonOne.Age}""";
var config = new QueryKitConfiguration(config =>
{
config.DerivedProperty<TestingPerson>(tp => tp.FirstName + " " + tp.LastName).HasQueryName("fullname");
});

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Theory]
[InlineData(88448)]
[InlineData(-83388)]
public async Task can_filter_by_int(int age)
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithAge(age)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithFirstName(fakePersonOne.FirstName)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""age == {fakePersonOne.Age}""";
var config = new QueryKitConfiguration(_ =>
{
});

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_and_also_bool()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithFirstName("John")
.WithAge(18)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder().Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""adult_johns == true""";
var config = new QueryKitConfiguration(config =>
{
config.DerivedProperty<TestingPerson>(tp => tp.Age >= 18 && tp.FirstName == "John").HasQueryName("adult_johns");
});

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_combo()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithFirstName(Guid.NewGuid().ToString())
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder().Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""fullname @=* "{fakePersonOne.FirstName}" """;
var config = new QueryKitConfiguration(config =>
{
config.DerivedProperty<TestingPerson>(tp => tp.FirstName + " " + tp.LastName).HasQueryName("fullname");
});

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input, config);
var people = await appliedQueryable.ToListAsync();
// var people = testingServiceScope.DbContext().People
// // .Where(p => (p.FirstName + " " + p.LastName).ToLower().Contains(fakePersonOne.FirstName.ToLower()))
// // .Where(x => ((x.FirstName + " ") + x.LastName).ToLower().Contains("ito".ToLower()))
// .ToList();

// Assert
people.Count.Should().Be(1);
people[0].Id.Should().Be(fakePersonOne.Id);
}

[Fact]
public async Task can_filter_by_string_for_collection()
{
Expand Down Expand Up @@ -164,6 +339,71 @@ public async Task can_filter_by_string_for_collection_contains()
recipes[0].Id.Should().Be(fakeRecipeOne.Id);
}

[Fact]
public async Task can_filter_within_collection_long()
{
var testingServiceScope = new TestingServiceScope();
var qualityLevel = 2L;
var fakeRecipeOne = new FakeRecipeBuilder().Build();
var ingredient = new FakeIngredientBuilder()
.WithQualityLevel(qualityLevel)
.Build();
fakeRecipeOne.AddIngredient(ingredient);

await testingServiceScope.InsertAsync(fakeRecipeOne);

var input = $"Ingredients.QualityLevel == {qualityLevel}";
var config = new QueryKitConfiguration(settings =>
{
settings.Property<Recipe>(x => x.Ingredients.Select(y => y.QualityLevel)).PreventSort();
});

var queryableRecipes = testingServiceScope.DbContext().Recipes;
var appliedQueryable = queryableRecipes.ApplyQueryKitFilter(input);
var recipes = await appliedQueryable.ToListAsync();

recipes.Count.Should().Be(1);
recipes[0].Id.Should().Be(fakeRecipeOne.Id);
}

[Fact(Skip = "Can not handle nested collections yet.")]
public async Task can_filter_by_string_for_nested_collection()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var faker = new Faker();
var preparationOne = new FakeIngredientPreparation().Generate();
var preparationTwo = new FakeIngredientPreparation().Generate();
var fakeIngredientOne = new FakeIngredientBuilder()
.WithPreparation(preparationOne)
.Build();
var fakeRecipeOne = new FakeRecipeBuilder().Build();
fakeRecipeOne.AddIngredient(fakeIngredientOne);

var fakeIngredientTwo = new FakeIngredientBuilder()
.WithName(faker.Lorem.Sentence())
.WithPreparation(preparationTwo)
.Build();
var fakeRecipeTwo = new FakeRecipeBuilder().Build();
fakeRecipeTwo.AddIngredient(fakeIngredientTwo);
await testingServiceScope.InsertAsync(fakeRecipeOne, fakeRecipeTwo);

var input = $"""Ingredients.Preparations.Text == "{preparationOne.Text}" """;
var config = new QueryKitConfiguration(settings =>
{
settings.Property<Recipe>(x => x.Ingredients.SelectMany(y => y.Preparations).Select(y => y.Text));
});

// Act
var queryableRecipes = testingServiceScope.DbContext().Recipes;
var appliedQueryable = queryableRecipes.ApplyQueryKitFilter(input, config);
var recipes = await appliedQueryable.ToListAsync();

// Assert
recipes.Count.Should().Be(1);
recipes[0].Id.Should().Be(fakeRecipeOne.Id);
}

[Fact]
public async Task can_filter_by_string_for_collection_does_not_contain()
{
Expand Down Expand Up @@ -577,7 +817,7 @@ public async Task can_filter_nested_property_using_ownsone_with_alias()
.WithPhysicalAddress(new Address(faker.Address.StreetAddress()
, faker.Address.SecondaryAddress()
, faker.Address.City()
, faker.Address.State()
, Guid.NewGuid().ToString()
, faker.Address.ZipCode()
, faker.Address.Country()))
.Build();
Expand Down Expand Up @@ -621,6 +861,31 @@ public async Task can_filter_by_decimal()
people.Count(x => x.Id == fakePersonOne.Id).Should().Be(1);
}

[Fact]
public async Task can_filter_by_negative_decimal()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var faker = new Faker();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithRating(-3.533M)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithRating(2M)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);

var input = $"""{nameof(TestingPerson.Rating)} == -3.533""";

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count(x => x.Id == fakePersonOne.Id).Should().Be(1);
}

[Fact]
public async Task can_filter_complex_expression()
{
Expand Down Expand Up @@ -687,24 +952,58 @@ public async Task can_handle_in_for_int()
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithAge(22)
.WithAge(-22)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithAge(40)
.Build();
var fakePersonThree = new FakeTestingPersonBuilder()
.WithAge(60)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo);
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo, fakePersonThree);

var input = """Age ^^ [22, 30, 40]""";
var input = """Age ^^ [-22, 60]""";

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().BeGreaterOrEqualTo(1);
people.Count.Should().BeGreaterOrEqualTo(2);
people.FirstOrDefault(x => x.Id == fakePersonOne.Id).Should().NotBeNull();
people.FirstOrDefault(x => x.Id == fakePersonTwo.Id).Should().BeNull();
people.FirstOrDefault(x => x.Id == fakePersonThree.Id).Should().NotBeNull();
}

[Fact]
public async Task can_handle_in_for_decimal()
{
// Arrange
var testingServiceScope = new TestingServiceScope();
var fakePersonOne = new FakeTestingPersonBuilder()
.WithRating(-22.44M)
.Build();
var fakePersonTwo = new FakeTestingPersonBuilder()
.WithRating(40.55M)
.Build();
var fakePersonThree = new FakeTestingPersonBuilder()
.WithRating(60.99M)
.Build();
await testingServiceScope.InsertAsync(fakePersonOne, fakePersonTwo, fakePersonThree);

var input = """Rating ^^ [-22.44, 60.99]""";

// Act
var queryablePeople = testingServiceScope.DbContext().People;
var appliedQueryable = queryablePeople.ApplyQueryKitFilter(input);
var people = await appliedQueryable.ToListAsync();

// Assert
people.Count.Should().BeGreaterOrEqualTo(2);
people.FirstOrDefault(x => x.Id == fakePersonOne.Id).Should().NotBeNull();
people.FirstOrDefault(x => x.Id == fakePersonTwo.Id).Should().BeNull();
people.FirstOrDefault(x => x.Id == fakePersonThree.Id).Should().NotBeNull();
}

[Fact]
Expand Down
Loading