a4902bb730
需求与文档: - R-013 策略自定义字段配置(定义随策略 configSchema 存 settings、值落 strategy_holdings.values JSON、类型化文本/数字/布尔/枚举)定稿并进入 PLAN-012 / 迭代 11(含目标数据模型 + 端到端示例 + 技术方案 + 验收标准) - R-011(Tab设置)/ R-012(UI主题适配)归档至 已完成/,索引标记已归档 - 沉淀约束: 产品约束-010 / 技术约束-015 / 技术约束-016 / UI约束-005 结构重构(技术约束-016): - src/component/ 平铺还原为语义分域目录: data-source/ storage/ position/ market/ trades/(git rename 保留历史) - 清理死代码: 删除 AllocationStorage.js(0 引用)、DataStore setDataset/removeDataset(无调用方) - 同步更新 src/index.js / scripts/*.mjs / tsdown.config.ts 引用 - typecheck + build + test-r009 回归 14/14 通过
65 lines
2.8 KiB
JavaScript
65 lines
2.8 KiB
JavaScript
/**
|
|
* API 层集成测试:handleTrade 各端点(含 settings 依赖的 candidates 端点)
|
|
* 独立数据目录,mock dataSource + 真实 storage/settings
|
|
*/
|
|
import { mkdtempSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { DataStore } from '../src/storage/DataStore.js';
|
|
import { handleTrade, TRADE_METHODS } from '../src/api/trades.js';
|
|
|
|
const dir = mkdtempSync(join(tmpdir(), 'odl-api-test-'));
|
|
process.env.ODL_TEST_DATA_DIR = dir;
|
|
|
|
let pass = 0, fail = 0;
|
|
function assert(cond, msg) {
|
|
if (cond) { pass++; console.log(' ✅ ' + msg); }
|
|
else { fail++; console.log(' ❌ ' + msg); }
|
|
}
|
|
|
|
const storage = new DataStore({ dataDir: dir });
|
|
await storage._ensure();
|
|
|
|
// mock settings(含策略名)
|
|
const settings = {
|
|
get: () => ({ strategies: [
|
|
{ id: 'grid-supermarket', name: '网格超市' },
|
|
{ id: 'manual-t', name: '手动做T' },
|
|
]}),
|
|
};
|
|
|
|
// mock dataSource
|
|
const dataSource = {
|
|
async getOrders() { return []; },
|
|
async getTrades() { return []; },
|
|
async getTradingDates() { return { market: '', dates: [] }; },
|
|
};
|
|
|
|
const runtime = { dataSource, storage, settings };
|
|
|
|
// 端点注册齐全
|
|
assert(TRADE_METHODS.has('orders/attribution-candidates'), '端点 orders/attribution-candidates 已注册');
|
|
assert(TRADE_METHODS.has('orders/set-attribution'), '端点 orders/set-attribution 已注册');
|
|
assert(TRADE_METHODS.has('trades/history'), '端点 trades/history 已注册');
|
|
|
|
// 候选端点(含策略名)
|
|
storage.openHolding('manual-t', '300057.SZ', 1000);
|
|
const cands = await handleTrade('orders/attribution-candidates', { code: '300057.SZ' }, runtime);
|
|
assert(cands.length === 1 && cands[0].strategyName === '手动做T', '候选含策略名(手动做T)');
|
|
|
|
// set-attribution 端点
|
|
storage.upsertTradeOrders([{ orderId: 'X-1', tradeDate: '20260901', code: '300057.SZ', insertDate: '20260901', insertTime: '093000' }]);
|
|
const h = storage.sqlite.db.prepare('SELECT holding_id FROM strategy_holdings WHERE strategy_id=?').get('manual-t');
|
|
const setRes = await handleTrade('orders/set-attribution', { orderId: 'X-1', strategyId: 'manual-t', holdingId: h.holding_id }, runtime);
|
|
assert(setRes.ok === true, 'set-attribution 端点返回 ok');
|
|
const row = storage.sqlite.db.prepare('SELECT strategy_id FROM trade_orders WHERE order_id=?').get('X-1');
|
|
assert(row.strategy_id === 'manual-t', '归属已持久化');
|
|
|
|
// trades/history 端点(策略过滤)
|
|
const hist = await handleTrade('trades/history', { start: '20260901', end: '20260901', strategyId: 'manual-t' }, runtime);
|
|
assert(hist.orders.length === 1 && hist.orders[0].orderId === 'X-1', 'history 端点按策略过滤命中');
|
|
|
|
storage.close();
|
|
rmSync(dir, { recursive: true, force: true });
|
|
console.log('\n结果: ' + pass + ' 通过, ' + fail + ' 失败');
|
|
process.exit(fail > 0 ? 1 : 0); |