Files
zszq-trs/UnitTestProject/Modules/SwapModule/ClearSwapPositionsScenarioTest.cs
T
hjhan fb0ab3e948 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全通过。
2026-07-02 09:11:38 +08:00

254 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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)被排除 ✅");
}
}
}