chore: deploy initial code base
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
using NUnit.Framework;
|
||||
using Strata.RxNorm.Biz.Utilities;
|
||||
using System.Collections.Generic;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Strata.RxNorm.Biz.Test.Unit.Utilities
|
||||
{
|
||||
[TestFixture]
|
||||
public class LogUtilsExtensionsTests
|
||||
{
|
||||
[TestCaseSource(nameof(TestCases))]
|
||||
public void Test(object input, object expected)
|
||||
{
|
||||
var dictionary = LogUtilsExtensions.ToDictionary<object>(input);
|
||||
var expectedStr = JsonSerializer.Serialize(expected);
|
||||
var actualStr = JsonSerializer.Serialize(dictionary);
|
||||
Assert.That(actualStr, Is.EqualTo(expectedStr));
|
||||
}
|
||||
|
||||
public static IEnumerable<TestCaseData> TestCases()
|
||||
{
|
||||
yield return new TestCaseData(new { a = 1, b = "test" }, new Dictionary<string, object> { { "a", 1 }, { "b", "test" } });
|
||||
yield return new TestCaseData(new { a = 1, b = (string)null }, new Dictionary<string, object> { { "a", 1 }, { "b", null } });
|
||||
yield return new TestCaseData(new { a = 1, b = new { c = 2 } }, new Dictionary<string, object> { { "a", 1 }, { "b", new Dictionary<string, object> { { "c", 2 } } } });
|
||||
yield return new TestCaseData(123, new Dictionary<string, object> { { "object", 123 } });
|
||||
yield return new TestCaseData("test", new Dictionary<string, object> { { "object", "test" } });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
#nullable enable
|
||||
using NUnit.Framework;
|
||||
using Strata.RxNorm.Biz.Utilities;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using static Strata.RxNorm.Biz.Utilities.SearchFilterExpressions;
|
||||
|
||||
namespace Strata.RxNorm.Biz.Test.Unit.Utilities
|
||||
{
|
||||
[TestFixture]
|
||||
public class SearchFilterExpressionsTests
|
||||
{
|
||||
// Test entity classes for testing
|
||||
private class TestEntity
|
||||
{
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Code { get; set; } = string.Empty;
|
||||
public string Description { get; set; } = string.Empty;
|
||||
public string Category { get; set; } = string.Empty;
|
||||
public string? NullableField { get; set; }
|
||||
}
|
||||
|
||||
private enum TestEnum
|
||||
{
|
||||
Value1,
|
||||
Value2,
|
||||
Value3
|
||||
}
|
||||
|
||||
#region AddSearchFilters Tests
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithNullSearch_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters(null, _ => e => true);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithEmptySearch_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters("", _ => e => true);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithWhitespaceSearch_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters(" ", _ => e => true);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithSingleTerm_AddsOneFilter()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters("test", term => e => e.Name.Contains(term));
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithMultipleTerms_CombinesWithOrLogic()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testData = new List<TestEntity>
|
||||
{
|
||||
new() { Name = "apple" },
|
||||
new() { Name = "banana" },
|
||||
new() { Name = "cherry" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters("apple,banana", term => e => e.Name.Contains(term));
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testData[0]), Is.True, "apple should match");
|
||||
Assert.That(compiled(testData[1]), Is.True, "banana should match");
|
||||
Assert.That(compiled(testData[2]), Is.False, "cherry should not match");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithNegationTerm_CombinesWithAndLogic()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testData = new List<TestEntity>
|
||||
{
|
||||
new() { Name = "apple" },
|
||||
new() { Name = "banana" },
|
||||
new() { Name = "applebana" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = filters.AddSearchFilters("apple,!banana", term =>
|
||||
{
|
||||
var cleanTerm = term.StartsWith('!') ? term[1..] : term;
|
||||
return term.StartsWith('!')
|
||||
? e => !e.Name.Contains(cleanTerm)
|
||||
: e => e.Name.Contains(cleanTerm);
|
||||
});
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testData[0]), Is.True, "apple and !banana should match");
|
||||
Assert.That(compiled(testData[1]), Is.False, "banana should fail !banana");
|
||||
//Assert.That(compiled(testData[2]), Is.False, "applebana should fail !banana");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(AddSearchFiltersTermsTestCases))]
|
||||
public void AddSearchFilters_WithVariousTermFormats_ParsesCorrectly(string search, int expectedTerms)
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var termsCaptured = new List<string>();
|
||||
|
||||
// Act
|
||||
filters.AddSearchFilters(search, term =>
|
||||
{
|
||||
termsCaptured.Add(term);
|
||||
return e => true;
|
||||
});
|
||||
|
||||
// Assert
|
||||
Assert.That(termsCaptured, Has.Count.EqualTo(expectedTerms));
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> AddSearchFiltersTermsTestCases()
|
||||
{
|
||||
yield return new TestCaseData("apple,banana,cherry", 3)
|
||||
.SetName("{m}(search: {0}, expectedTerms: {1})");
|
||||
yield return new TestCaseData(" apple , banana , cherry ", 3)
|
||||
.SetName("{m}(search: \"{0}\", expectedTerms: {1})");
|
||||
yield return new TestCaseData("apple,,banana", 2)
|
||||
.SetName("{m}(search: {0}, expectedTerms: {1})");
|
||||
yield return new TestCaseData("apple", 1)
|
||||
.SetName("{m}(search: {0}, expectedTerms: {1})");
|
||||
yield return new TestCaseData("", 0)
|
||||
.SetName("{m}(search: \"{0}\", expectedTerms: {1})");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region AddEntityFilters Tests
|
||||
|
||||
[Test]
|
||||
public void AddEntityFilters_WithNullSearch_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddEntityFilters(null, e => e.Name);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEntityFilters_WithEmptyPropertySelectors_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddEntityFilters("test");
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEntityFilters_WithValidInput_AddsFilter()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testEntity = new TestEntity { Name = "test" };
|
||||
|
||||
// Act
|
||||
var result = filters.AddEntityFilters("test", e => e.Name);
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testEntity), Is.True);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEntityFilters_WithNegation_CreatesNegationExpression()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testEntity1 = new TestEntity { Name = "test" };
|
||||
var testEntity2 = new TestEntity { Name = "other" };
|
||||
|
||||
// Act
|
||||
var result = filters.AddEntityFilters("!test", e => e.Name);
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testEntity1), Is.False, "should not match entities containing 'test'");
|
||||
Assert.That(compiled(testEntity2), Is.True, "should match entities not containing 'test'");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CreateEntityFilters Tests
|
||||
|
||||
[TestCaseSource(nameof(CreateEntityFiltersTestCases))]
|
||||
public void CreateEntityFilters_WithVariousInputs_ReturnsExpectedResults(
|
||||
string search,
|
||||
string[] entityValues,
|
||||
bool[] expectedResults)
|
||||
{
|
||||
// Arrange
|
||||
var testEntities = entityValues.Select(v => new TestEntity { Name = v }).ToList();
|
||||
|
||||
// Act
|
||||
var filters = search.CreateEntityFilters<TestEntity>(e => e.Name);
|
||||
|
||||
// Assert
|
||||
if (string.IsNullOrEmpty(search))
|
||||
{
|
||||
Assert.That(filters, Has.Count.EqualTo(0));
|
||||
return;
|
||||
}
|
||||
|
||||
Assert.That(filters, Has.Count.EqualTo(1));
|
||||
var compiled = filters.First().Compile();
|
||||
|
||||
for (int i = 0; i < testEntities.Count; i++)
|
||||
{
|
||||
Assert.That(compiled(testEntities[i]), Is.EqualTo(expectedResults[i]),
|
||||
$"Entity {i} ('{entityValues[i]}') should be {expectedResults[i]}");
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> CreateEntityFiltersTestCases()
|
||||
{
|
||||
yield return new TestCaseData(
|
||||
null,
|
||||
new[] { "test" },
|
||||
new[] { false }
|
||||
).SetName("{m}(search: null, entities: [\"{1}\"], expected: [{2}])");
|
||||
|
||||
yield return new TestCaseData(
|
||||
"",
|
||||
new[] { "test" },
|
||||
new[] { false }
|
||||
).SetName("{m}(search: \"\", entities: [\"{1}\"], expected: [{2}])");
|
||||
|
||||
yield return new TestCaseData(
|
||||
"apple",
|
||||
new[] { "apple", "banana", "pineapple", "grape" },
|
||||
new[] { true, false, true, false }
|
||||
).SetName("{m}(search: \"{0}\", entities: [{1}], expected: [{2}])");
|
||||
|
||||
yield return new TestCaseData(
|
||||
"apple,banana",
|
||||
new[] { "apple", "banana", "cherry", "applebana" },
|
||||
new[] { true, true, false, true }
|
||||
).SetName("{m}(search: \"{0}\", entities: [{1}], expected: [{2}])");
|
||||
|
||||
yield return new TestCaseData(
|
||||
"!apple",
|
||||
new[] { "apple", "banana", "pineapple", "grape" },
|
||||
new[] { false, true, false, true }
|
||||
).SetName("{m}(search: \"{0}\", entities: [{1}], expected: [{2}])");
|
||||
|
||||
yield return new TestCaseData(
|
||||
"APPLE",
|
||||
new[] { "apple", "APPLE", "Apple" },
|
||||
new[] { true, true, true }
|
||||
).SetName("{m}(search: \"{0}\", entities: [{1}], expected: [{2}])");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region CreateEntityFilters with EnumFilters Tests
|
||||
|
||||
[Test]
|
||||
public void CreateEntityFilters_WithEnumFilters_AppliesBothSearchAndEnumFilters()
|
||||
{
|
||||
// Arrange
|
||||
var testEntities = new[]
|
||||
{
|
||||
new TestEntity { Name = "test1", Category = "VALUE1" },
|
||||
new TestEntity { Name = "test2", Category = "VALUE2" },
|
||||
new TestEntity { Name = "other", Category = "VALUE1" },
|
||||
new TestEntity { Name = "test3", Category = "VALUE3" }
|
||||
};
|
||||
|
||||
var enumFilters = new[]
|
||||
{
|
||||
new EnumFilter<TestEntity>(new TestEnum[] { TestEnum.Value1, TestEnum.Value2 }, e => e.Category)
|
||||
};
|
||||
|
||||
// Act
|
||||
var filters = "test".CreateEntityFilters(enumFilters, e => e.Name);
|
||||
|
||||
// Assert
|
||||
Assert.That(filters, Has.Count.EqualTo(2), "should have one filter for search and one for enum");
|
||||
|
||||
var query = testEntities.AsQueryable();
|
||||
foreach (var filter in filters)
|
||||
{
|
||||
query = query.Where(filter);
|
||||
}
|
||||
|
||||
var results = query.ToList();
|
||||
Assert.That(results, Has.Count.EqualTo(2), "should match test1 and test2");
|
||||
Assert.That(results.All(r => r.Name.Contains("test")), Is.True, "all results should contain 'test'");
|
||||
Assert.That(results.All(r => r.Category is "VALUE1" or "VALUE2"), Is.True, "all results should have matching categories");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateEntityFilters_WithNullEnumFilters_OnlyAppliesSearchFilters()
|
||||
{
|
||||
// Arrange
|
||||
var testEntity = new TestEntity { Name = "test" };
|
||||
|
||||
// Act
|
||||
var filters = SearchFilterExpressions.CreateEntityFilters<TestEntity>("test", (SearchFilterExpressions.EnumFilter<TestEntity>[]?)null, e => e.Name);
|
||||
|
||||
// Assert
|
||||
Assert.That(filters, Has.Count.EqualTo(1));
|
||||
Assert.That(filters.First().Compile()(testEntity), Is.True);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region AddEnumFilter Tests
|
||||
|
||||
[Test]
|
||||
public void AddEnumFilter_WithNullArray_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddEnumFilter(null, e => e.Category);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEnumFilter_WithEmptyArray_ReturnsOriginalFilters()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act
|
||||
var result = filters.AddEnumFilter(new TestEnum[0], e => e.Category);
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.SameAs(filters));
|
||||
Assert.That(result, Has.Count.EqualTo(0));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEnumFilter_WithValidEnums_AddsFilter()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testEntities = new[]
|
||||
{
|
||||
new TestEntity { Category = "VALUE1" },
|
||||
new TestEntity { Category = "VALUE2" },
|
||||
new TestEntity { Category = "VALUE3" },
|
||||
new TestEntity { Category = null }
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = filters.AddEnumFilter(new[] { TestEnum.Value1, TestEnum.Value2 }, e => e.Category);
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testEntities[0]), Is.True, "VALUE1 should match");
|
||||
Assert.That(compiled(testEntities[1]), Is.True, "VALUE2 should match");
|
||||
Assert.That(compiled(testEntities[2]), Is.False, "VALUE3 should not match");
|
||||
Assert.That(compiled(testEntities[3]), Is.False, "null should not match");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(AddEnumFilterCaseTestCases))]
|
||||
public void AddEnumFilter_WithMixedCaseProperty_MatchesCorrectly(string categoryValue, bool expectedMatch)
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testEntity = new TestEntity { Category = categoryValue };
|
||||
|
||||
// Act
|
||||
var result = filters.AddEnumFilter(new[] { TestEnum.Value1 }, e => e.Category);
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Has.Count.EqualTo(1));
|
||||
Assert.That(compiled(testEntity), Is.EqualTo(expectedMatch));
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> AddEnumFilterCaseTestCases()
|
||||
{
|
||||
yield return new TestCaseData("value1", false)
|
||||
.SetName("{m}(categoryValue: \"{0}\", expectedMatch: {1})");
|
||||
yield return new TestCaseData("Value1", false)
|
||||
.SetName("{m}(categoryValue: \"{0}\", expectedMatch: {1})");
|
||||
yield return new TestCaseData("VALUE1", true)
|
||||
.SetName("{m}(categoryValue: \"{0}\", expectedMatch: {1})");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region EnumFilter Record Tests
|
||||
|
||||
[Test]
|
||||
public void EnumFilter_CanBeCreated_WithNullValues()
|
||||
{
|
||||
// Act
|
||||
var filter = new EnumFilter<TestEntity>(null, e => e.Category);
|
||||
|
||||
// Assert
|
||||
Assert.That(filter.EnumValues, Is.Null);
|
||||
Assert.That(filter.PropertySelector, Is.Not.Null);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EnumFilter_CanBeCreated_WithValidValues()
|
||||
{
|
||||
// Arrange
|
||||
var enumArray = new[] { TestEnum.Value1, TestEnum.Value2 };
|
||||
|
||||
// Act
|
||||
var filter = new EnumFilter<TestEntity>(enumArray, e => e.Category);
|
||||
|
||||
// Assert
|
||||
Assert.That(filter.EnumValues, Is.SameAs(enumArray));
|
||||
Assert.That(filter.PropertySelector, Is.Not.Null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Edge Cases and Error Conditions
|
||||
|
||||
[Test]
|
||||
public void CreateEntityFilters_WithNullPropertySelector_ThrowsArgumentNullException()
|
||||
{
|
||||
// Act & Assert
|
||||
Assert.Throws<ArgumentNullException>(() =>
|
||||
{
|
||||
var filters = SearchFilterExpressions.CreateEntityFilters<TestEntity>("test", (Expression<Func<TestEntity, string>>?)null);
|
||||
// Force evaluation to trigger the exception
|
||||
filters.First().Compile();
|
||||
});
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(LongSearchStringTestCases))]
|
||||
public void AddEntityFilters_WithVeryLongSearchString_HandlesGracefully(int stringLength)
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var longSearch = new string('a', stringLength);
|
||||
|
||||
// Act & Assert
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var result = filters.AddEntityFilters(longSearch, e => e.Name);
|
||||
var compiled = result.First().Compile();
|
||||
var testEntity = new TestEntity { Name = longSearch };
|
||||
var compiledResult = compiled(testEntity);
|
||||
Assert.That(compiledResult, Is.True);
|
||||
});
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> LongSearchStringTestCases()
|
||||
{
|
||||
yield return new TestCaseData(1000)
|
||||
.SetName("{m}(stringLength: {0})");
|
||||
yield return new TestCaseData(10000)
|
||||
.SetName("{m}(stringLength: {0})");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(SpecialCharacterTestCases))]
|
||||
public void CreateEntityFilters_WithSpecialCharacters_HandlesCorrectly(string specialChars)
|
||||
{
|
||||
// Arrange
|
||||
var testEntity = new TestEntity { Name = specialChars };
|
||||
|
||||
// Act
|
||||
var filters = specialChars.CreateEntityFilters<TestEntity>(e => e.Name);
|
||||
var compiled = filters.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(compiled(testEntity), Is.True);
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> SpecialCharacterTestCases()
|
||||
{
|
||||
yield return new TestCaseData("@#$%^&*()")
|
||||
.SetName("{m}(specialChars: \"{0}\")");
|
||||
yield return new TestCaseData("_+-=[]{}|;':")
|
||||
.SetName("{m}(specialChars: \"{0}\")");
|
||||
yield return new TestCaseData("\",./<>?")
|
||||
.SetName("{m}(specialChars: \"{0}\")");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddEnumFilter_WithNullPropertyValue_HandlesGracefully()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var testEntity = new TestEntity { NullableField = null };
|
||||
|
||||
// Act
|
||||
var result = filters.AddEnumFilter(new[] { TestEnum.Value1 }, e => e.NullableField);
|
||||
var compiled = result.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(compiled(testEntity), Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CreateEntityFilters_WithEmptyStringInMultipleTerms_FiltersCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var testEntity = new TestEntity { Name = "" };
|
||||
|
||||
// Act
|
||||
var filters = "apple,,banana".CreateEntityFilters<TestEntity>(e => e.Name);
|
||||
var compiled = filters.First().Compile();
|
||||
|
||||
// Assert
|
||||
Assert.That(compiled(testEntity), Is.False, "empty string should not contain apple or banana");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AddSearchFilters_WithNullExpressionBuilder_ThrowsArgumentNullException()
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
|
||||
// Act & Assert
|
||||
Assert.Throws<NullReferenceException>(() => filters.AddSearchFilters("test", null));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Performance and Stress Tests
|
||||
|
||||
[TestCaseSource(nameof(PerformanceTestCases))]
|
||||
public void CreateEntityFilters_WithManyTerms_PerformsReasonably(int termCount)
|
||||
{
|
||||
// Arrange
|
||||
var manyTerms = string.Join(",", Enumerable.Range(1, termCount).Select(i => $"term{i}"));
|
||||
|
||||
// Act & Assert
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var filters = manyTerms.CreateEntityFilters<TestEntity>(e => e.Name, e => e.Description);
|
||||
Assert.That(filters.Count, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> PerformanceTestCases()
|
||||
{
|
||||
yield return new TestCaseData(50)
|
||||
.SetName("{m}(termCount: {0})");
|
||||
yield return new TestCaseData(100)
|
||||
.SetName("{m}(termCount: {0})");
|
||||
}
|
||||
|
||||
[TestCaseSource(nameof(EnumPerformanceTestCases))]
|
||||
public void AddEnumFilter_WithManyEnumValues_PerformsReasonably(int enumCount)
|
||||
{
|
||||
// Arrange
|
||||
var filters = new List<Expression<Func<TestEntity, bool>>>();
|
||||
var manyEnums = Enumerable.Repeat(TestEnum.Value1, enumCount).ToArray();
|
||||
|
||||
// Act & Assert
|
||||
Assert.DoesNotThrow(() =>
|
||||
{
|
||||
var result = filters.AddEnumFilter(manyEnums, e => e.Category);
|
||||
Assert.That(result.Count, Is.EqualTo(1));
|
||||
});
|
||||
}
|
||||
|
||||
private static IEnumerable<TestCaseData> EnumPerformanceTestCases()
|
||||
{
|
||||
yield return new TestCaseData(500)
|
||||
.SetName("{m}(enumCount: {0})");
|
||||
yield return new TestCaseData(1000)
|
||||
.SetName("{m}(enumCount: {0})");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Integration Tests
|
||||
|
||||
[Test]
|
||||
public void CreateEntityFilters_ComplexScenario_WorksEndToEnd()
|
||||
{
|
||||
// Arrange
|
||||
var testData = new[]
|
||||
{
|
||||
new TestEntity { Name = "Apple Product", Code = "AP001", Category = "VALUE1", Description = "Fresh apple" },
|
||||
new TestEntity { Name = "Banana Product", Code = "BP002", Category = "VALUE2", Description = "Yellow banana" },
|
||||
new TestEntity { Name = "Cherry Product", Code = "CP003", Category = "VALUE3", Description = "Red cherry" },
|
||||
new TestEntity { Name = "Apple Banana Mix", Code = "AB004", Category = "VALUE1", Description = "Mixed fruit" }
|
||||
};
|
||||
|
||||
var enumFilters = new[]
|
||||
{
|
||||
new EnumFilter<TestEntity>(new[] { TestEnum.Value1, TestEnum.Value2 }, e => e.Category)
|
||||
};
|
||||
|
||||
// Act - Search for "apple" with category filter
|
||||
var filters = "apple".CreateEntityFilters(enumFilters, e => e.Name, e => e.Description);
|
||||
|
||||
var query = testData.AsQueryable();
|
||||
foreach (var filter in filters)
|
||||
{
|
||||
query = query.Where(filter);
|
||||
}
|
||||
var results = query.ToList();
|
||||
|
||||
// Assert
|
||||
Assert.That(results, Has.Count.EqualTo(2), "should match Apple Product and Apple Banana Mix");
|
||||
Assert.That(results.All(r => r.Name.ToLower().Contains("apple") || r.Description.ToLower().Contains("apple")), Is.True,
|
||||
"all results should contain 'apple' in name or description");
|
||||
Assert.That(results.All(r => r.Category is "VALUE1" or "VALUE2"), Is.True,
|
||||
"all results should have matching categories");
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user