test(swap): 补ApproveSwapTrade/ApplySwapTrade测试+3个seam(借鉴testable分支)
借鉴 origin/glms/feature/refactor-swap-event-testable 的 SwapUnwindScenarioTest 场景4/5, 补全当前分支缺失的平仓审批流程覆盖。 SwapDealService 新增3个 protected virtual seam: - FindSwapEvent(查待审核事件) - FindFlowEventsByEventId(查事件关联流水) - CloseReCheckSetTrade(前置校验包装) ApproveSwapTrade/ApplySwapTrade 重构为调seam,生产行为不变。 SwapDealSettlementTest 新增2个测试(SD_005/006,共6个): - SD_005 ApproveSwapTrade 全平仓审核: 反序列化swap_event.EventData→资金流水→TradeStatus - SD_006 ApplySwapTrade 提交审核: CloseReCheck前置校验+SwapRealizedPnL=SwapCloseAmount 融合策略: 保留当前分支SwapIncome资金流水断言独特价值(SD_001/002), 借鉴testable分支审批流程覆盖(SD_005/006),不二选一。 SwapModule 157测试全绿,无回归。
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
using Newtonsoft.Json;
|
||||
using YLErp.DBModels;
|
||||
using YLErp.DBModels.Enums;
|
||||
|
||||
@@ -32,13 +33,21 @@ namespace YLErp.Modules.SwapModule
|
||||
private sealed class StubDealService : SwapDealService
|
||||
{
|
||||
private readonly trade _trade;
|
||||
private readonly Dictionary<int, swap_event> _swapEvents;
|
||||
private readonly Dictionary<long, List<swap_flow_event>> _flowEventsByEventId;
|
||||
public List<(double amount, string action, DateTime date)> ClientCashCalls = new();
|
||||
public List<(UnwindData data, int eventType, int clientCashId)> SaveSwapDealCalls = new();
|
||||
public int SaveAllChangesCount;
|
||||
public int CloseReCheckCallCount;
|
||||
|
||||
public StubDealService(trade td) : base(new OptUserInfo(0, nameof(SwapDealSettlementTest), OptUserFrom.UnitTest))
|
||||
public StubDealService(trade td,
|
||||
Dictionary<int, swap_event> swapEvents = null,
|
||||
Dictionary<long, List<swap_flow_event>> flowEventsByEventId = null)
|
||||
: base(new OptUserInfo(0, nameof(SwapDealSettlementTest), 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;
|
||||
@@ -56,6 +65,24 @@ namespace YLErp.Modules.SwapModule
|
||||
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) { } // 空操作
|
||||
@@ -247,5 +274,86 @@ namespace YLErp.Modules.SwapModule
|
||||
$"TradingAmount 应=ExitDirtyPrice(1.02)×CloseQty(1000)=1020,实际={closeEvent.TradingAmount}");
|
||||
Console.WriteLine($"SD_004 通过:ExitDirtyFeePrice={closeEvent.TradingAmountFeeAvg},ExitCleanFeePrice={closeEvent.TradingAmountNetFeeAvg},TradingAmount={closeEvent.TradingAmount} ✅");
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SD_005:ApproveSwapTrade 审核通过 —— 反序列化事件、资金流水、持仓状态
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_005] ApproveSwapTrade 审核通过全部平仓
|
||||
/// ------------------------------------------------------------
|
||||
/// 后端 SwapDealService.ApproveSwapTrade:从 swap_event.EventData 反序列化 UnwindData,
|
||||
/// 据此生成资金流水 + 更新持仓状态。
|
||||
/// 借鉴 testable 分支 SwapUnwindScenarioTest.Scenario4,验证:
|
||||
/// - SwapRealizedPnL 从事件反序列化正确(EventData JSON)
|
||||
/// - 资金流水金额 = -SwapRealizedPnL
|
||||
/// - 全平仓 → TradeStatus=已平仓
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_005_ApproveSwapTrade_全平仓审核_反序列化事件并记账()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
// 构造待审核事件:EventData 里序列化了 UnwindData(含 SwapRealizedPnL=8000)
|
||||
var unwindData = CreateUnwindData(swapRealizedPnL: 8000m,
|
||||
closeMethod: (int)CloseMethodEnum.全部平仓, closePercent: 1m,
|
||||
closeQty: 10000m, closeNotionalValue: 1000000m);
|
||||
var swapEvent = new swap_event
|
||||
{
|
||||
id = 1, SwapTradeId = SwapTradeId,
|
||||
EventType = (int)SwapEventTypeEnum.平仓, Invalid = false,
|
||||
EventData = JsonConvert.SerializeObject(unwindData)
|
||||
};
|
||||
var flowEvents = new Dictionary<long, List<swap_flow_event>>
|
||||
{
|
||||
[1] = new List<swap_flow_event> { new swap_flow_event { id = 1, EventId = 1, PositionId = 1 } }
|
||||
};
|
||||
var service = new StubDealService(td,
|
||||
swapEvents: new Dictionary<int, swap_event> { [(int)SwapEventTypeEnum.平仓] = swapEvent },
|
||||
flowEventsByEventId: flowEvents);
|
||||
|
||||
service.ApproveSwapTrade(td, (int)SwapEventTypeEnum.平仓);
|
||||
|
||||
// 资金流水:从反序列化的 SwapRealizedPnL(8000) 记账 → -8000
|
||||
Assert.AreEqual(1, service.ClientCashCalls.Count, "全平仓无预付金时应1条资金流水");
|
||||
Assert.AreEqual(-8000.0, service.ClientCashCalls[0].amount, 0.001, "资金流水=-反序列化的SwapRealizedPnL");
|
||||
// 持仓状态
|
||||
Assert.AreEqual("已平仓", td.TradeStatus, "审核全平仓 TradeStatus=已平仓");
|
||||
Console.WriteLine($"SD_005 通过:审核反序列化 SwapRealizedPnL=8000,资金流水={service.ClientCashCalls[0].amount},TradeStatus={td.TradeStatus} ✅");
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SD_006:ApplySwapTrade 提交审核 —— 前置校验 + 保存事件
|
||||
// ================================================================
|
||||
|
||||
/// <summary>
|
||||
/// [SD_006] ApplySwapTrade 提交审核
|
||||
/// ------------------------------------------------------------
|
||||
/// 后端 SwapDealService.ApplySwapTrade:调 CloseReCheckSetTrade 前置校验 + SaveSwapDeal(approve=true)。
|
||||
/// 借鉴 testable 分支 SwapUnwindScenarioTest.Scenario5,验证:
|
||||
/// - CloseReCheckSetTrade 被调用1次
|
||||
/// - SaveSwapDeal 以 approve=true 调用(事件类型正确)
|
||||
/// - SwapRealizedPnL = SwapCloseAmount(ApplySwapTrade 内部赋值)
|
||||
/// </summary>
|
||||
[TestMethod]
|
||||
public void SD_006_ApplySwapTrade_提交审核_前置校验与保存事件()
|
||||
{
|
||||
var td = CreateTrade();
|
||||
var service = new StubDealService(td);
|
||||
// 前端提交时 SwapCloseAmount=6000(前端算好的总额),SwapRealizedPnL 初始可能为0
|
||||
var unwindData = CreateUnwindData(swapRealizedPnL: 0m);
|
||||
unwindData.SwapCloseAmount = 6000m; // 模拟前端传入的平仓总额
|
||||
|
||||
service.ApplySwapTrade(unwindData, (int)SwapEventTypeEnum.平仓);
|
||||
|
||||
// 前置校验被调用
|
||||
Assert.AreEqual(1, service.CloseReCheckCallCount, "应调用 CloseReCheckSetTrade 1次");
|
||||
// SaveSwapDeal 以 approve=true 调用
|
||||
Assert.AreEqual(1, service.SaveSwapDealCalls.Count, "应调用 SaveSwapDeal 1次");
|
||||
Assert.AreEqual((int)SwapEventTypeEnum.平仓, service.SaveSwapDealCalls[0].eventType, "事件类型=平仓");
|
||||
// SwapRealizedPnL 应被赋值为 SwapCloseAmount(ApplySwapTrade 内部 cs:1631)
|
||||
Assert.AreEqual(6000m, service.SaveSwapDealCalls[0].data.SwapRealizedPnL, 0.001m,
|
||||
"SwapRealizedPnL 应=SwapCloseAmount(6000)");
|
||||
Console.WriteLine($"SD_006 通过:CloseReCheck 调用{service.CloseReCheckCallCount}次,SwapRealizedPnL={service.SaveSwapDealCalls[0].data.SwapRealizedPnL} ✅");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user