/** * Task #228 — PATCH /api/admin/evolution/networks/:id/strategy 入参校验单测。 * * 这一组测试专门覆盖 patchStrategy handler 中可被纯函数化的入参校验段 * (`validatePatchStrategyBody`),不依赖 Postgres / Express,跑得很快, * 用来回归 reviewer (Task #228 code review) 提出的 "PATCH 路由 * success + unknown_strategy 路径需要小测试覆盖" 高影响项。 * * 覆盖契约: * 1. 缺 strategy 字段 → 400 strategy_required * 2. strategy 字段非字符串 → 400 strategy_required * 3. strategy 字段空字符串 → 400 strategy_required * 4. 未注册的 strategy 名 → 400 unknown_strategy + 列出可选名 * 5. 已注册的 strategy 名 → ok=true, status=200, 回显 strategy * * 副作用段(requireNetwork → db.update → recordEvent)走另外的 e2e * 测试覆盖,避免单测里再起一遍 ephemeral schema。 */ import { test } from "node:test"; import assert from "node:assert/strict"; process.env.DATABASE_URL ??= "postgres://test:test@127.0.0.1:5432/test"; const { validatePatchStrategyBody } = await import("../evolutionFlywheel"); const { listEvolutionStrategyNames, DEFAULT_EVOLUTION_STRATEGY } = await import( "../../lib/evolution/strategies" ); test("validatePatchStrategyBody: 缺 strategy 字段 → 400 strategy_required", () => { const r = validatePatchStrategyBody({}); assert.equal(r.ok, false); assert.equal(r.status, 400); assert.equal(r.body.error, "strategy_required"); assert.equal(r.body.strategy, undefined); assert.equal(r.body.available, undefined); }); test("validatePatchStrategyBody: body 为 null → 400 strategy_required", () => { const r = validatePatchStrategyBody(null); assert.equal(r.ok, false); assert.equal(r.status, 400); assert.equal(r.body.error, "strategy_required"); }); test("validatePatchStrategyBody: strategy 非字符串 → 400 strategy_required", () => { for (const bad of [42, true, [], {}, { foo: "bar" }]) { const r = validatePatchStrategyBody({ strategy: bad }); assert.equal(r.ok, false, `strategy=${JSON.stringify(bad)}`); assert.equal(r.status, 400); assert.equal(r.body.error, "strategy_required"); } }); test("validatePatchStrategyBody: 空字符串 → 400 strategy_required", () => { const r = validatePatchStrategyBody({ strategy: "" }); assert.equal(r.ok, false); assert.equal(r.status, 400); assert.equal(r.body.error, "strategy_required"); }); test("validatePatchStrategyBody: 未注册策略名 → 400 unknown_strategy + 列出可选名", () => { const r = validatePatchStrategyBody({ strategy: "definitely_not_registered" }); assert.equal(r.ok, false); assert.equal(r.status, 400); assert.equal(r.body.error, "unknown_strategy"); // 列表必须非空且与注册表一致,前端要据此渲染下拉。 assert.ok(Array.isArray(r.body.available)); assert.deepEqual(r.body.available, listEvolutionStrategyNames()); assert.ok((r.body.available as string[]).length > 0); // 默认策略名永远在列表里(回退路径的兜底实例)。 assert.ok((r.body.available as string[]).includes(DEFAULT_EVOLUTION_STRATEGY)); }); test("validatePatchStrategyBody: 已注册策略名 → ok=true, 回显 strategy", () => { // hyperparameter_grid (默认) 与 fitness_guided 都已在 Task #228 注册。 for (const name of ["hyperparameter_grid", "fitness_guided"]) { const r = validatePatchStrategyBody({ strategy: name }); assert.equal(r.ok, true, `strategy=${name}`); assert.equal(r.status, 200); assert.equal(r.body.strategy, name); assert.equal(r.body.error, undefined); assert.equal(r.body.available, undefined); } }); test("validatePatchStrategyBody: 未注册名 + 默认策略名永远可用作后续切换", () => { // 这条测试锚定 reviewer 提到的 "未知名安全回退" 语义在 admin 层的体现: // 即便用户写了未知名, 我们至少能把回退目标 (DEFAULT_EVOLUTION_STRATEGY) // 列在 available 里,前端可一键切回安全态。 const r = validatePatchStrategyBody({ strategy: "unknown_xyz" }); assert.equal(r.ok, false); const available = r.body.available as string[]; assert.ok(available.includes(DEFAULT_EVOLUTION_STRATEGY)); });