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
30 changes: 30 additions & 0 deletions src/AutoFilterer/Attributes/ToLowerEqualsComparisonAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;

namespace AutoFilterer.Attributes;

/// <summary>
/// This class generates equal query, for string fields.
/// </summary>
public class ToLowerEqualsComparisonAttribute : FilteringOptionsBaseAttribute
{
public override Expression BuildExpression(ExpressionBuildContext context)
{
var equalsMethod = typeof(string).GetMethod(nameof(string.Equals), types: new[] { typeof(string) });

var toLowerMethod = typeof(string).GetMethod(nameof(string.ToLower), types: new Type[0]);
Comment thread
enisn marked this conversation as resolved.

var comparison = Expression.Equal(
Expression.Call(
method: equalsMethod,
instance: Expression.Call(method: toLowerMethod, instance: Expression.Property(context.ExpressionBody, context.TargetProperty.Name)
),
arguments: new[] { Expression.Call(method: toLowerMethod, instance: context.FilterPropertyExpression) }),
Expression.Constant(true));
Comment thread
enisn marked this conversation as resolved.
Comment thread
enisn marked this conversation as resolved.

Comment thread
enisn marked this conversation as resolved.
return comparison;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
using AutoFilterer.Extensions;
using AutoFilterer.Tests.Core;
using AutoFilterer.Tests.Environment.Dtos;
using AutoFilterer.Tests.Environment.Models;
using System.Collections.Generic;
using System.Linq;
using Xunit;
using AutoFilterer.Attributes;
using AutoFilterer.Types;
using System;

namespace AutoFilterer.Tests.Attributes;

public class ToLowerEqualsComparisonAttributeTests
{
[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldGenerateQueryCorrect_WithAttribute(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "Test Book"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldBeCaseInsensitive(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "TEST BOOK"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldHandleEmptyString(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = ""
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldHandleNullValue(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = null
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
// When filter value is null, the framework should skip the filter entirely
// and return all results (no filtering applied)
Assert.Equal(dummyData.Count, result.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldHandleSpecialCharacters(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "Test-Book_123"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldHandleWhitespace(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = " Test Book "
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldHandleUnicodeCharacters(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "Café Book"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldReturnEmptyResult_WhenNoMatch(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "NonExistentBook"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);
Assert.Empty(result);
}

[Theory, AutoMoqData(count: 16)]
public void BuildExpression_ShouldReturnExactMatchesOnly(List<Book> dummyData)
{
// Arrange
var filter = new BookFilter_LowerEquals
{
Title = "Test"
};
IQueryable<Book> query = dummyData.AsQueryable();

// Act
var filteredQuery = query.ApplyFilter(filter);
var result = filteredQuery.ToList();

// Assert
var actualResult = query.Where(x => x.Title.ToLower().Equals(filter.Title.ToLower())).ToList();

Assert.Equal(result.Count, actualResult.Count);

// Verify that all results are exact matches (not partial matches)
foreach (var book in result)
{
Assert.Equal(filter.Title.ToLower(), book.Title.ToLower());
}
}
}

public class BookFilter_LowerEquals : FilterBase
{
[ToLowerEqualsComparison]
public string Title { get; set; }
}
Loading