Files

122 lines
4.6 KiB
C#

using FluentAssertions;
using NUnit.Framework;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Dtos;
using Strata.ContinuousImprovement.Biz.StrategicOpportunities.Queries;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using VerifyNUnit;
namespace Strata.ContinuousImprovement.Biz.Test.Unit.StrategicOpportunities
{
[ExcludeFromCodeCoverage]
[TestFixture, Category("Unit"), Category("CCI")]
internal class StrategicOpportunityGLUpdateQueryBuilderTest : StrategicOpportunityTestsBase
{
private StrategicOpportunityGLUpdateQueryBuilder _queryBuilder;
private static readonly IEnumerable<string> Rows =
[
StrategicOpportunityDetailDimension.System.Value,
StrategicOpportunityDetailDimension.Entity.Value,
StrategicOpportunityDetailDimension.Department.Value,
StrategicOpportunityDetailDimension.GLAccount.Value,
];
[SetUp]
public void Setup()
{
_queryBuilder = new StrategicOpportunityGLUpdateQueryBuilder();
}
[Test]
public async Task BuildUpdateQuery_WithFourAdjustments_GeneratesCorrectSql()
{
// Arrange
const long opportunityId = 42;
var adjustments = new List<StrategicOpportunityGLAdjustment>
{
// Root-level adjustment
new() { NodeKey = "root", GoalAmountValue = 50000 },
// Entity-level adjustment
new() { NodeKey = "101", GoalAmountValue = 20000 },
// Entity + Department level: null (passthrough)
new() { NodeKey = "101|201", GoalAmountValue = null },
// Entity + Department + GLAccount (lowest level)
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000 },
};
// Act
var result = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows).Query;
// Assert
await Verifier.Verify(result, VerifySettings);
}
[Test]
public void BuildUpdateQuery_WithAccountTypeOnSecondAdjustment_IncludesAccountTypeFilterInSubquery()
{
// AccountType filters are embedded in the inner SELECT WHERE for non-first adjustments
// (the first adjustment uses an outer WHERE clause that filters by hierarchy only)
const long opportunityId = 42;
var adjustments = new List<StrategicOpportunityGLAdjustment>
{
new() { NodeKey = "root", GoalAmountValue = 50000 },
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000, AccountType = 0 },
};
var (query, parameters) = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows);
query.Should().Contain("AccountType = :AccountType_1");
parameters.Should().ContainKey("AccountType_1");
parameters["AccountType_1"].Should().Be((byte)0);
}
[Test]
public void BuildUpdateQuery_WithNullAccountType_OmitsAccountTypeFilter()
{
const long opportunityId = 42;
var adjustments = new List<StrategicOpportunityGLAdjustment>
{
new() { NodeKey = "root", GoalAmountValue = 50000 },
new() { NodeKey = "101|201|3001", GoalAmountValue = 15000, AccountType = null },
};
var (query, parameters) = _queryBuilder.BuildUpdateQuery(adjustments, opportunityId, Rows);
query.Should().NotContain("AccountType = :");
parameters.Should().NotContainKey("AccountType_1");
}
[Test]
public void BuildUpdateQuery_WithInvalidOpportunityId_ThrowsArgumentException()
{
var adjustments = new List<StrategicOpportunityGLAdjustment>
{
new() { NodeKey = "101", GoalAmountValue = 1000 },
};
var act = () => _queryBuilder.BuildUpdateQuery(adjustments, opportunityId: 0, Rows);
act.Should().Throw<ArgumentException>().WithParameterName("opportunityId");
}
[Test]
public void BuildUpdateQuery_WithEmptyNodeKey_ThrowsArgumentException()
{
var adjustments = new List<StrategicOpportunityGLAdjustment>
{
new() { NodeKey = "", GoalAmountValue = 1000 },
};
var act = () => _queryBuilder.BuildUpdateQuery(adjustments, opportunityId: 42, Rows);
act.Should().Throw<ArgumentException>().WithParameterName("nodeKey");
}
}
}