From 775d7a58810f6a794fbe674f827ced8434418a6b Mon Sep 17 00:00:00 2001 From: hjhan Date: Wed, 1 Jul 2026 18:21:51 +0800 Subject: [PATCH] =?UTF-8?q?test(swap):=20=E9=98=B6=E6=AE=B53=20=E6=89=A9?= =?UTF-8?q?=E5=B1=95DealInterests=E6=B5=8B=E8=AF=95-=E6=99=AE=E9=80=9A?= =?UTF-8?q?=E6=97=A5=E5=BD=92=E6=A1=A3=E5=80=BC+=E5=A4=9A=E6=97=A5?= =?UTF-8?q?=E8=BF=9E=E7=BB=AD=E5=AE=88=E6=81=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 依赖阶段1c的CalcSwapInterests虚方法接缝,现在普通日分支(copy) 也可纯内存测试。 新增/改进的测试: - DI_BRANCH_001: 改为真正的普通日归档值验证(InterestIncomeSum 前日+1天新计=2天利息),不再是Inconclusive - DI_MULTI_001: 连续5天收盘归档,验证InterestIncomeSum线性递增 (每天+1天利息,5天后=5×DailyInterest) StubEodPositionService扩展: - override CalcSwapInterests(用真实SwapDealService算固定利率利息) - ExecuteDealInterests public包装(替代反射调用) - 删除旧的反射辅助方法 验证: 89(T0/T1)+5(DealInterests)=94全通过,无回归。 --- .../SwapModule/DealInterestsScenarioTest.cs | 151 ++++++++++++------ 1 file changed, 103 insertions(+), 48 deletions(-) diff --git a/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs b/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs index d8ff95ce..ae3c5e42 100644 --- a/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs +++ b/UnitTestProject/Modules/SwapModule/DealInterestsScenarioTest.cs @@ -65,6 +65,26 @@ namespace YLErp.Modules.SwapModule return 1.0; // 本币,汇率=1 } + // override CalcSwapInterests:用真实 SwapDealService 算(固定利率不需 mock 浮动利率) + // 生产代码默认实现也是 new SwapDealService(this).GetInterests(...),这里保持一致 + // 但 SwapDealService 内部 TryGetFloatRate 会连库——固定利率(FloatRateUnderlyingCode=null)不会触发 + protected override List CalcSwapInterests( + trade td, trade_extend tradeExtend, + DateTime valueDate, DateTime unwindDate, + List eodPositions, List positions, + decimal posiNotionalValue, decimal posiLongNotionalValue, decimal posiShortNotionalValue, + decimal closePosiNotionalValue, decimal closePrecent, + int eventType, bool tdClose, bool needPrice, + decimal grossPrice, decimal orginPv, + bool add = false, bool settment = true, bool newCalcLast = false, + List closeList = null) + { + return new SwapDealService(this).GetInterests(td, tradeExtend, valueDate, unwindDate, + eodPositions, positions, posiNotionalValue, posiLongNotionalValue, posiShortNotionalValue, + closePosiNotionalValue, closePrecent, eventType, tdClose, needPrice, + grossPrice, orginPv, add, settment, newCalcLast, closeList); + } + // public 包装:让测试能调用 protected 方法 public eod_swap_position ExecuteSaveEodInterestPosition( eod_swap_position eodPayPosition, eod_swap_position newEodPayPosition, @@ -73,6 +93,23 @@ namespace YLErp.Modules.SwapModule SaveEodInterestPosition(eodPayPosition, newEodPayPosition, position, td, valueDate, flowEvents); return PersistedPositions.LastOrDefault(); } + + // public 包装:调用 DealInterests(通过反射,因为参数太多不好包) + public void ExecuteDealInterests( + List interestList, List eodPositions, + DateTime settleDate, trade td, List flowEvents, + decimal posiLongNational, decimal posiShortNational, + decimal closeNational, decimal grossPrice, decimal orginPv) + { + var method = typeof(SwapEodPositionService).GetMethod("DealInterests", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + method.Invoke(this, new object[] + { + interestList, eodPositions, new List(), + settleDate, td, flowEvents, new List(), null, + posiLongNational, posiShortNational, closeNational, grossPrice, orginPv + }); + } } #endregion @@ -265,40 +302,37 @@ namespace YLErp.Modules.SwapModule /// flowEvents 为空,insterval=null,hasSwap=false,hasClose=false /// → 应走 SaveEodInterestPositionCopy(cs:338) /// --------------------------------------------------------------- + /// + /// [DI_BRANCH_001] 普通日收盘归档:InterestIncomeSum 每天递增1天利息 + /// --------------------------------------------------------------- + /// 前日待实现=1天利息,今日收盘(无互换无平仓),应变成2天利息。 + /// 验证 copy 分支(SaveEodInterestPositionCopy)的 InterestIncomeSum 公式。 + /// --------------------------------------------------------------- /// [TestMethod] - public void DI_BRANCH_001_普通日走copy分支() + public void DI_BRANCH_001_普通日归档待实现递增() { var service = new StubEodPositionService(); var td = CreateTrade(); var position = CreateInterestPosition(); - var settleDate = new DateTime(2026, 5, 11); - var preEod = CreatePreEod(settleDate.AddDays(-1), DailyInterest); + var settleDate = new DateTime(2026, 4, 28); // 第2天 + var preEod = CreatePreEod(settleDate.AddDays(-1), DailyInterest); // 前日=1天利息 - // 普通日:flowEvents 为空 - var interestList = new List { position }; - var eodPositions = new List { preEod }; + // 普通日:无互换无平仓,InterestSwapInterval=null(当天非观察日) + position.InterestSwapInterval = null; - // DealInterests 需要 SwapIntervalList(当天不是观察日 → insterval=null) - position.InterestSwapInterval = null; // 清空,确保当天无观察日 + service.ExecuteDealInterests( + new List { position }, + new List { preEod }, + settleDate, td, new List(), + Principal, 0m, 0m, 1m, Principal); - // 调 DealInterests(protected,通过 Stub 类的 protected 访问) - // 注意:DealInterests 调 SaveEodInterestPositionCopy,后者调 GetInterests - // GetInterests 需要 SwapDealService 的接缝。当前 StubEodPositionService 没有 override 它。 - // 这个测试先验证分支不抛异常(分支选择正确),具体值验证待 CalcSwapInterests 接缝 - // TODO: 阶段1c 加 CalcSwapInterests 接缝后补充值断言 - try - { - CallDealInterests(service, interestList, eodPositions, settleDate, td, - new List(), new List(), - Principal, 0m, 0m, 1m, Principal); - // 如果到了这里说明没抛异常(可能 GetInterests 成功了,或者没走到) - Assert.IsTrue(true, "普通日分支执行完成"); - } - catch (Exception ex) when (ex.Message.Contains("GetInterests") || ex.Message.Contains("浮动利率")) - { - Assert.Inconclusive("需要 CalcSwapInterests 接缝才能测试普通日分支的值。异常: " + ex.Message); - } + Assert.IsTrue(service.PersistedPositions.Count > 0, "应生成eod"); + var result = service.PersistedPositions[0]; + // 普通日:InterestIncomeSum 应 = 前日 + 当天新计 = 1天 + 1天 = 2天 + AssertDecimal(DailyInterest * 2, result.InterestIncomeSum, + $"普通日后 InterestIncomeSum 应=2天利息({DailyInterest * 2:F6})"); + Console.WriteLine($"普通日归档:InterestIncomeSum={result.InterestIncomeSum:F6} = 2×{DailyInterest:F6} ✅"); } /// @@ -325,8 +359,8 @@ namespace YLErp.Modules.SwapModule var interestList = new List { position }; var eodPositions = new List { preEod }; - CallDealInterests(service, interestList, eodPositions, settleDate, td, - new List { swapEvent }, new List(), + service.ExecuteDealInterests(interestList, eodPositions, settleDate, td, + new List { swapEvent }, Principal, 0m, 0m, 1m, Principal); // 互换分支应生成1条 eod @@ -339,32 +373,53 @@ namespace YLErp.Modules.SwapModule #endregion - #region 反射调用 protected DealInterests + // ================================================================ + // 场景3:多日守恒——连续收盘归档,InterestIncomeSum 应线性递增 + // ================================================================ + + #region 场景3:多日连续归档 /// - /// DealInterests 是 protected,通过反射调用(MSTest 不支持 InternalsVisibleTo 方式)。 - /// 也可以在 StubEodPositionService 里加 public 包装方法,但反射更简洁且不改生产类。 + /// [DI_MULTI_001] 连续5天普通日收盘归档,InterestIncomeSum 每天递增1天利息 + /// --------------------------------------------------------------- + /// 从4/27(首日)开始,连续收盘到5/1,验证 InterestIncomeSum 线性递增。 + /// 每天收盘后 InterestIncomeSum 应 = 天数 × DailyInterest。 + /// --------------------------------------------------------------- /// - private static void CallDealInterests( - SwapEodPositionService service, - List interestList, - List eodPositions, - DateTime settleDate, - trade td, - List flowEvents, - List autoInterests, - decimal posiLongNational, decimal posiShortNational, - decimal closeNational, decimal grossPrice, decimal orginPv = Principal) + [TestMethod] + public void DI_MULTI_001_连续5天归档待实现线性递增() { - var method = typeof(SwapEodPositionService).GetMethod("DealInterests", - System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); - Assert.IsNotNull(method, "DealInterests 方法应存在"); - method.Invoke(service, new object[] + var td = CreateTrade(); + var position = CreateInterestPosition(); + position.InterestSwapInterval = null; // 无观察日 + + decimal runningIncomeSum = 0m; + var runningDate = StartDate; + + for (int day = 0; day < 5; day++) { - interestList, eodPositions, new List(), - settleDate, td, flowEvents, autoInterests, null, - posiLongNational, posiShortNational, closeNational, grossPrice, orginPv - }); + var service = new StubEodPositionService(); + var preEod = CreatePreEod(runningDate.AddDays(-1), runningIncomeSum); + + service.ExecuteDealInterests( + new List { position }, + new List { preEod }, + runningDate, td, new List(), + Principal, 0m, 0m, 1m, Principal); + + Assert.IsTrue(service.PersistedPositions.Count > 0, $"第{day + 1}天应生成eod"); + var result = service.PersistedPositions[0]; + + // 首日 InterestIncomeSum = 1天利息,后续每天+1天利息 + decimal expected = DailyInterest * (day + 1); + AssertDecimal(expected, result.InterestIncomeSum, + $"第{day + 1}天 InterestIncomeSum 应={(day + 1)}天利息"); + + runningIncomeSum = result.InterestIncomeSum; + runningDate = runningDate.AddDays(1); + } + + Console.WriteLine($"连续5天归档:InterestIncomeSum 从0递增到{runningIncomeSum:F6} = 5×{DailyInterest:F6} ✅"); } #endregion