/** * historicalReconciliation.test.js — 历史对账 + 独立规格差分骨架(离线,无需数据库) * ============================================================================ * 运行:cd YLErpWeb/fe-tests && npx jest historicalReconciliation.test.js * * 数据库边界(关键): * - 本测试运行时【零数据库依赖】。历史数据来自 fixtures/historical_swap_events.json, * 该 fixture 可离线提交进仓库。 * - 唯一的数据库触碰是 tools/exportHistoricalSwapEvents.js —— 开发期一次性导出脚本, * 把真实 swap_event.data 灌进上面的 fixture。它不属于 npm test 路径,CI 不执行。 * * 设计要点: * - referenceOracle:基于 unwindSwapTrade.js:329 的 calcCloseAmount 核心【独立重写】的纯函数 * ("规格"),不信任任何既有实现。用于校验"当前公式产出" == "落库/记录值"。 * - 对已知差异字段(如 swapLongShort.js 的 SwapMarginAmount 无符号)通过 * record.knownDiscrepancies 冻结现状:仅当"差异量"变化时才报红,既不掩盖也不误报。 * - 这是特征测试(characterization):先把现状冻住,未来任何重构只要改变了产出, * 测试立即变红,逼你查根因(绝不自动 re-baseline)。 */ const fs = require('fs'); const path = require('path'); const round2 = v => Math.round((Number(v) || 0) * 100) / 100; // 独立规格:等价于 unwindSwapTrade.js:329-367 的 calcCloseAmount 金额聚合核心。 // 注意:这里只看 SwapCloseAmount / SwapRealizedPnL / SwapMarginRebatePnl / SwapMarginAmount // 四个金额字段;TradingAmount / FeeAvg 等展示字段不在对账范围内(且 swapCalc 也未实现)。 function referenceOracle({ floatPosition, interestList, marginList }) { const fp = floatPosition || {}; const interestLegs = interestList || []; const marginLegs = marginList || []; let closeAmount = round2(fp.FloatPnlSum || 0); let realizedPnL = closeAmount; let marginRebatePnl = 0; let marginAmount = 0; interestLegs.forEach(x => { const amt = round2(x.InterestClosePnL || 0); closeAmount = round2(closeAmount + amt); realizedPnL = round2(realizedPnL + amt); }); marginLegs.forEach(x => { const amt = round2(x.InterestClosePnL || 0); // 与 unwindSwapTrade.js:355 同口径:InterestDirection==1 ? -1 : 1 const interestRatio = x.InterestDirection == 1 ? -1 : 1; closeAmount = round2(closeAmount + amt); marginRebatePnl = round2(marginRebatePnl + amt); realizedPnL = round2(realizedPnL + amt); marginAmount = round2(marginAmount + (Number(x.InterestPrincipal) || 0) * interestRatio); }); return { SwapCloseAmount: closeAmount, SwapRealizedPnL: realizedPnL, SwapMarginRebatePnl: marginRebatePnl, SwapMarginAmount: marginAmount, }; } const TOL = 1e-6; function expectClose(actual, expected, msg) { expect(Math.abs(actual - expected)).toBeLessThanOrEqual(TOL, msg || ''); } const fixturePath = path.join(__dirname, 'fixtures', 'historical_swap_events.json'); const fixture = JSON.parse(fs.readFileSync(fixturePath, 'utf8')); const RECON_FIELDS = ['SwapCloseAmount', 'SwapRealizedPnL', 'SwapMarginRebatePnl', 'SwapMarginAmount']; describe('历史对账:当前公式产出 == 落库/记录值(离线,无 DB)', () => { fixture.records.forEach(rec => { test(`[${rec.id}] ${rec.desc}`, () => { const got = referenceOracle(rec); const exp = rec.expected || {}; const tolerated = rec.knownDiscrepancies || {}; RECON_FIELDS.forEach(field => { if (!(field in exp)) return; // 记录未提供该字段则跳过 const actual = got[field]; const recorded = exp[field]; if (tolerated[field] != null) { // 冻结已知差异:仅当"差异量"本身变化时才报红(行为漂移告警) const deltaNow = Math.abs(actual - recorded); expectClose(deltaNow, tolerated[field], `${field} 已知差异量应稳定为 ${tolerated[field]}(当前 ${deltaNow})`); } else { expectClose(actual, recorded, `${field} 应等于落库值`); } }); }); }); }); // 该 describe 仅做"骨架自洽性"校验:确保 fixture 里的手搓样本与独立规格自洽, // 否则说明 fixture 写错(不是生产 bug)。真实历史差异由上面对账 + 导出脚本覆盖。 describe('骨架自洽:fixture 样本与独立规格一致(非生产断言)', () => { test('UNWIND_STD 全部字段自洽', () => { const rec = fixture.records.find(r => r.id === 'UNWIND_STD'); const got = referenceOracle(rec); expectClose(got.SwapCloseAmount, 51500); expectClose(got.SwapMarginAmount, -50000); }); });