test: 日终价格回归测试(日期窗口纯单测 + 新增可见性DB集成测试)

- EodPriceService 提取 static ResolveValueDateWindow(reqStart,reqEnd),使日期窗口逻辑可脱离数据库单测

- EodPriceDtoTest 新增4个纯单测:未传日期回退±1年、起止都填今天→仅今天窗口、显式区间原样生效、结束日今天上界为明天;锁定'页面始终5条'根因

- EodPriceGoldenReplayTest 新增 Record_NewRecordVisibility([Ignore]手动跑):直接跑 SearchUnderlyingList,锁定(a)今天+已上市可查 (b)日期0001查不出 (c)未上市查不出

- 构建0错误;EodPriceDtoTest 19通过0失败
This commit is contained in:
hjhan
2026-07-13 13:37:18 +08:00
parent d978f7048d
commit 02624abf75
3 changed files with 137 additions and 2 deletions
@@ -202,5 +202,53 @@ namespace YLErp.Modules.EodModule
}
#endregion
#region "页面始终5条"
[TestMethod]
[Description("前端未传日期(年份<=2000) → 回退到 [今天-1年, 今天+1年)")]
public void _回退最近一年到明年()
{
var (start, end) = EodPriceService.ResolveValueDateWindow(DateTime.MinValue, DateTime.MinValue);
Assert.AreEqual(DateTime.Today.AddYears(-1).Date, start.Date, "起始应回退到今天-1年");
Assert.AreEqual(DateTime.Today.AddYears(1).Date, end.Date, "结束应回退到今天+1年");
Assert.IsTrue(end > start, "窗口应正向");
}
[TestMethod]
[Description("列表页默认起止都填今天 → 窗口=[今天, 今天+1天),仅返回当天记录(即'5条'现象成因)")]
public void _窗口仅今天()
{
var today = DateTime.Today;
var (start, end) = EodPriceService.ResolveValueDateWindow(today, today);
Assert.AreEqual(today.Date, start.Date, "起始应为今天");
Assert.AreEqual(today.AddDays(1).Date, end.Date, "结束应为今天+1天(半开区间含今天)");
Assert.IsTrue(today >= start && today < end, "今天的记录应落入窗口");
Assert.IsFalse(today.AddDays(-1) >= start && today.AddDays(-1) < end, "昨天的记录不应落入仅今天窗口");
Assert.IsFalse(today.AddDays(1) >= start && today.AddDays(1) < end, "明天的记录不应落入仅今天窗口");
}
[TestMethod]
[Description("显式传区间(如近30天) → 原样生效,不被回退覆盖")]
public void _原样生效()
{
var start0 = DateTime.Today.AddDays(-30);
var end0 = DateTime.Today;
var (start, end) = EodPriceService.ResolveValueDateWindow(start0, end0);
Assert.AreEqual(start0.Date, start.Date, "起始应等于传入");
Assert.AreEqual(end0.AddDays(1).Date, end.Date, "结束应等于传入+1天");
}
[TestMethod]
[Description("结束日=今天 → 半开区间上界=今天+1天,今天当天记录可命中")]
public void _上界为明天_当天可命中()
{
var (start, end) = EodPriceService.ResolveValueDateWindow(DateTime.Today.AddDays(-365), DateTime.Today);
var today = DateTime.Today;
Assert.IsTrue(today >= start && today < end, "今天记录应命中");
Assert.IsFalse(today.AddDays(1) >= start && today.AddDays(1) < end, "明天记录不应命中");
}
#endregion
}
}
@@ -187,5 +187,78 @@ namespace YLErp.Modules.EodModule
}
#endregion
#region [Ignore]
/// <summary>
/// 回归"新增日终价格后是否查得出",直接跑生产查询 SearchUnderlyingList。
/// 守护点(与之前"新增后查不出"的修复一一对应):
/// (a) 今天 + 已上市(LaunchState=1) 标的 → 查得出;
/// (b) 估值日期=0001(未填) → 落在列表默认"仅今天"窗口外 → 查不出;
/// (c) 标的未上市(LaunchState!=1) → 被 inner join(underlying_manager.LaunchState=="1") 过滤 → 查不出。
/// 复用库中已有标的(不新建 underlying_manager,避免触碰该表约束),只插入/清理临时债券估值行。
/// </summary>
[TestMethod]
[Ignore]
[TestCategory("EodVisibility")]
[Description("新增日终价格可见性:(a)今天+已上市可查 (b)日期0001查不出 (c)未上市查不出")]
public void Record_NewRecordVisibility()
{
using (var db = DbContextFactory.GetYLDbContext())
{
var svc = new EodPriceService(OptUserInfo.SystemUser);
var today = DateTime.Today;
var req = new EodCommodityFuturePriceReq { ValueDateStart = today, ValueDateEnd = today };
// 取一个已上市的债券类标的(正向用例);退而求其次取任意已上市标的
var listedBond = db.underlying_manager
.FirstOrDefault(x => x.LaunchState == "1" && x.UnderlyingInstrumentType == ConsGlobal.InstrumentType.CreditBonds)
?? db.underlying_manager.FirstOrDefault(x => x.LaunchState == "1");
Assert.IsNotNull(listedBond, "需存在一个 LaunchState=1 的标的用于正向回归");
// 取一个未上市的标的(负向用例)
var unlisted = db.underlying_manager.FirstOrDefault(x => x.LaunchState != "1");
Assert.IsNotNull(unlisted, "需存在一个 LaunchState!=1 的标的用于负向回归");
var insertedIds = new List<long>();
try
{
// (a) 今天 + 已上市 → 查得出
var a = new ChinaBondValuation { bond_id = listedBond.UnderlyingCode, valuation_date = today, dirty_price_close = 100, net_price = 100, yield = 3 };
db.china_bond_valuation.Add(a);
db.SaveChanges();
insertedIds.Add(a.id);
var rA = svc.SearchUnderlyingList(req);
Assert.IsTrue(rA.rows.Any(x => x.id == a.id), "(a) 今天+已上市债券应查得出");
// (b) 日期=0001(未填) → 落在仅今天窗口外,查不出
var b = new ChinaBondValuation { bond_id = listedBond.UnderlyingCode, valuation_date = DateTime.MinValue, dirty_price_close = 100, net_price = 100, yield = 3 };
db.china_bond_valuation.Add(b);
db.SaveChanges();
insertedIds.Add(b.id);
var rB = svc.SearchUnderlyingList(req);
Assert.IsFalse(rB.rows.Any(x => x.id == b.id), "(b) 日期0001 应查不出");
// (c) 未上市标的 → 被 inner join 过滤,查不出
var c = new ChinaBondValuation { bond_id = unlisted.UnderlyingCode, valuation_date = today, dirty_price_close = 100, net_price = 100, yield = 3 };
db.china_bond_valuation.Add(c);
db.SaveChanges();
insertedIds.Add(c.id);
var rC = svc.SearchUnderlyingList(req);
Assert.IsFalse(rC.rows.Any(x => x.id == c.id), "(c) 未上市标的应查不出");
}
finally
{
foreach (var id in insertedIds)
{
var e = db.china_bond_valuation.Find(id);
if (e != null) db.china_bond_valuation.Remove(e);
}
db.SaveChanges();
}
}
}
#endregion
}
}
+16 -2
View File
@@ -17,10 +17,24 @@ namespace YLErp.Modules.EodModule
}
/// <summary>
/// 解析日终价格列表的"估值日期"查询窗口。抽成 static 以便纯单测锁定行为(避免改坏)。
/// 规则:
/// - 起始日期年份 &gt; 2000(前端传了有效日期)→ 用传入值;否则回退到 今天-1年。
/// - 结束日期年份 &gt; 2000 → 用传入值+1天(闭区间转半开);否则回退到 今天+1年。
/// 注意:列表页默认把起止都设成"今天",于是窗口=[今天, 今天+1天)=仅今天 → 仅返回当天的记录
/// (即"页面始终5条"现象的真正成因,非分页/查询 bug)。要看历史须把起始日期调早。
/// </summary>
public static (DateTime start, DateTime end) ResolveValueDateWindow(DateTime reqStart, DateTime reqEnd)
{
var start = reqStart.Year > 2000 ? reqStart : DateTime.Today.AddYears(-1);
var end = reqEnd.Year > 2000 ? reqEnd.AddDays(1) : DateTime.Today.AddYears(1);
return (start, end);
}
public SearchListResult<EodUnderlyingPriceDto> SearchUnderlyingList(EodCommodityFuturePriceReq req)
{
var valueDtStart = req.ValueDateStart.Year > 2000 ? req.ValueDateStart : DateTime.Today.AddYears(-1);
var valueDtEnd = req.ValueDateEnd.Year > 2000 ? req.ValueDateEnd.AddDays(1) : DateTime.Today.AddYears(1);
var (valueDtStart, valueDtEnd) = ResolveValueDateWindow(req.ValueDateStart, req.ValueDateEnd);
var predicatUn = PredicateBuilder.Create<underlying_manager>(d => d.LaunchState == "1");
var predicatEoc = PredicateBuilder.Create<eod_commodity_future_price>(source => source.ValueDate >= valueDtStart && source.ValueDate < valueDtEnd);