refactor(test): SwapDealSettlementTest拆分为SwapUnwind/SwapIncome+提取共享stub
按业务拆分原 SwapDealSettlementTest(8场景)为3个文件,提升可读性: - TestableSwapDealService.cs: 共享stub(7个seam override)+工厂方法(CreateTrade/CreateUnwindData) - SwapUnwindScenarioTest.cs: 平仓全流程6场景(UW_001~006: 全平/部分平/含预付金/含费价重算/审核/提交) - SwapIncomeScenarioTest.cs: 结息2场景(SI_001~002: 正常结息/含预付金返息) 命名对齐 testable 分支风格(SwapUnwindScenarioTest),场景前缀改 UW_/SI_ 更直观。 删除 SwapDealSettlementTest.cs(内容已拆分迁移)。 SwapModule 173测试全绿,无回归。
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
using Newtonsoft.Json;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
|
||||
namespace YLErp.Modules.SwapModule
|
||||
{
|
||||
/// <summary>
|
||||
/// SwapDealService 的可测试化子类(共享 stub)。
|
||||
/// 继承 SwapDealService,override seam 把 DB/事务/外部服务替换为内存收集器。
|
||||
/// 被 SwapUnwindScenarioTest / SwapIncomeScenarioTest 共用,避免重复。
|
||||
/// </summary>
|
||||
public class TestableSwapDealService : SwapDealService
|
||||
{
|
||||
private readonly trade _trade;
|
||||
private readonly Dictionary<int, swap_event> _swapEvents;
|
||||
private readonly Dictionary<long, List<swap_flow_event>> _flowEventsByEventId;
|
||||
|
||||
/// <summary>捕获 AddClientCash 的每次调用(金额, 操作, 日期)</summary>
|
||||
public List<(double amount, string action, DateTime date)> ClientCashCalls { get; } = new();
|
||||
|
||||
/// <summary>捕获 SaveSwapDeal 的每次调用(unwindData, eventType, clientCashId)</summary>
|
||||
public List<(UnwindData data, int eventType, int clientCashId)> SaveSwapDealCalls { get; } = new();
|
||||
|
||||
public int SaveAllChangesCount;
|
||||
public int CloseReCheckCallCount;
|
||||
|
||||
public TestableSwapDealService(trade td,
|
||||
Dictionary<int, swap_event> swapEvents = null,
|
||||
Dictionary<long, List<swap_flow_event>> flowEventsByEventId = null)
|
||||
: base(new OptUserInfo(0, nameof(TestableSwapDealService), OptUserFrom.UnitTest))
|
||||
{
|
||||
_trade = td;
|
||||
_swapEvents = swapEvents ?? new Dictionary<int, swap_event>();
|
||||
_flowEventsByEventId = flowEventsByEventId ?? new Dictionary<long, List<swap_flow_event>>();
|
||||
}
|
||||
|
||||
protected override trade FindTrade(int tradeId) => tradeId == _trade.id ? _trade : null;
|
||||
|
||||
protected override int AddClientCash(trade td, double amount, string action, DateTime valueDate)
|
||||
{
|
||||
ClientCashCalls.Add((amount, action, valueDate));
|
||||
return ClientCashCalls.Count; // 返回自增 id
|
||||
}
|
||||
|
||||
// 整体 override SaveSwapDeal:收集入参,规避内部 new SwapEventService 连库
|
||||
protected override long SaveSwapDeal(UnwindData unwindData, int eventType, int clientCashId, string eventResason = "", bool approve = false)
|
||||
{
|
||||
SaveSwapDealCalls.Add((unwindData, eventType, clientCashId));
|
||||
return SaveSwapDealCalls.Count; // 返回自增 eventId
|
||||
}
|
||||
|
||||
// ApproveSwapTrade 查待审核事件:从内存字典取(key=eventType)
|
||||
protected override swap_event FindSwapEvent(int tradeId, int eventType)
|
||||
{
|
||||
return _swapEvents.TryGetValue(eventType, out var evt) ? evt : null;
|
||||
}
|
||||
|
||||
// ApproveSwapTrade 查事件关联流水:从内存字典取
|
||||
protected override List<swap_flow_event> FindFlowEventsByEventId(long eventId)
|
||||
{
|
||||
return _flowEventsByEventId.TryGetValue(eventId, out var list) ? list : new List<swap_flow_event>();
|
||||
}
|
||||
|
||||
// ApplySwapTrade 的前置校验:计数,不实际执行
|
||||
protected override void CloseReCheckSetTrade(int swapTradeId, bool isSwap, bool needCheck)
|
||||
{
|
||||
CloseReCheckCallCount++;
|
||||
}
|
||||
|
||||
protected override void SaveAllChanges() { SaveAllChangesCount++; }
|
||||
protected override void ExecuteInTransaction(Action action) => action(); // 不包事务,直接执行
|
||||
protected override void CallSaveSwapTradeClientCash(trade td, DateTime valueDate) { } // 空操作
|
||||
protected override void TriggerRealtimeSwapPosition() { } // 空操作
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// SwapDealService 测试的共享工厂方法(TestableSwapDealService + UnwindData 构造)。
|
||||
/// 被 SwapUnwindScenarioTest / SwapIncomeScenarioTest 共用。
|
||||
/// </summary>
|
||||
public static class SwapDealTestFactory
|
||||
{
|
||||
public const int SwapTradeId = 7700;
|
||||
public static readonly DateTime ValueDate = new(2026, 6, 15);
|
||||
public static readonly DateTime UnwindDate = new(2026, 6, 16);
|
||||
|
||||
public static trade CreateTrade()
|
||||
{
|
||||
return new trade
|
||||
{
|
||||
id = SwapTradeId, TradeNumber = "UT-SD-001", ClientId = 888888,
|
||||
TradeType = "收益互换", StartDate = new DateTime(2026, 1, 5),
|
||||
ExerciseDate = new DateTime(2026, 6, 14), // 已到期边界(SwapIncome 判断用)
|
||||
TradeStatus = "确认成交", ValidState = "Valid",
|
||||
Notional = 1000000, StockEqvNotional = 1000000, TradeAmount = 10000
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>构造结息/平仓的 UnwindData(金额由前端算好传入,后端直接用)</summary>
|
||||
public static UnwindData CreateUnwindData(decimal swapRealizedPnL, decimal swapMarginRebatePnl = 0m,
|
||||
decimal swapMarginAmount = 0m, int closeMethod = 0, decimal closePercent = 0m,
|
||||
decimal closeQty = 0m, decimal closeNotionalValue = 0m, decimal positionQty = 0m)
|
||||
{
|
||||
return new UnwindData
|
||||
{
|
||||
SwapTradeId = SwapTradeId,
|
||||
SwapRealizedPnL = swapRealizedPnL,
|
||||
SwapMarginRebatePnl = swapMarginRebatePnl,
|
||||
SwapMarginAmount = swapMarginAmount,
|
||||
SwapCloseAmount = swapRealizedPnL,
|
||||
CloseMethod = closeMethod,
|
||||
ClosePercent = closePercent,
|
||||
CloseQty = closeQty,
|
||||
CloseNotionalValue = closeNotionalValue,
|
||||
PositionQty = positionQty,
|
||||
ValueDate = ValueDate,
|
||||
UnwindDate = UnwindDate,
|
||||
StartDate = new DateTime(2026, 1, 5)
|
||||
};
|
||||
}
|
||||
|
||||
public static void AssertDecimalEqual(decimal expected, decimal actual, decimal tolerance, string message = "")
|
||||
{
|
||||
Assert.IsTrue(Math.Abs(expected - actual) <= tolerance,
|
||||
$"{message} Expected: {expected}, Actual: {actual}, Diff: {expected - actual}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user