163 lines
6.5 KiB
C#
163 lines
6.5 KiB
C#
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||
using Newtonsoft.Json;
|
||
using YLErp.DBModels;
|
||
using YLErp.Helpers;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
|
||
namespace YLErp.Helpers.Tests
|
||
{
|
||
/// <summary>
|
||
/// 需求①:交易提交进入审批流程时的「起点触发条件跳过」测试。
|
||
/// <para>场景:交易一进来,若节点1、2配置的触发条件均不满足,应直接从节点3开始审核;
|
||
/// 若全部节点都不满足,则直接审批通过(无需任何审核)。</para>
|
||
/// </summary>
|
||
[TestClass]
|
||
public class TriggerNodeSkipTests
|
||
{
|
||
/// <summary>构造一个审批节点:order + 可选的触发条件JSON。</summary>
|
||
private static approvalprocess Node(int order, string triggerCondition = null)
|
||
{
|
||
return new approvalprocess
|
||
{
|
||
order = order,
|
||
node = 0,
|
||
triggerCondition = triggerCondition
|
||
};
|
||
}
|
||
|
||
/// <summary>构造"期初名义本金 > 阈值"的触发条件JSON。</summary>
|
||
private static string InitialNotionalGt(double threshold)
|
||
{
|
||
return JsonConvert.SerializeObject(new ConditionExpressionConfig
|
||
{
|
||
tokens = new List<ConditionToken>
|
||
{
|
||
new ConditionToken
|
||
{
|
||
type = "condition",
|
||
condition = new ConditionItem { field = "initialNotional", op = "gt", value = threshold }
|
||
}
|
||
}
|
||
});
|
||
}
|
||
|
||
private static ConditionContext Ctx(double initialNotional)
|
||
{
|
||
return new ConditionContext
|
||
{
|
||
Trade = new trade { OriginalStockEqvNotional = initialNotional }
|
||
};
|
||
}
|
||
|
||
[TestMethod]
|
||
public void StartNode_NoTrigger_ReturnsStartDirectly()
|
||
{
|
||
// 节点1无触发条件 → 直接返回节点1
|
||
var nodes = new List<approvalprocess> { Node(1), Node(2), Node(3) };
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(500));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(1, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void StartNode_TriggerSatisfied_ReturnsStart()
|
||
{
|
||
// 节点1触发条件">100",交易期初200满足 → 返回节点1
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(100)),
|
||
Node(2),
|
||
Node(3)
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(200));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(1, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void StartNode_TriggerNotSatisfied_SkipsToNextSatisfied()
|
||
{
|
||
// 节点1触发">100",节点2触发">500",交易期初200
|
||
// → 节点1不满足(200>100满足? 满足)... 重新设计:节点1触发">1000",200不满足;节点2无触发 → 返回节点2
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(1000)), // 200 不满足 >1000
|
||
Node(2), // 无触发条件
|
||
Node(3)
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(200));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(2, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void AllStartNodesNotSatisfied_ReturnsNull_DirectlyApproved()
|
||
{
|
||
// 节点1、2、3都有触发条件,交易都不满足 → 返回null(表示直接审批通过)
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(1000)), // 200 不满足
|
||
Node(2, InitialNotionalGt(5000)), // 200 不满足
|
||
Node(3, InitialNotionalGt(10000)) // 200 不满足
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(200));
|
||
Assert.IsNull(result);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void SkipMultipleNodes_LandsOnThird()
|
||
{
|
||
// 节点1、2都不满足,节点3无触发 → 返回节点3
|
||
// 模拟"A默认审核、B>100W再审核、C>=500W再审核"中,小额交易跳过B、C直达... 实际应停在满足的节点
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(1000000)), // 50W 不满足
|
||
Node(2, InitialNotionalGt(2000000)), // 50W 不满足
|
||
Node(3) // 无触发条件(兜底审核)
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(500000));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(3, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void SatisfiedAtSecondNode_StopsThere()
|
||
{
|
||
// 节点1触发">1000"不满足,节点2触发">100"满足 → 返回节点2(不会继续到节点3)
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(1000)), // 200 不满足
|
||
Node(2, InitialNotionalGt(100)), // 200 满足
|
||
Node(3, InitialNotionalGt(50)) // 不会走到这
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[0], Ctx(200));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(2, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void StartFromMiddleNode_Works()
|
||
{
|
||
// 起点不是节点1(如分支调整后从节点2开始),从节点2起判断
|
||
var nodes = new List<approvalprocess>
|
||
{
|
||
Node(1, InitialNotionalGt(1000)),
|
||
Node(2, InitialNotionalGt(1000)), // 200 不满足
|
||
Node(3) // 无触发
|
||
};
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, nodes[1], Ctx(200));
|
||
Assert.IsNotNull(result);
|
||
Assert.AreEqual(3, result.order);
|
||
}
|
||
|
||
[TestMethod]
|
||
public void NullStart_ReturnsNull()
|
||
{
|
||
var nodes = new List<approvalprocess> { Node(1) };
|
||
var result = ConditionEvaluator.FindFirstTriggeredNode(nodes, null, Ctx(500));
|
||
Assert.IsNull(result);
|
||
}
|
||
}
|
||
}
|