Files
jdescopingtool/NEW/tests/JdeScoping.Database.Tests/Functions/SimpleTableFunctionTests.cs
T
Joseph Doherty 1e23616638 refactor(tests): migrate Database.Tests from FluentAssertions to Shouldly
Replace FluentAssertions with Shouldly across all 6 test files (94 tests).
Add ShouldlyExtensions for BeCloseTo and BeEquivalentTo patterns.
2026-01-29 14:40:18 -05:00

619 lines
20 KiB
C#

using Dapper;
using JdeScoping.Core.Models.Search;
using JdeScoping.Database.Tests.Infrastructure;
using Shouldly;
namespace JdeScoping.Database.Tests.Functions;
/// <summary>
/// Tests for simple table extraction functions that return single-column result sets.
/// These inline TVFs extract arrays from Search.Criteria JSON.
/// </summary>
[Collection("DatabaseTests")]
public class SimpleTableFunctionTests : DatabaseTestBase
{
#region fn_GetSearchWorkOrders Tests
[Fact]
public async Task fn_GetSearchWorkOrders_ValidArray_ReturnsAllValues()
{
// Arrange
var criteria = new SearchCriteria { WorkOrderNumbers = [12345, 67890, 11111] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo([12345L, 67890L, 11111L]);
}
[Fact]
public async Task fn_GetSearchWorkOrders_EmptyArray_ReturnsEmpty()
{
// Arrange
var criteria = new SearchCriteria { WorkOrderNumbers = [] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkOrders_MissingProperty_ReturnsEmpty()
{
// Arrange - criteria without WorkOrderNumbers property set (uses default empty list)
var searchId = await InsertTestSearchWithRawCriteriaAsync("{\"MinimumDt\":null}");
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkOrders_SearchNotFound_ReturnsEmpty()
{
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = 99999 });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkOrders_NullCriteria_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync(null);
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkOrders_InvalidJson_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync("not valid json");
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkOrders_BadTypeValues_FilteredOut()
{
// Arrange - include null values that should be filtered out
// Note: OPENJSON...WITH BIGINT will filter out NULLs via WHERE clause,
// but will throw conversion error on incompatible types like strings.
// This test verifies NULL values are properly filtered.
var searchId = await InsertTestSearchWithRawCriteriaAsync(
"{\"WorkOrderNumbers\":[12345, null, 67890]}");
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert - null values should be filtered out
results.ShouldBeEquivalentTo([12345L, 67890L]);
}
#endregion
#region fn_GetSearchItemNumbers Tests
[Fact]
public async Task fn_GetSearchItemNumbers_ValidArray_ReturnsAllValues()
{
// Arrange
var criteria = new SearchCriteria { ItemNumbers = ["ITEM001", "ITEM002", "ITEM003"] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo(["ITEM001", "ITEM002", "ITEM003"]);
}
[Fact]
public async Task fn_GetSearchItemNumbers_EmptyArray_ReturnsEmpty()
{
// Arrange
var criteria = new SearchCriteria { ItemNumbers = [] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchItemNumbers_MissingProperty_ReturnsEmpty()
{
// Arrange - criteria without ItemNumbers property
var searchId = await InsertTestSearchWithRawCriteriaAsync("{\"MinimumDt\":null}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchItemNumbers_SearchNotFound_ReturnsEmpty()
{
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = 99999 });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchItemNumbers_NullCriteria_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync(null);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchItemNumbers_InvalidJson_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync("not valid json");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchItemNumbers_BadTypeValues_FilteredOut()
{
// Arrange - include null values that should be filtered out
var searchId = await InsertTestSearchWithRawCriteriaAsync(
"{\"ItemNumbers\":[\"ITEM001\", null, \"ITEM002\"]}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert - null values should be filtered out
results.ShouldBeEquivalentTo(["ITEM001", "ITEM002"]);
}
[Fact]
public async Task fn_GetSearchItemNumbers_SpecialCharacters_ReturnsCorrectly()
{
// Arrange
var criteria = new SearchCriteria { ItemNumbers = ["ITEM-001", "ITEM_002", "ITEM.003"] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo(["ITEM-001", "ITEM_002", "ITEM.003"]);
}
#endregion
#region fn_GetSearchProfitCenters Tests
[Fact]
public async Task fn_GetSearchProfitCenters_ValidArray_ReturnsAllValues()
{
// Arrange
var criteria = new SearchCriteria { ProfitCenters = ["PC01", "PC02", "PC03"] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo(["PC01", "PC02", "PC03"]);
}
[Fact]
public async Task fn_GetSearchProfitCenters_EmptyArray_ReturnsEmpty()
{
// Arrange
var criteria = new SearchCriteria { ProfitCenters = [] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchProfitCenters_MissingProperty_ReturnsEmpty()
{
// Arrange - criteria without ProfitCenters property
var searchId = await InsertTestSearchWithRawCriteriaAsync("{\"MinimumDt\":null}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchProfitCenters_SearchNotFound_ReturnsEmpty()
{
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = 99999 });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchProfitCenters_NullCriteria_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync(null);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchProfitCenters_InvalidJson_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync("not valid json");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchProfitCenters_BadTypeValues_FilteredOut()
{
// Arrange - include null values that should be filtered out
var searchId = await InsertTestSearchWithRawCriteriaAsync(
"{\"ProfitCenters\":[\"PC01\", null, \"PC02\"]}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchProfitCenters(@SearchId)",
new { SearchId = searchId });
// Assert - null values should be filtered out
results.ShouldBeEquivalentTo(["PC01", "PC02"]);
}
#endregion
#region fn_GetSearchWorkCenters Tests
[Fact]
public async Task fn_GetSearchWorkCenters_ValidArray_ReturnsAllValues()
{
// Arrange
var criteria = new SearchCriteria { WorkCenters = ["WC001", "WC002", "WC003"] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo(["WC001", "WC002", "WC003"]);
}
[Fact]
public async Task fn_GetSearchWorkCenters_EmptyArray_ReturnsEmpty()
{
// Arrange
var criteria = new SearchCriteria { WorkCenters = [] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkCenters_MissingProperty_ReturnsEmpty()
{
// Arrange - criteria without WorkCenters property
var searchId = await InsertTestSearchWithRawCriteriaAsync("{\"MinimumDt\":null}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkCenters_SearchNotFound_ReturnsEmpty()
{
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = 99999 });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkCenters_NullCriteria_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync(null);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkCenters_InvalidJson_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync("not valid json");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchWorkCenters_BadTypeValues_FilteredOut()
{
// Arrange - include null values that should be filtered out
var searchId = await InsertTestSearchWithRawCriteriaAsync(
"{\"WorkCenters\":[\"WC001\", null, \"WC002\"]}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT Code FROM dbo.fn_GetSearchWorkCenters(@SearchId)",
new { SearchId = searchId });
// Assert - null values should be filtered out
results.ShouldBeEquivalentTo(["WC001", "WC002"]);
}
#endregion
#region fn_GetSearchOperatorIDs Tests
[Fact]
public async Task fn_GetSearchOperatorIDs_ValidArray_ReturnsAllValues()
{
// Arrange
var criteria = new SearchCriteria { OperatorIDs = ["OP001", "OP002", "OP003"] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEquivalentTo(["OP001", "OP002", "OP003"]);
}
[Fact]
public async Task fn_GetSearchOperatorIDs_EmptyArray_ReturnsEmpty()
{
// Arrange
var criteria = new SearchCriteria { OperatorIDs = [] };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchOperatorIDs_MissingProperty_ReturnsEmpty()
{
// Arrange - criteria without OperatorIDs property
var searchId = await InsertTestSearchWithRawCriteriaAsync("{\"MinimumDt\":null}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchOperatorIDs_SearchNotFound_ReturnsEmpty()
{
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = 99999 });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchOperatorIDs_NullCriteria_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync(null);
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchOperatorIDs_InvalidJson_ReturnsEmpty()
{
// Arrange
var searchId = await InsertTestSearchWithRawCriteriaAsync("not valid json");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert
results.ShouldBeEmpty();
}
[Fact]
public async Task fn_GetSearchOperatorIDs_BadTypeValues_FilteredOut()
{
// Arrange - include null values that should be filtered out
var searchId = await InsertTestSearchWithRawCriteriaAsync(
"{\"OperatorIDs\":[\"OP001\", null, \"OP002\"]}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT OperatorID FROM dbo.fn_GetSearchOperatorIDs(@SearchId)",
new { SearchId = searchId });
// Assert - null values should be filtered out
results.ShouldBeEquivalentTo(["OP001", "OP002"]);
}
#endregion
#region Additional Edge Case Tests
[Fact]
public async Task fn_GetSearchWorkOrders_LargeArray_ReturnsAll()
{
// Arrange
var workOrders = Enumerable.Range(1, 1000).Select(i => (long)i).ToList();
var criteria = new SearchCriteria { WorkOrderNumbers = workOrders };
var searchId = await InsertTestSearchAsync(criteria);
// Act
var results = await Connection.QueryAsync<long>(
"SELECT WorkOrderNumber FROM dbo.fn_GetSearchWorkOrders(@SearchId)",
new { SearchId = searchId });
// Assert
results.Count().ShouldBe(1000);
results.ShouldBeEquivalentTo(workOrders);
}
[Fact]
public async Task fn_GetSearchItemNumbers_LongValues_Truncated()
{
// Arrange - ItemNumber column is VARCHAR(128)
var longValue = new string('A', 200);
var searchId = await InsertTestSearchWithRawCriteriaAsync(
$"{{\"ItemNumbers\":[\"{longValue}\"]}}");
// Act
var results = await Connection.QueryAsync<string>(
"SELECT ItemNumber FROM dbo.fn_GetSearchItemNumbers(@SearchId)",
new { SearchId = searchId });
// Assert - value should be truncated to 128 characters
results.Count().ShouldBe(1);
results.First().Length.ShouldBe(128);
}
#endregion
}