test(swap): ClearSwapPositions重收盘清理合成测试(78751f0a覆盖)

验证78751f0a的核心修复:重收盘清理自动互换资金记录时排除手动互换。

4个测试场景:
- CSW_001: 自动互换资金记录被正确选中删除
- CSW_002: 手动互换资金记录被排除(manualClientCashIds排除逻辑)
- CSW_003: 已在excludedClientCashIds中的记录不重复选中
- CSW_004: 预付金返息记录也参与清理(手动被排除)

Stub override 3个虚方法注入内存数据:
FindFlowEventsByEventIds/FindManualClientCashIds/FindClientCashRecords

至此4个最近修复全部有合成测试覆盖:
①复利consumedInterest() ②eod InterestIncomeSum()
③重收盘误删资金() ④分红重复计算()
验证: 111+4=115全通过。
This commit is contained in:
hjhan
2026-07-02 09:11:38 +08:00
parent 17005aa539
commit fb0ab3e948
@@ -0,0 +1,253 @@
using Newtonsoft.Json;
using YLErp.DBModels;
using YLErp.DBModels.Enums;
namespace YLErp.Modules.SwapModule
{
/// <summary>
/// ClearSwapPositions 重收盘清理 - 合成单元测试
/// ============================================================================
/// 验证 78751f0a 的核心修复:重收盘清理自动互换资金记录时,
/// 正确排除手动互换的资金记录(manualClientCashIds)。
///
/// 核心方法:GetLegacyAutoSwapClientCashRecordsSwapTradeBaseService
/// 它接收自动互换事件列表,返回应该删除的资金记录。
/// 关键逻辑:cs:491 !manualClientCashIds.Contains(x.id) 排除手动互换的记录。
/// ============================================================================
[TestClass]
public class ClearSwapPositionsScenarioTest
{
private const int TradeId = 300;
#region Stub
private sealed class StubService : SwapEodPositionService
{
// 注入的内存数据
public List<swap_flow_event> FlowEvents { get; set; } = new();
public List<swap_event> SwapEvents { get; set; } = new();
public List<ClientCashInCashOut> CashRecords { get; set; } = new();
public List<ClientCashInCashOut> DeletedRecords { get; } = new();
public StubService() : base(new OptUserInfo(0, nameof(ClearSwapPositionsScenarioTest), OptUserFrom.UnitTest))
{
}
// override GetLegacyAutoSwapClientCashRecords 的内部依赖
protected override List<swap_flow_event> FindFlowEventsByEventIds(List<long> eventIds)
{
return FlowEvents.Where(x => x.EventId.HasValue && eventIds.Contains(x.EventId.Value)).ToList();
}
protected override List<int> FindManualClientCashIds(int swapTradeId)
{
return SwapEvents
.Where(x => x.SwapTradeId == swapTradeId
&& x.ClientCashId > 0
&& x.EventType != (int)SwapEventTypeEnum.)
.Select(x => x.ClientCashId)
.ToList();
}
protected override List<ClientCashInCashOut> FindClientCashRecords(int tradeId)
{
return CashRecords.Where(x => x.TradeId == tradeId).ToList();
}
// public 包装
public List<ClientCashInCashOut> ExecuteGetLegacyAutoSwapClientCashRecords(
List<swap_event> swapEvents, List<int> excludedClientCashIds)
{
return GetLegacyAutoSwapClientCashRecords(swapEvents, excludedClientCashIds);
}
}
#endregion
#region
private static swap_event CreateAutoSwapEvent(long id, DateTime valueDate, params int[] cashIds)
{
// SwapRealizedPnL=-100 匹配 Money=100IsLegacyAutoSwapClientCashRecord 按金额校验)
var data = new UnwindData
{
SwapTradeId = TradeId, ValueDate = valueDate,
ClientCashIds = cashIds.ToList(),
SwapRealizedPnL = -100m // -Money → IsLegacyAutoSwapClientCashRecord 匹配
};
return new swap_event
{
id = id, SwapTradeId = TradeId, EventType = (int)SwapEventTypeEnum.,
ValueDate = valueDate, ClientCashId = cashIds.FirstOrDefault(),
EventData = JsonConvert.SerializeObject(data)
};
}
private static swap_event CreateManualSwapEvent(long id, DateTime valueDate, int clientCashId)
{
return new swap_event
{
id = id, SwapTradeId = TradeId, EventType = (int)SwapEventTypeEnum.,
ValueDate = valueDate, ClientCashId = clientCashId,
EventData = JsonConvert.SerializeObject(new UnwindData { SwapTradeId = TradeId, ValueDate = valueDate })
};
}
private static ClientCashInCashOut CreateCashRecord(int id, string action, DateTime happenDate, double money = 100)
{
return new ClientCashInCashOut
{
id = id, TradeId = TradeId, Action = action,
HappenDate = happenDate, Money = money,
State = ClientCashInCashOut., ValidState = "Valid"
};
}
#endregion
// ================================================================
// 场景1:自动互换资金记录被正确选中删除
// ================================================================
/// <summary>
/// [CSW_001] 只有一条自动互换资金记录 → 应被选中删除
/// ---------------------------------------------------------------
/// 构造1条自动互换事件(EventData含ClientCashIds)
/// 1条资金记录(Action=系统操作-互换)。
/// GetLegacyAutoSwapClientCashRecords 应返回这条资金记录。
/// </summary>
[TestMethod]
public void CSW_001_自动互换资金记录被选中删除()
{
var date = new DateTime(2026, 6, 29);
var service = new StubService();
// 不含ClientCashIds → 走legacy路径
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
service.SwapEvents.Add(autoEvent);
var cashRecord = CreateCashRecord(id: 7001, action: ClientCashInCashOut._互换, happenDate: date);
service.CashRecords.Add(cashRecord);
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
Assert.AreEqual(1, result.Count, "应选中1条自动互换资金记录");
Assert.AreEqual(7001, result[0].id, "选中的应是id=7001");
Console.WriteLine($"自动互换资金记录(id=7001)被正确选中 ✅");
}
// ================================================================
// 场景2:手动互换资金记录被排除(78751f0a 核心修复)
// ================================================================
/// <summary>
/// [CSW_002] 手动互换资金记录不被选中(manualClientCashIds排除)
/// ---------------------------------------------------------------
/// 构造1条自动互换事件 + 1条手动互换事件(ClientCashId=8001)。
/// 2条资金记录都是Action=系统操作-互换,但1条属于手动(id=8001)。
/// GetLegacyAutoSwapClientCashRecords 应只返回自动的那条,排除手动的。
/// </summary>
[TestMethod]
public void CSW_002_手动互换资金记录被排除()
{
var date = new DateTime(2026, 6, 29);
var service = new StubService();
// 自动互换事件(无ClientCashIds,走legacy路径)
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
// 手动互换事件(ClientCashId=8001
var manualEvent = CreateManualSwapEvent(id: 5002, valueDate: date, clientCashId: 8001);
service.SwapEvents.Add(autoEvent);
service.SwapEvents.Add(manualEvent);
// 两条资金记录都是系统操作-互换
var autoCash = CreateCashRecord(id: 7001, action: ClientCashInCashOut._互换, happenDate: date);
var manualCash = CreateCashRecord(id: 8001, action: ClientCashInCashOut._互换, happenDate: date);
service.CashRecords.Add(autoCash);
service.CashRecords.Add(manualCash);
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
// 应只返回自动的(7001),排除手动的(8001)
Assert.AreEqual(1, result.Count, "应只选中1条(排除手动的)");
Assert.AreEqual(7001, result[0].id, "选中的应是自动的id=7001");
Assert.IsFalse(result.Any(x => x.id == 8001), "手动互换(id=8001)不应被选中");
Console.WriteLine($"手动互换资金记录(id=8001)被正确排除 ✅");
}
// ================================================================
// 场景3excludedClientCashIds 排除已处理的记录
// ================================================================
/// <summary>
/// [CSW_003] 已在excludedClientCashIds中的记录不被重复选中
/// ---------------------------------------------------------------
/// 资金记录id=7001已在excludedClientCashIds中(之前已处理过),
/// 不应再次被选中。
/// </summary>
[TestMethod]
public void CSW_003_已处理的记录不重复选中()
{
var date = new DateTime(2026, 6, 29);
var service = new StubService();
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
service.SwapEvents.Add(autoEvent);
var cash1 = CreateCashRecord(id: 7001, action: ClientCashInCashOut._互换, happenDate: date);
var cash2 = CreateCashRecord(id: 7002, action: ClientCashInCashOut._互换, happenDate: date);
service.CashRecords.Add(cash1);
service.CashRecords.Add(cash2);
// 7001 已在 excludedClientCashIds 中
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int> { 7001 });
Assert.AreEqual(1, result.Count, "应只选中1条(排除已处理的7001)");
Assert.AreEqual(7002, result[0].id, "选中的应是未处理的7002");
Console.WriteLine($"已处理记录(7001)被排除,只选中7002 ✅");
}
// ================================================================
// 场景4:预付金返息记录也被正确处理
// ================================================================
/// <summary>
/// [CSW_004] 预付金返息记录(Action=系统操作-预付金返息)也参与清理
/// ---------------------------------------------------------------
/// 自动互换产生的预付金返息记录应被选中,手动的不应被选中。
/// </summary>
[TestMethod]
public void CSW_004_预付金返息记录参与清理()
{
var date = new DateTime(2026, 6, 29);
var service = new StubService();
var autoEvent = CreateAutoSwapEvent(id: 5001, valueDate: date);
// 预付金返息匹配 SwapMarginRebatePnl,覆盖默认的 SwapRealizedPnL
var autoData = JsonConvert.DeserializeObject<UnwindData>(autoEvent.EventData);
autoData.SwapMarginRebatePnl = -100m;
autoData.SwapRealizedPnL = 0m;
autoEvent.EventData = JsonConvert.SerializeObject(autoData);
var manualEvent = CreateManualSwapEvent(id: 5002, valueDate: date, clientCashId: 8001);
service.SwapEvents.Add(autoEvent);
service.SwapEvents.Add(manualEvent);
// 预付金返息记录
var autoRebate = CreateCashRecord(id: 7001, action: ClientCashInCashOut._预付金返息, happenDate: date);
var manualRebate = CreateCashRecord(id: 8001, action: ClientCashInCashOut._预付金返息, happenDate: date);
service.CashRecords.Add(autoRebate);
service.CashRecords.Add(manualRebate);
var result = service.ExecuteGetLegacyAutoSwapClientCashRecords(
new List<swap_event> { autoEvent }, excludedClientCashIds: new List<int>());
Assert.AreEqual(1, result.Count, "应只选中1条预付金返息(排除手动的)");
Assert.AreEqual(7001, result[0].id, "选中的应是自动的预付金返息7001");
Console.WriteLine($"预付金返息: 自动的(7001)被选中, 手动的(8001)被排除 ✅");
}
}
}