test(swap): 修复 SPC_007 红灯并锁定 a1cdb2cd 日期分桶修复

a1cdb2cd(修正平仓事件日期过滤条件)将 ResolveInterestLegPositionsAsOf 的分桶从
EventDate 改为 UnwindDate,方向正确(应按经济生效日而非簿记日),但 SPC_007 的测试
事件未设 UnwindDate(null) → 落入空桶 → 断言 Expected7000 Actual10000 红灯。

改动(仅测试,生产代码零改动):
- SPC_007:给 close/floatClose 补 UnwindDate(与 EventDate 同日,保持原语义),让测试
  数据贴近真实生产事件(平仓事件总会设 UnwindDate),红灯转绿
- 新增 SPC_008:EventDate≠UnwindDate 场景(簿记 7/11 滞后于经济生效 7/9),验证按
  UnwindDate 分桶——锁定 a1cdb2cd 的修复价值。已反向验证:临时回退 a1cdb2cd 后 SPC_008
  立即失败(Expected7000 Actual10000),证明该测试有效,能抓住此类修复被回滚

双绿通过。生产代码无改动。
This commit is contained in:
hjhan
2026-08-07 11:39:08 +08:00
parent c40b1ba416
commit b3ce94b67b
@@ -401,6 +401,10 @@ namespace YLErp.Modules.SwapModule
PositionType = 0,
EventType = (int)SwapEventTypeEnum.,
EventDate = new DateTime(2026, 7, 9),
// UnwindDate 必须显式设置:ResolveInterestLegPositionsAsOf 自 a1cdb2cd 起按 UnwindDate
// (经济生效日)分桶,而非 EventDate(簿记日)。生产平仓事件总会设 UnwindDate
// InitUnwind:327、AuotoSwapUnwind:1590)。此处同日场景:UnwindDate == EventDate。
UnwindDate = new DateTime(2026, 7, 9),
InterestMode = (int)InterestModeEnum.,
InterestPrincipal = 3000m
};
@@ -410,6 +414,7 @@ namespace YLErp.Modules.SwapModule
PositionType = 1,
EventType = (int)SwapEventTypeEnum.,
EventDate = new DateTime(2026, 7, 9),
UnwindDate = new DateTime(2026, 7, 9),
TradingAmount = 3000000m
};
var originalWithFloat = new List<swap_position>
@@ -430,5 +435,84 @@ namespace YLErp.Modules.SwapModule
Assert.AreEqual(10000m, beforeClose.InterestPrincipalFix);
Assert.AreEqual(7000m, onCloseDate.InterestPrincipalFix);
}
/// <summary>
/// [SPC_008] EventDate ≠ UnwindDate 时,ResolveInterestLegPositionsAsOf 按 UnwindDate(经济生效日)分桶。
/// ----------------------------------------------------------------------------
/// 锁定 a1cdb2cd 的修复价值:平仓事件的簿记日(EventDate)可能滞后于经济生效日(UnwindDate)
/// (如 T+N 结算、手动补录)。重放预付金腿 as-of 本金时,分桶必须按 UnwindDate:
/// - settleDate &lt; UnwindDate → 平仓"未发生"as-of=原始本金
/// - settleDate &gt;= UnwindDate → 平仓"已生效"as-of=实时剩余本金
/// 修复前按 EventDate 分桶:settleDate 落在 [UnwindDate, EventDate) 区间时,会被误判为"未发生"。
/// 本测试构造 EventDate=7/11、UnwindDate=7/9,验证 settleDate=7/10 时已按 UnwindDate 生效。
/// </summary>
[TestMethod]
public void SPC_008_EventDateDiffersFromUnwindDate_BucketsByUnwindDate()
{
const long originalPositionId = 2;
var original = new swap_position
{
id = originalPositionId, PosiDirection = 0,
InterestDirection = (int)SwapDirectionEnum.,
InterestMode = (int)InterestModeEnum.,
InterestPrincipalFix = 10000m
};
var realtime = new swap_position
{
PositionId = originalPositionId,
InterestMode = (int)InterestModeEnum.,
InterestPrincipalFix = 7000m
};
// 关键:EventDate(簿记 7/11) 滞后于 UnwindDate(经济生效 7/9) —— T+N 结算/补录常见
var close = new swap_flow_event
{
PositionId = originalPositionId,
PositionType = 0,
EventType = (int)SwapEventTypeEnum.,
EventDate = new DateTime(2026, 7, 11),
UnwindDate = new DateTime(2026, 7, 9),
InterestMode = (int)InterestModeEnum.,
InterestPrincipal = 3000m
};
var floatClose = new swap_flow_event
{
PositionId = 1,
PositionType = 1,
EventType = (int)SwapEventTypeEnum.,
EventDate = new DateTime(2026, 7, 11),
UnwindDate = new DateTime(2026, 7, 9),
TradingAmount = 3000000m
};
var originalWithFloat = new List<swap_position>
{
original,
new swap_position { id = 1, PosiDirection = 1, PosiNotionalValue = 10000000m }
};
var flows = new[] { close, floatClose };
// settleDate=7/8(经济生效日前)→ as-of=原始 10000
var beforeEffective = SwapDealService.ResolveInterestLegPositionsAsOf(
originalWithFloat, new List<swap_position> { realtime }, flows, new DateTime(2026, 7, 8))
.Single(x => x.id == originalPositionId);
Assert.AreEqual(10000m, beforeEffective.InterestPrincipalFix,
"7/8(经济生效日前):平仓未发生,as-of 本金应=原始 10000");
// settleDate=7/9(经济生效日当天)→ as-of=实时剩余 7000
var onEffectiveDate = SwapDealService.ResolveInterestLegPositionsAsOf(
originalWithFloat, new List<swap_position> { realtime }, flows, new DateTime(2026, 7, 9))
.Single(x => x.id == originalPositionId);
Assert.AreEqual(7000m, onEffectiveDate.InterestPrincipalFix,
"7/9(经济生效日):平仓已生效,as-of 本金应=实时剩余 7000");
// 【关键·锁定 a1cdb2cd】settleDate=7/10(生效后、簿记前)→ 应按 UnwindDate 判为已生效 =7000
// 修复前按 EventDate(7/11) 分桶:7/10 < 7/11 → 误判"未发生" → 返回 10000(错误)
// 修复后按 UnwindDate(7/9) 分桶:7/10 >= 7/9 → 已生效 → 返回 7000(正确)
var afterEffectiveBeforeBook = SwapDealService.ResolveInterestLegPositionsAsOf(
originalWithFloat, new List<swap_position> { realtime }, flows, new DateTime(2026, 7, 10))
.Single(x => x.id == originalPositionId);
Assert.AreEqual(7000m, afterEffectiveBeforeBook.InterestPrincipalFix,
"7/10(生效后、簿记前):必须按 UnwindDate 判已生效 → 7000。" +
"若返回 10000,说明 a1cdb2cd 修复被回滚(退回按 EventDate 分桶)。");
}
}
}