SetFixedLegRealizedPnl 替换4处复制粘贴(清理L1296双分号); NormalizeInterestSignForReport 替换16行内联报表分支; CalculateWeightedMarginInterest private->public static。均为纯静态无实例依赖,配套无库单测锁定行为。
151 lines
6.7 KiB
C#
151 lines
6.7 KiB
C#
using YLErp.DBModels;
|
||
using YLErp.DBModels.Enums;
|
||
|
||
namespace YLErp.Modules.SwapModule
|
||
{
|
||
/// <summary>
|
||
/// SwapEodPositionService.CalculateWeightedMarginInterest 的回归测试。
|
||
/// -----------------------------------------------------------------
|
||
/// 守卫提交 a0f8dc9d "refactor(SwapModule): 简化预付金利息计算逻辑"。
|
||
///
|
||
/// 旧实现:按 InterestPrincipalFix 绝对值加权平均利率 × 总本金,对方向不敏感,
|
||
/// 当收取/支付双腿并存时会把支付方向的利息错误计为收益。
|
||
/// 新实现:InterestIncomeSum 已是各腿利息金额,按方向(收取=+1,支付=-1)轧差求和。
|
||
///
|
||
/// 抽为 public static 纯函数以支持无库单测。本测试锁定方向轧差契约。
|
||
/// </summary>
|
||
[TestClass]
|
||
public class SwapWeightedMarginInterestTest
|
||
{
|
||
// ================================================================
|
||
// 场景1:空集合 → 0(Sum 空序列默认值)
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 空集合_返回0()
|
||
{
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(
|
||
Enumerable.Empty<eod_swap_position>());
|
||
|
||
Assert.AreEqual(0m, result, 0.0001m, "空集合轧差应为 0");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景2:单腿收取 → InterestIncomeSum 原值
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 单腿收取_利息原值计入()
|
||
{
|
||
var margins = new[]
|
||
{
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.收取, interestIncomeSum: 1000m)
|
||
};
|
||
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(margins);
|
||
|
||
Assert.AreEqual(1000m, result, 0.0001m, "单腿收取:+1000");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景3:单腿支付 → InterestIncomeSum 取负
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 单腿支付_利息取负计入()
|
||
{
|
||
var margins = new[]
|
||
{
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.支付, interestIncomeSum: 1000m)
|
||
};
|
||
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(margins);
|
||
|
||
Assert.AreEqual(-1000m, result, 0.0001m, "单腿支付:-1000");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景4:双腿轧差(收取 1000 + 支付 600 → 400)
|
||
// 旧 bug:按本金加权会忽略方向,结果不是 400
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 双腿轧差_收取大于支付_净额为正()
|
||
{
|
||
var margins = new[]
|
||
{
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.收取, interestIncomeSum: 1000m),
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.支付, interestIncomeSum: 600m)
|
||
};
|
||
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(margins);
|
||
|
||
Assert.AreEqual(400m, result, 0.0001m, "双腿轧差:1000 - 600 = 400");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景5:本金为零但 InterestIncomeSum 非零 —— 回归旧 bug 关键场景
|
||
// 旧实现:totalWeight=0 → 返回 0,丢失利息
|
||
// 新实现:不看本金,按方向轧差 InterestIncomeSum
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 本金为零_利息仍按方向轧差_不丢失()
|
||
{
|
||
var margins = new[]
|
||
{
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.收取,
|
||
interestIncomeSum: 500m, interestPrincipalFix: 0m),
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.支付,
|
||
interestIncomeSum: 200m, interestPrincipalFix: 0m)
|
||
};
|
||
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(margins);
|
||
|
||
Assert.AreEqual(300m, result, 0.0001m,
|
||
"本金为零时旧实现返回 0 丢失利息,新实现应按方向轧差 = 500 - 200 = 300");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景6:多腿混合方向 —— 收取 100+200,支付 50+80 → 170
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void 多腿混合方向_正确轧差()
|
||
{
|
||
var margins = new[]
|
||
{
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.收取, interestIncomeSum: 100m),
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.支付, interestIncomeSum: 50m),
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.收取, interestIncomeSum: 200m),
|
||
NewMargin(interestDirection: (int)SwapDirectionEnum.支付, interestIncomeSum: 80m)
|
||
};
|
||
|
||
var result = SwapEodPositionService.CalculateWeightedMarginInterest(margins);
|
||
|
||
Assert.AreEqual(170m, result, 0.0001m, "多腿轧差:(100+200) - (50+80) = 170");
|
||
}
|
||
|
||
// ================================================================
|
||
// 场景7:null 入参防御 —— Sum 对 null 抛 ArgumentNullException
|
||
// 仅作行为锚点:若未来改为 null 安全,此处需同步更新
|
||
// ================================================================
|
||
[TestMethod]
|
||
public void Null入参_抛ArgumentNullException()
|
||
{
|
||
Assert.ThrowsException<ArgumentNullException>(() =>
|
||
SwapEodPositionService.CalculateWeightedMarginInterest(null!));
|
||
}
|
||
|
||
// ================================================================
|
||
// Helper:构造 eod_swap_position(只设置参与计算的 2 个字段 + 可选本金)
|
||
// ================================================================
|
||
private static eod_swap_position NewMargin(
|
||
int interestDirection,
|
||
decimal interestIncomeSum,
|
||
decimal interestPrincipalFix = 0m)
|
||
{
|
||
return new eod_swap_position
|
||
{
|
||
InterestDirection = interestDirection,
|
||
InterestIncomeSum = interestIncomeSum,
|
||
InterestPrincipalFix = interestPrincipalFix
|
||
};
|
||
}
|
||
}
|
||
}
|