FNOS-022 测试用例
| 项目 | 内容 |
|---|---|
| 需求编号 | FNOS-022 |
| 计划编号 | PLAN-FNOS-022 |
| 本轮覆盖功能 | FNOS-022-07、FNOS-022-08 |
| 测试状态 | 通过 |
测试范围与需求分析
被测对象是 @tnnevol/dsh-codebuddy 插件的出站请求改写与错误分类两部分,对应本需求的两项 P0 功能。需求中原有的「参考项目口径对齐」功能已因来源项目停止维护而取消,不在测试范围内。
覆盖的功能编号
| 功能 | 覆盖内容 |
|---|---|
| FNOS-022-07 | 出站前对系统提示开头的身份说明做单句最小改写;非句首与未命中时逐字不变;改写后语义与模型行为正常 |
| FNOS-022-08 | 内容策略拦截(业务码 11128)独立成码;不重试、不换号;与额度耗尽、请求非法、鉴权失败互不混淆 |
关键风险点
| 风险 | 对应的用例方向 |
|---|---|
| 改写范围过宽,误伤用户正文 | 非句首出现、用户消息内的同一句必须逐字不变 |
| 改写范围过窄,未覆盖真实请求路径 | options.system 与 session 内的系统消息两条路径都必须被改写 |
| 规则被改为子串替换,命中引用文本 | 缺少句末标点、产品名后加字两种形态必须不改写 |
| 重复请求反复改写 | 改写结果必须幂等 |
| 内容拦截被归到可重试集合,放大无效请求 | 可重试集合必须不含该码 |
内容拦截被归到 AUTH(403 与鉴权失败同码) | 业务码识别必须先于状态码分派 |
资料缺口
- ❓ 上游对「句首整句后接内容」的判定已实测(仍拦截),但对「同一句出现在后续系统消息开头」未实测;当前实现对每条系统消息各自改写,属防御性覆盖,无实测证据。
- ❓ 上游是否会扩展匹配到其它句子或改为语义判定,无法预期;已登记为计划风险一、风险四与风险五。
测试环境
| 功能 | 执行环境 | 说明 |
|---|---|---|
| FNOS-022-07 | DSH Web、DSH Desktop;本地包级单测 | 单测覆盖改写规则与接线;真实账号端到端已验证改写可解除拦截。真实会话走查已在 DSH Web 与 DSH Desktop 完成(见 TC-015 与执行说明)。 |
| FNOS-022-08 | DSH Web、DSH Desktop;本地包级单测 | 分类与集合归属由单测完整覆盖;真实拦截场景已用本地桩注入验证(见 TC-023)。 |
被测插件不依赖 fnOS 专属能力,不要求真实 NAS 验收。
测试用例
FNOS-022-07 CodeBuddy 模型恢复可用
| 用例 ID | 覆盖验收 | 用例 | 前置条件 | 预期结果 | 优先级 |
|---|---|---|---|---|---|
| TC-001 | FNOS-022-07-AC-03 | 句首命中改写 | 无 | 句首身份说明的 powered by 改为 powered via,其余内容逐字保留 | P0 |
| TC-002 | FNOS-022-07-AC-07 | 非句首出现不改写 | 无 | 前面加换行、加词、另有句子三种形态均逐字不变(同一字符串引用) | P0 |
| TC-003 | FNOS-022-07-AC-02 | 未命中返回原引用 | 无 | 普通文本与空串均返回原字符串 | P1 |
| TC-004 | FNOS-022-07-AC-02 | 整句精确匹配 | 无 | 缺句末标点、产品名后加字、措辞不同三种形态均不改写 | P0 |
| TC-005 | FNOS-022-07-AC-02 | 句首整句后接内容仍改写 | 无 | 句首整句后接其它文本时仍被改写(实测该形态同样被上游拦截) | P0 |
| TC-006 | FNOS-022-07-AC-02 | 只改第一处 | 同一句在文首与后文各出现一次 | 仅文首那处被改写,后文保持原样 | P1 |
| TC-007 | FNOS-022-07-AC-02 | 幂等 | 无 | 改写结果再次改写不再变化 | P1 |
| TC-008 | FNOS-022-07-AC-02 | 规则表结构约束 | 无 | 每条规则均句首锚定、无全局标志、无多行标志、替换串非空 | P1 |
| TC-009 | FNOS-022-07-AC-01 | 请求开头系统提示被改写 | 构造带身份说明的请求开头系统提示 | 发往上游的第一条系统消息开头已改写,后续内容不变 | P0 |
| TC-010 | FNOS-022-07-AC-01 | session 系统消息被改写 | 构造带身份说明的 session 系统消息 | 该系统消息被改写 | P0 |
| TC-011 | FNOS-022-07-AC-05 | 非白名单角色归一 | 构造 developer 角色消息 | 发出时角色为 system,且内容同样被改写 | P0 |
| TC-012 | FNOS-022-07-AC-07 | 用户正文不改 | 用户消息内容恰为身份说明 | 该消息逐字不变(上游只比对系统提示开头) | P1 |
| TC-013 | FNOS-022-07-AC-01、AC-06 | 真实完整系统提示端到端 | 真实 CodeBuddy 账号;真实约 19 KB 完整系统提示;插件真实请求头 | 原样发送被上游拦截(400/11128);经插件改写后返回成功,复测仍成功 | P0 |
| TC-014 | FNOS-022-07-AC-03 | 改写后模型行为正常 | 真实账号 | 普通问答正确作答、提供函数定义时正确发起工具调用、遵循中文指令精确输出 | P0 |
| TC-015 | FNOS-022-07-AC-01 | DSH Web 真实会话走查 | DSH Web(项目 .dsh profile)+ 真实账号 | 选中 CodeBuddy 模型发起对话,模型正常返回内容,不再出现「请求被拦截」 | P0 |
FNOS-022-08 内容拦截被独立识别
| 用例 ID | 覆盖验收 | 用例 | 前置条件 | 预期结果 | 优先级 |
|---|---|---|---|---|---|
| TC-016 | FNOS-022-08-AC-01 | 拦截归入独立错误码 | 构造业务码 11128 的错误体 | 返回 CONTENT_BLOCKED | P0 |
| TC-017 | FNOS-022-08-AC-01 | 两种状态码都识别 | 同上,状态码分别为 400 与 403 | 均返回 CONTENT_BLOCKED | P0 |
| TC-018 | FNOS-022-08-AC-01 | 嵌套信封识别 | 业务码分别位于扁平、error.code、error.data.code 三处 | 三种形态均返回 CONTENT_BLOCKED | P0 |
| TC-019 | FNOS-022-08-AC-01 | 不与其它错误混淆 | 同上错误体 | 不返回 INVALID_REQUEST、不返回 QUOTA;403 形态不返回 AUTH | P0 |
| TC-020 | FNOS-022-08-AC-02 | 不在可重试集合 | 读取本路由重试策略 | 可重试集合不含 CONTENT_BLOCKED | P0 |
| TC-021 | FNOS-022-08-AC-03 | 不触发换号 | 检查换号判定 | 换号只认 QUOTA 与 RATE_LIMIT,CONTENT_BLOCKED 不换号 | P0 |
| TC-022 | FNOS-022-08-AC-05 | 既有瞬时 400 行为不变 | 读取本路由重试策略 | INVALID_REQUEST 仍在可重试集合内 | P1 |
| TC-023 | FNOS-022-08-AC-02、AC-03 | DSH Web 真实拦截场景走查 | DSH Web + 真实账号;构造可触发拦截的请求 | 失败一次即返回,不出现多次重试,不切换到其它账号 | P1 |
测试执行结果
| 用例 | 环境 | 结果 | 日期 | 备注 |
|---|---|---|---|---|
| TC-001~TC-012 | 本地包级单测 | 通过 | 2026-10-21 | tests/outbound-mask.spec.ts、tests/serialize.spec.ts |
| TC-013 | 本地 + 真实账号(只读脚本,tmp/) | 通过 | 2026-10-21 | 原样发送 400/11128(341 ms);改写后 200(1917 ms);复测 200(1622 ms)。系统提示 19,496 字节 |
| TC-014 | 本地 + 真实账号(只读脚本,tmp/) | 通过 | 2026-10-21 | 问答「1+1等于2。」;工具调用发起 read_file;中文指令精确回「你好」 |
| TC-015 | DSH Web | 通过 | 2026-10-21 | 项目 .dsh profile,本地 DSH Web(8070);新建会话、模型为 CodeBuddy deepseek-v4.1-flash(选择器显示 x0.11 倍率小尾巴,确认由本插件提供);发送「只回答两个字:收到」→ 状态「已完成,用时 2 秒」,回复「收到」。会话记录 turn/end kind=completed provider=codebuddy model=deepseek-v4.1-flash。 |
| TC-016~TC-022 | 本地包级单测 | 通过 | 2026-10-21 | tests/error-envelope.spec.ts、tests/retry-policy.spec.ts |
| TC-023 | DSH Web(本地桩注入) | 通过 | 2026-10-21 | 用本地桩代理真实上游、仅让 /v2/chat/completions 返回 400 + 业务码 11128,并把活动账号 endpoint 临时指向该桩。发送一条消息后:界面「处理失败」+ 错误码 CONTENT_BLOCKED;会话记录 assistant/attempt = 1、retry 事件 0 条、turn/end kind=error error.code=CONTENT_BLOCKED;桩记录聊天请求 2 次 = 主请求 1 次 + 标题生成 1 次(session/title-llm-request 走同一账号),两次均落在同一账号的桩上,未切换到其它账号。实测后已恢复 endpoint 与自动切换设置。 |
汇总:17 条通过、0 条失败、0 条未执行。
切号前后对照(同一 DSH Web、同一模型、仅活动账号不同):
| 时间 | 活动账号 | 结果 |
|---|---|---|
| 12:30 | 账号 A(workbuddy) | turn/end kind=error provider=codebuddy error=AUTH:request illegal |
| 12:32 | 账号 B(cli) | turn/end kind=completed provider=codebuddy model=deepseek-v4.1-flash,界面「已完成,用时 2 秒」 |
前者是账号级受限(业务码 11140,与本次修复无关,计划风险六已登记);后者证明本插件在真实 DSH Web 会话中已可正常调用 CodeBuddy 模型。
DSH Desktop 走查(TC-014、TC-015 的 Desktop 侧):开发态 Desktop(上游源码检出 fork-pj/deepseek-harness,DSH_HOME 指向项目 .dsh,渲染进程 CDP 9333)——新建会话、模型 Deepseek-V4.1-Flash · x0.11、发送同一提示词 → 页面标题「要求仅回复收到」,状态「已完成,用时 1 秒」,回复「收到」,用量 16K tok · 缓存命中 0%、1 轮 1 步 · 29 tok/s。
测试数据清理:临时修改的活动账号 endpoint 已删除、environment 已恢复、自动切换已恢复开启;本地桩进程已停止、桩日志已删除;本地 DSH Web(8070)与开发态 Desktop 已停止;项目 .dsh 未残留测试账号改动。
补充证据(非用例):插件全量 73 个测试文件 933 条用例全绿;typecheck 与 build 通过;构建产物 lib/index.js 含改写逻辑,lib/client.js 不含(改写属宿主侧,符合预期)。
Bug 记录
| Bug | 描述 | 影响 | 状态 |
|---|---|---|---|
| BUG-01 | 首版用例把「句首整句后接内容」误列为「不该改写」 | 仅测试用例期望写错,实现行为正确(实测该形态同样被上游拦截,应当改写) | 已修复:拆为「整句精确匹配不改写」与「后接内容仍改写」两组用例 |
测试结论
FNOS-022-07 与 FNOS-022-08 验收通过:
- 改写可解除上游拦截——真实账号端到端确认(原样发送 400/11128、改写后 200),且 DSH Web 与 DSH Desktop 两个客户端均实测模型正常返回内容;
- 改写范围符合实测边界(句首锚定、整句匹配、非句首不改写),全量用例无回归;
- 内容拦截归入独立错误码
CONTENT_BLOCKED,本地桩注入下只失败一次、不重试、不换号。
17 条用例全部通过,0 条失败、0 条未执行。
遗留风险:
- 上游若把匹配扩展到其它句子或改为语义判定,单句改写会失效——失效时表现为一次明确失败(
CONTENT_BLOCKED),不会静默损坏。 - 存在与本次修复无关的账号级错误(业务码 11140 / HTTP 403「request illegal」):排查期间本地有账号出现该状态,需重新登录才能恢复,改写无法解决;已登记在计划风险十。