refactor(data-access): update ISearchQueryBuilder to use SearchId only
- Change interface methods to accept int searchId instead of SearchModel - Update SqlKataSearchQueryBuilder to generate SQL using extraction functions - SQL now calls dbo.fn_GetSearchWorkOrders(@SearchId) etc instead of TVPs - Update SearchProcessor to pass model.Id to query builder - Update tests for new method signatures
This commit is contained in:
+106
-158
@@ -1,9 +1,5 @@
|
||||
using JdeScoping.DataAccess.FilterHandlers;
|
||||
using JdeScoping.DataAccess.Interfaces;
|
||||
using JdeScoping.DataAccess.Models;
|
||||
using JdeScoping.DataAccess.Models.FilterEntries;
|
||||
using JdeScoping.DataAccess.QueryBuilders;
|
||||
using NSubstitute;
|
||||
using Shouldly;
|
||||
using SqlKata.Compilers;
|
||||
using Xunit;
|
||||
@@ -18,221 +14,173 @@ public sealed class SqlKataSearchQueryBuilderTests
|
||||
private readonly SqlServerCompiler _compiler = new();
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_WithEmptyFilters_ProducesMinimalQuery()
|
||||
public void BuildSearchQuery_WithSearchId_ProducesValidQuery()
|
||||
{
|
||||
// Arrange
|
||||
var handlers = Array.Empty<IFilterHandler>();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
var searchId = 123;
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
var result = builder.BuildSearchQuery(searchId);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Sql.ShouldNotBeNullOrEmpty();
|
||||
result.TempTableSetupSql.ShouldNotBeEmpty();
|
||||
result.Parameters.ShouldContainKey("SearchId");
|
||||
result.Parameters["SearchId"].ShouldBe(searchId);
|
||||
}
|
||||
|
||||
// Should contain temp table creation
|
||||
[Fact]
|
||||
public void BuildSearchQuery_ContainsTempTableCreation()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
setupSql.ShouldContain("#Temp_WO");
|
||||
setupSql.ShouldContain("CREATE TABLE");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_WithEmptyFilters_ResultSqlContainsSelect()
|
||||
public void BuildSearchQuery_ContainsValidationCall()
|
||||
{
|
||||
// Arrange
|
||||
var handlers = Array.Empty<IFilterHandler>();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
setupSql.ShouldContain("usp_ValidateSearchCriteria");
|
||||
setupSql.ShouldContain("@SearchId");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_UsesExtractionFunctions()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
|
||||
// Should use extraction functions instead of TVPs
|
||||
setupSql.ShouldContain("fn_GetSearchWorkOrders(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchItemNumbers(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchProfitCenters(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchWorkCenters(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchOperatorIDs(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchComponentLots(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchPartOperations(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchMinimumDt(@SearchId)");
|
||||
setupSql.ShouldContain("fn_GetSearchMaximumDt(@SearchId)");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_CreatesFilterTempTables()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
|
||||
// Should create filter temp tables
|
||||
setupSql.ShouldContain("#P_WorkOrders");
|
||||
setupSql.ShouldContain("#P_ItemNumbers");
|
||||
setupSql.ShouldContain("#P_WorkCenters");
|
||||
setupSql.ShouldContain("#P_OperatorIDs");
|
||||
setupSql.ShouldContain("#P_ComponentLots");
|
||||
setupSql.ShouldContain("#P_PartOperations");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_ResultSqlContainsSelect()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
result.Sql.ShouldContain("SELECT");
|
||||
result.Sql.ShouldContain("WorkOrderNumber");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_WithSingleFilter_ProducesCorrectStructure()
|
||||
public void BuildSearchQuery_ContainsStepFlaggingQuery()
|
||||
{
|
||||
// Arrange
|
||||
var workOrderHandler = new WorkOrderFilterHandler();
|
||||
var handlers = new IFilterHandler[] { workOrderHandler };
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel
|
||||
{
|
||||
WorkOrderFilter =
|
||||
[
|
||||
new WorkOrderFilterEntry { WorkOrderNumber = 12345 }
|
||||
]
|
||||
};
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
var result = builder.BuildSearchQuery(1);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.TempTableSetupSql.ShouldNotBeEmpty();
|
||||
result.Parameters.ShouldContainKey("p_WorkOrderFilter");
|
||||
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
// Should have temp table creation and work order merge
|
||||
setupSql.ShouldContain("#Temp_WO");
|
||||
setupSql.ShouldContain("MERGE");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_WithMultipleFilters_CombinesCorrectly()
|
||||
{
|
||||
// Arrange
|
||||
var workOrderHandler = new WorkOrderFilterHandler();
|
||||
var itemNumberHandler = new ItemNumberFilterHandler();
|
||||
var handlers = new IFilterHandler[] { workOrderHandler, itemNumberHandler };
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel
|
||||
{
|
||||
WorkOrderFilter =
|
||||
[
|
||||
new WorkOrderFilterEntry { WorkOrderNumber = 12345 }
|
||||
],
|
||||
ItemNumberFilter =
|
||||
[
|
||||
new ItemNumberFilterEntry { ItemNumber = "ABC123" }
|
||||
]
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Parameters.ShouldContainKey("p_WorkOrderFilter");
|
||||
result.Parameters.ShouldContainKey("p_ItemNumberFilter");
|
||||
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
setupSql.ShouldContain("#Temp_WO");
|
||||
setupSql.ShouldContain("#P_ItemNumbers");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_HandlersAreAppliedInPriorityOrder()
|
||||
{
|
||||
// Arrange
|
||||
var lowPriorityHandler = Substitute.For<IFilterHandler>();
|
||||
lowPriorityHandler.Priority.Returns(100);
|
||||
lowPriorityHandler.IsEnabled(Arg.Any<SearchModel>()).Returns(true);
|
||||
lowPriorityHandler.Apply(Arg.Any<SearchModel>(), Arg.Any<SqlServerCompiler>())
|
||||
.Returns(new FilterResult(["-- LOW PRIORITY SQL"], new Dictionary<string, object>()));
|
||||
|
||||
var highPriorityHandler = Substitute.For<IFilterHandler>();
|
||||
highPriorityHandler.Priority.Returns(1);
|
||||
highPriorityHandler.IsEnabled(Arg.Any<SearchModel>()).Returns(true);
|
||||
highPriorityHandler.Apply(Arg.Any<SearchModel>(), Arg.Any<SqlServerCompiler>())
|
||||
.Returns(new FilterResult(["-- HIGH PRIORITY SQL"], new Dictionary<string, object>()));
|
||||
|
||||
// Pass handlers in reverse priority order to verify sorting
|
||||
var handlers = new[] { lowPriorityHandler, highPriorityHandler };
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
var highIndex = setupSql.IndexOf("-- HIGH PRIORITY SQL", StringComparison.Ordinal);
|
||||
var lowIndex = setupSql.IndexOf("-- LOW PRIORITY SQL", StringComparison.Ordinal);
|
||||
|
||||
highIndex.ShouldBeGreaterThan(-1);
|
||||
lowIndex.ShouldBeGreaterThan(-1);
|
||||
highIndex.ShouldBeLessThan(lowIndex);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_DisabledHandlersAreSkipped()
|
||||
{
|
||||
// Arrange
|
||||
var enabledHandler = Substitute.For<IFilterHandler>();
|
||||
enabledHandler.Priority.Returns(1);
|
||||
enabledHandler.IsEnabled(Arg.Any<SearchModel>()).Returns(true);
|
||||
enabledHandler.Apply(Arg.Any<SearchModel>(), Arg.Any<SqlServerCompiler>())
|
||||
.Returns(new FilterResult(["-- ENABLED"], new Dictionary<string, object>()));
|
||||
|
||||
var disabledHandler = Substitute.For<IFilterHandler>();
|
||||
disabledHandler.Priority.Returns(2);
|
||||
disabledHandler.IsEnabled(Arg.Any<SearchModel>()).Returns(false);
|
||||
|
||||
var handlers = new[] { enabledHandler, disabledHandler };
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
|
||||
// Assert
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
setupSql.ShouldContain("-- ENABLED");
|
||||
|
||||
// Apply should never be called on disabled handler
|
||||
disabledHandler.DidNotReceive().Apply(Arg.Any<SearchModel>(), Arg.Any<SqlServerCompiler>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_WithTimespanFilter_IncludesStepFlagging()
|
||||
{
|
||||
// Arrange
|
||||
var handlers = Array.Empty<IFilterHandler>();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel
|
||||
{
|
||||
MinimumDt = DateTime.Now.AddDays(-30),
|
||||
MaximumDt = DateTime.Now
|
||||
};
|
||||
|
||||
// Act
|
||||
var result = builder.BuildSearchQuery(model);
|
||||
|
||||
// Assert
|
||||
// When ShouldSearchSteps returns true, step flagging query is added
|
||||
var setupSql = string.Join("\n", result.TempTableSetupSql);
|
||||
setupSql.ShouldContain("LU_WO");
|
||||
setupSql.ShouldContain("Flagged");
|
||||
setupSql.ShouldContain("MERGE");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildMisQuery_ReturnsValidResult()
|
||||
{
|
||||
// Arrange
|
||||
var handlers = Array.Empty<IFilterHandler>();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildMisQuery(model);
|
||||
var result = builder.BuildMisQuery(1);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Sql.ShouldNotBeNullOrEmpty();
|
||||
result.Sql.ShouldContain("#TempMisData");
|
||||
result.Parameters.ShouldContainKey("SearchId");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildMisNonMatchQuery_ReturnsValidResult()
|
||||
{
|
||||
// Arrange
|
||||
var handlers = Array.Empty<IFilterHandler>();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler, handlers);
|
||||
var model = new SearchModel();
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result = builder.BuildMisNonMatchQuery(model);
|
||||
var result = builder.BuildMisNonMatchQuery(1);
|
||||
|
||||
// Assert
|
||||
result.ShouldNotBeNull();
|
||||
result.Sql.ShouldNotBeNullOrEmpty();
|
||||
result.Sql.ShouldContain("WasJobStepAdded");
|
||||
result.Sql.ShouldContain("MatchedJobStepNumber");
|
||||
result.Parameters.ShouldContainKey("SearchId");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BuildSearchQuery_DifferentSearchIds_ProduceDifferentParameters()
|
||||
{
|
||||
// Arrange
|
||||
var builder = new SqlKataSearchQueryBuilder(_compiler);
|
||||
|
||||
// Act
|
||||
var result1 = builder.BuildSearchQuery(100);
|
||||
var result2 = builder.BuildSearchQuery(200);
|
||||
|
||||
// Assert
|
||||
result1.Parameters["SearchId"].ShouldBe(100);
|
||||
result2.Parameters["SearchId"].ShouldBe(200);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user