Skip to content

FNOS-010 测试用例 ​

本文件记录 FNOS-010(三方搜索接入与官方兜底)的测试用例定义、执行结果、发现的 Bug 与最终结论,对应计划 PLAN-FNOS-010。用例编号、格式与回填规则遵循测试用例文档规范。

测试范围与需求分析 ​

被测对象:新增 DSH 插件 @tnnevol/dsh-failover-search(目录 plugins/dsh-failover-search-plugin),以复合搜索提供方接管会话内网页搜索,按 TinyFish → Tavily → 官方搜索顺序转移,支持同平台多账号均摊、用量快照展示与请求前探测。

覆盖的功能编号:FNOS-010-01 至 FNOS-010-10。

关键风险点:

  • 转移语义:接缝只支持「配置固定提供方」,多级兜底必须在插件内部实现;顺序、跳过与终止条件写错会让搜索静默失败或产生重复请求。
  • 账号均摊与探测:均摊游标与探测排除共同决定「这次请求打给谁」;探测误判会让可用账号被跳过,或让已触限账号反复被请求。
  • 凭据红线:key 是敏感凭据,且配置表单需要承载它;明文一旦进入会话记录、错误信息或日志即违规。
  • 上游契约漂移:两个三方服务的响应字段与日期格式不是稳定契约(实测已发现 url 会返回相对重定向包装)。

资料不足处:❓ 无。需求与计划中的待核实项已在实现期用真实调用核实,结论记录在下面各用例的结果列与本文件「Bug 记录」中。

测试环境 ​

功能目标环境说明
FNOS-010-01、02、03、05、07、08、09DSH Web;DSH Desktop依赖真实网络与真实三方账号;Desktop 结果见「测试执行结果」的缺口说明
FNOS-010-04、06、10DSH Web;DSH Desktop插件详情页配置区与组合行行为
文档与发布清单文档站构建环境pnpm run check -- --docs、pnpm run build -- --docs

本插件不依赖 fnOS 宿主能力(授权目录、网关、生命周期),按需求「验收环境范围」不要求真实 NAS 验收。

版本三元组(2026-10-09 本次执行):

环境版本
DSH 运行时0.2.0-rc.2(仓库根 @deepseek-ai/dsh,profile web)
插件构建版本@tnnevol/dsh-failover-search@0.2.0-rc.2.1
本地 Web DSH_HOME<repo>/.dsh(端口 8070)
三方账号TinyFish 2 个真实 key;Tavily 2 个真实 key
DSH Desktopfork-pj/deepseek-harness 检出 639ed01539(0.2.0-rc.2),开发态构建标识 0.2.0-rc.2-639ed01,Electron 44.0.0 / Chrome 152;经 DSH_DESKTOP_RENDERER_DEBUG_PORT=9333 的 CDP 驱动,DSH_HOME 指向项目根 .dsh

测试用例 ​

FNOS-010-01 三方搜索优先生效 ​

用例覆盖前置条件步骤预期优先级
TC-001FNOS-010-01-AC-01仅配置 TinyFish key,真实网络发起一次搜索结果由 TinyFish 返回;官方搜索未被调用P0
TC-002FNOS-010-01-AC-02同时配置两个三方 key发起一次搜索结果来自顺序最高且可用的来源(TinyFish)P0

FNOS-010-02 来源间自动故障转移 ​

用例覆盖前置条件步骤预期优先级
TC-003FNOS-010-02-AC-01TinyFish key 无效,Tavily 有效发起一次搜索同一查询自动改用 Tavily 完成,模型拿到正常结果P0
TC-004FNOS-010-02-AC-02仅配置 Tavily key发起一次搜索TinyFish 被直接跳过,无请求、无等待P0
TC-005FNOS-010-02-AC-03两个三方 key 均无效,官方兜底也失败发起一次搜索抛出带错误码的失败信息,不出现空结果或挂起P0
TC-006需求行为约束任意来源搜索过程中取消抛 WEB_ABORTED,不触发向下一来源转移P0

FNOS-010-03 官方搜索兜底 ​

用例覆盖前置条件步骤预期优先级
TC-007FNOS-010-03-AC-01不配置任何三方 key发起一次搜索直接使用官方搜索,行为与现版本一致P0
TC-008FNOS-010-03-AC-02三方全部失败;官方无凭据发起一次搜索回落官方并保留 WEB_PROVIDER_CREDENTIAL_MISSING 语义P0
TC-009FNOS-010-03(配置沿用)用户改过官方搜索端点/模型触发官方兜底兜底级沿用用户既有设置,不使用包内常量P1

FNOS-010-04 搜索来源与多账号配置管理 ​

用例覆盖前置条件步骤预期优先级
TC-010FNOS-010-04-AC-01本地 DSH Web,插件已加载在详情页添加账号 key 与备注名账号写入配置并持久化;key 不回显P1
TC-011FNOS-010-04-AC-02同上修改超时、切换开关并保存写入生效,保存后下一次搜索按新配置执行P1
TC-012FNOS-010-04-AC-03同上打开「设置」弹框不出现本插件配置入口;配置只在插件详情页P1
TC-013FNOS-010-04-AC-04同上检查配置区承载方式标量字段用官方表单;官方控件表达不了的交互自绘且风格一致P1
TC-014FNOS-010-04-AC-07文档站核对文档配置项表与表单名称、说明、取值范围、默认值逐项一致P1

FNOS-010-05 搜索结果呈现兼容 ​

用例覆盖前置条件步骤预期优先级
TC-015FNOS-010-05-AC-01真实 key检查归一后的来源字段标题、链接、摘要齐全;日期有则合法 ISO、无则缺席P1
TC-016FNOS-010-05-AC-01固定样本解析相对时间/英文日期/RFC2822/不可解析取值可解析的转 ISO;不可解析的省略 publishedAtP1
TC-017FNOS-010-05-AC-02真实 key以 maxResults 请求来源侧返回完整结果,截断由接缝按上限执行P1
TC-018FNOS-010-05-AC-01/02本地 DSH Web会话内搜索卡片走查(亮/暗主题)卡片呈现与官方来源一致;截断时模型可见提示P1

FNOS-010-06 禁用/卸载后恢复官方搜索 ​

用例覆盖前置条件步骤预期优先级
TC-019FNOS-010-06-AC-01组合已接管搜索检查组合与官方行官方搜索行保持原样、未被禁用(随插件失效即回落)P1
TC-020FNOS-010-06-AC-01本地 DSH Web禁用插件后检查组合行web 行回落 base 层的官方搜索配置,无需手工清理P1

FNOS-010-07 同平台多账号均摊 ​

用例覆盖前置条件步骤预期优先级
TC-021FNOS-010-07-AC-01单测:3 个账号连续 6 次搜索请求在账号间轮转,分布均匀P0
TC-022FNOS-010-07-AC-01真实网络 + 同平台两个真实账号连续多次搜索,用平台侧用量数据核对请求分摊到各账号,无单一账号被持续集中消耗P0
TC-023FNOS-010-07-AC-02单测:账号 A 失败、账号 B 可用发起一次搜索先试同平台账号 B;平台顺序不乱序P0
TC-024FNOS-010-07(退化)单测:单账号连续两次搜索退化为直连,行为与无均摊一致P1

FNOS-010-08 平台用量展示 ​

用例覆盖前置条件步骤预期优先级
TC-025FNOS-010-08-AC-01真实 Tavily key查询用量端点展示 key 用量与账户套餐已用/上限、套餐名称P1
TC-026FNOS-010-08-AC-02真实 TinyFish key查询钱包端点展示钱包余额与自动充值状态P1
TC-027FNOS-010-08-AC-03端点返回失败查看展示区该账号显示可理解的失败提示;搜索不受影响P1
TC-028FNOS-010-08-AC-01/02本地 DSH Web详情页用量区块走查(亮/暗主题)展示值与平台端点一致,带获取时间P1

FNOS-010-09 用量驱动的请求前探测 ​

用例覆盖前置条件步骤预期优先级
TC-029FNOS-010-09-AC-01单测:快照标记账号已满发起一次搜索该账号请求前被跳过,不发出注定失败的请求P1
TC-030FNOS-010-09-AC-02单测:快照过期 / 无快照发起一次搜索不排除账号,搜索正常发起P1
TC-031FNOS-010-09-AC-03单测:快照由满恢复为可用先后各发起一次搜索恢复后的账号重新参与均摊P1
TC-032需求行为约束单测:探测关闭发起一次搜索即使快照标记已满也不跳过P1

FNOS-010-10 插件标识命名 ​

用例覆盖前置条件步骤预期优先级
TC-033FNOS-010-10-AC-01构建产物与源码核对命名定义各标识符包名、目录、显示名、行 id、提供方 id、配置命名空间与计划「命名定义」一致P1
TC-034FNOS-010-10-AC-02本地 DSH Web 与文档站核对三处插件名称插件管理页、文档站、发布清单名称一致P1

客户端兼容回归(DSH Web 与 DSH Desktop) ​

需求要求 FNOS-010-01 至 FNOS-010-11 在两个客户端验收。本组用例覆盖最近三轮改动(抓取兜底、座位迁移、刷新入口)在 DSH Desktop 上的回归。

用例覆盖前置条件步骤预期优先级
TC-061FNOS-010-04-AC-04 / FNOS-010-08-AC-05DSH Desktop(fork 检出开发态)已打开本插件详情页核对区块顺序、刷新入口与配置区顺序为「包含的组件 → 平台账号 → … → 平台用量」;刷新入口存在且带可访问名称;配置区渲染、账号读出、保存按钮在位——与 DSH Web 逐项一致P0
TC-062FNOS-010-08-AC-04同上,快照就绪点击刷新并观察按钮状态与获取时间点击中禁用且文案「刷新中」;完成后获取时间更新——Desktop 的 RPC 链路同样承载刷新端点P0
TC-063FNOS-010-11-AC-01/AC-03DSH Desktop 会话 + 三方账号,插件 debug 日志会话内让模型 web_fetch 抓一个本地被拒的 URL日志级证据 local failed → tinyfish succeeded;模型拿到 HTTP 200 正文并正确总结(兜底在 Desktop 同样生效)P0
TC-064回归(Desktop 搜索链路)同上会话内发起一次搜索搜索仍走三方(日志 tinyfish/tinyfish-1 succeeded),新版 fetch provider 未干扰搜索链路P0

端到端全流程(搜索 → 网页内容获取) ​

真实使用是串联的:先搜索、拿到来源、再读某一篇全文(web_search 的结果里带 Follow up with web_fetch ... 引导)。既有 smoke 只测搜索、fetch 脚本只测抓取,都不覆盖串联——本组补齐。

用例站点类型覆盖前置条件步骤预期(可观察)优先级
TC-081包管理站(npm)FNOS-010-01 + 010-11三方 key 已配置1.搜索 npm 上的包 2.从返回的来源里挑一条 3.取回其正文搜索有来源(≥1 条);来源 URL 可被取回正文(>200 字符);搜索与抓取分别有成功的 attempt/hop 记录P0
TC-082代码托管站(GitHub)同上同上同上,目标是 GitHub 仓库同上;站点类型不影响链路可完成性P0
TC-083npm + GitHub同上(会话内)DSH Web 会话 + debug 日志会话内让模型「搜索 X,然后 fetch 选中的页面并总结」日志同时出现搜索成功与抓取降级记录(local failed → tinyfish succeeded)且抓取的 URL 来自搜索结果;模型回答含只可能来自页面正文的细节P0
TC-084同上FNOS-010-12-AC-03(关闭态对照)同上,开关关闭关闭开关后重复 TC-083三方零调用(平台计数不增、日志无 tinyfish succeeded);模型仍可工作(走官方路径)P0

FNOS-010-12 会话级「智能搜索」开关 ​

功能拆解(按测试架构师视角):

text
智能搜索开关
├─ 控件层(conversation.input.left 座位)
│  ├─ 控件存在性与位置(消息框左下角)
│  ├─ 可访问名称与开/关状态可辨
│  └─ 默认状态 = 开启
├─ 状态层(会话级)
│  ├─ 按会话独立(A 关闭不影响 B)
│  ├─ 切换即时生效(无需重启/重开会话)
│  └─ 不写入全局插件配置(无跨会话串扰)
├─ 门控层(host,tools/pre-execute)
│  ├─ 开启:行为与引入前逐项一致
│  └─ 关闭:三方零调用(账号/配额零消耗)、回落官方
└─ 兼容层
   ├─ DSH Web 与 DSH Desktop 行为一致
   └─ 插件禁用/卸载后控件不残留
用例覆盖分类前置条件步骤预期(可观察)优先级
TC-081npm 全流程真实网络(脚本)通过2026-10-10搜索返回 10 条来源;挑中的来源 https://www.npmjs.com/package/@deepseek-ai/dsh 取回 6641 字符正文;日志 tinyfish/tinyfish-1 succeeded + hop local failed (0ms) → hop tinyfish succeeded (986ms)
TC-082GitHub 全流程真实网络(脚本)通过2026-10-10搜索返回 7 条来源;https://github.com/deepseek-ai/deepseek-harness 取回 2597 字符;抓取降级记录同 TC-081
TC-083npm 会话内全流程DSH Web + debug 日志通过2026-10-10模型自主完成「搜索 → 挑 URL → 取正文」:搜索 tinyfish/tinyfish-1 succeeded (1469ms),抓取 local failed (17ms) → tinyfish succeeded (1232ms),抓取 URL 正是搜索结果里的 npm 包页。回答含只可能来自页面的细节:最新版本 0.2.0-rc.2、约 7 天前发布、「一切皆插件、以 profile 启动」、web/headless/sdk/sdk-minimal/acp 五种入口模式、dsh plugin 转发 pnpm、peer 版本校验
TC-083bGitHub 会话内全流程DSH Web + debug 日志通过2026-10-10模型读 GitHub 仓库后自行判断 README 未含目录树、追加用 GitHub API 补拉(api.github.com/repos/.../contents/,local failed (12ms) → tinyfish succeeded (1440ms));回答列出真实目录 packages/、apps/、python/、native/、examples/、docs/、website/、scripts/、patches/——含 README 之外的 API 内容
TC-084关闭态对照平台计数 + debug 日志通过2026-10-10关闭开关后做同样的事:TinyFish 平台计数 101 → 101(未增加);抓取日志只有 local failed(对 3 个 URL:GitHub API、raw README、npm registry)无任何 tinyfish succeeded;搜索无三方 attempt——门控对搜索与抓取两条链路同时生效
TC-069FNOS-010-12-AC-01正向插件已启用,打开任一会话1.观察消息框左下角 2.核对控件文案与初始状态出现「智能搜索」开关;初始为开启态;控件在「深度思考」同一行区域、位于消息框左下P0
TC-070FNOS-010-12-AC-02正向(基线一致)开关开启,已配三方 key1.发起一次搜索 2.查看插件 debug 日志走三方:日志 [failover-search] tinyfish/tinyfish-1 succeeded;与开关引入前逐项一致(基线:TC-001/TC-052 同特征)P0
TC-071FNOS-010-12-AC-03逆向(核心)开关关闭,已配三方 key1.发起一次搜索 2.核对 TinyFish 请求日志条数 3.查看插件日志三方零调用(TinyFish 日志 total 不增加、无 [failover-search] 尝试记录);结果由官方返回或按官方语义提示;配额零消耗P0
TC-072FNOS-010-12-AC-03逆向开关关闭,已配三方 key1.发起一次抓取(web_fetch)2.查看逐跳日志不走三方兜底(无 [failover-search:fetch] 事件);按官方本地抓取行为返回或报错P0
TC-073FNOS-010-12-AC-04正向(会话隔离)两个会话 A、B1.A 关闭开关 2.切到 B(默认开)3.分别搜索 4.切回 A 再搜索A 走官方(三方零调用);B 仍走三方;A 的状态在切换后保持关闭——互不影响P0
TC-074FNOS-010-12-AC-04边界(即时生效)会话内开关为开启1.发起搜索(走三方)2.关闭开关 3.不重开会话立即再搜索第二次搜索不再走三方——切换对下一次搜索立即生效,无需重启P0
TC-075FNOS-010-12-AC-04数据一致性会话内切换开关若干次1.关闭→搜索→开启→搜索→关闭 2.核对全局配置与其它会话全局插件配置未被改写(cordis.patch.yml 无开关字段变更);其它会话行为不受影响——无跨会话串扰P0
TC-076FNOS-010-12-AC-01边界(默认态)全新会话(首次使用)打开一个此前未使用过的新会话开关为开启(默认)——不是沿用其它会话的状态,而是按「默认开启」初始化P1
TC-077FNOS-010-12-AC-01边界(快速切换)会话内开关可用连续快速点击开关 5 次(奇数次后为关)最终状态与点击次数一致(无状态错乱/竞态);下一次搜索行为与最终状态一致P2
TC-078FNOS-010-12-AC-05兼容性同一部署在 DSH Web 与 DSH Desktop 分别打开会话操作开关两客户端控件形态与行为一致(位置、文案、默认态、切换效果);均能正确门控P1
TC-079FNOS-010-12-AC-05权限/生命周期插件被禁用或卸载1.禁用插件 2.重开会话界面「智能搜索」控件不出现(界面无残留);搜索按无插件时的官方行为执行P1
TC-080FNOS-010-12-AC-01易用性(A11y)控件渲染1.核对可访问名称 2.用键盘 Tab 到达并切换控件带可访问名称;键盘可达且可用 Enter/Space 切换(A11y:不能只能鼠标点)P2

站点类型覆盖(FNOS-010-11 兜底的普适性) ​

抓取难度随站点类型差异很大(静态文档站、JS 重度代码托管站、纯文本 raw 资源),单一站点不足以说明兜底能力。验证脚本支持 TARGET_URL 覆盖目标,便于按站点类型重复验证。

用例站点类型覆盖前置条件步骤预期优先级
TC-065代码托管站(HTML,JS 重度)FNOS-010-11-AC-01/02复合 provider + 真实 key以 GitHub 仓库页为目标跑兜底验证本地失败后 TinyFish 与 Tavily 均取回正文;两平台都不可用时保留本地错误码P1
TC-066代码托管站(会话内)FNOS-010-11-AC-03DSH Desktop 会话 + debug 日志会话内让模型 web_fetch 抓 GitHub 仓库页日志 local failed → tinyfish succeeded;模型读到 README 实质内容(项目定位、架构、启动方式),非泛泛而谈P1
TC-067纯文本 raw 资源FNOS-010-11-AC-01/02同上以 raw.githubusercontent.com 的 README 为目标两平台均取回纯文本正文(本地 provider 本身支持 text/plain)P2
TC-068站点内容质量对照FNOS-010-11-AC-02(次序依据)同上对同一站点比对两平台返回TinyFish 返回干净正文;Tavily 返回整页转换(含导航样板与大量链接)——为「TinyFish 优先」提供事实依据P2

FNOS-010-08 用量展示补充(手动刷新) ​

用例覆盖前置条件步骤预期优先级
TC-069FNOS-010-12-AC-01DSH Web通过2026-10-10控件存在(.dsh-failover-toggle)、文案「智能搜索」、可访问名称「智能搜索:已开启,点击关闭」、默认开启(semi-switch-checked);位于消息框左下角
TC-070FNOS-010-12-AC-02DSH Web + 平台日志通过2026-10-10开启状态下搜索走三方:插件日志 tinyfish/tinyfish-1 succeeded,TinyFish 平台计数 93 → 95(+2,两次搜索)——行为与开关引入前一致
TC-071FNOS-010-12-AC-03DSH Web + 平台日志通过2026-10-10关闭状态下三方零调用:TinyFish 平台计数 93 → 93(未增加),插件尝试记录只有 deepseek-official、无任何三方平台——回落官方
TC-072FNOS-010-12-AC-03单测通过2026-10-10关闭时会话的抓取只用本地跳、平台跳零调用;开启时仍走平台兜底(regression.spec.ts 两条)
TC-073FNOS-010-12-AC-04单测通过2026-10-10会话隔离:A 设为关闭后 isEnabled('A')=false 而 B/C 仍为 true;无会话身份时按默认开启(保守不绕过)
TC-074FNOS-010-12-AC-04DSH Web 真实会话通过2026-10-10同一会话内切换、无需重启:关闭后搜索三方计数不变(93),重新开启后同一会话再搜索三方计数 93 → 95——切换对下一次搜索立即生效
TC-075FNOS-010-12-AC-04单测 + 配置核对通过2026-10-10同一会话反复切换无副作用;状态存在宿主内存 Map、不写 cordis.patch.yml(实测走查后配置文件无开关字段)——无跨会话串扰
TC-076FNOS-010-12-AC-01单测 + DSH Web通过2026-10-10全新会话首次打开为开启(store 未记录的会话一律返回默认值,实测新会话即 semi-switch-checked)
TC-077FNOS-010-12-AC-01单测通过2026-10-10快速切换:同值重复设置不写 Map、不通知订阅者(订阅序列断言 a:false → b:false → a:true,无重复通知)
TC-080FNOS-010-12-AC-01源码契约通过2026-10-10控件 aria-label 取自动态可访问名称(含功能与状态);标签支持 Enter/Space 键触发(键盘可达);含 :focus-visible 焦点环
TC-065GitHub 站点类型真实网络通过2026-10-10仓库页 4/4:TinyFish 2597 字符、Tavily 53091 字符(含 11 处导航样板、375 个链接);issues 页 4/4:2526 / 8361 字符。两平台的取回均成功、失败语义正确
TC-066GitHub 站点类型(会话内)DSH Desktop + debug 日志通过2026-10-10日志取证 [failover-search:fetch] local failed (5ms) → tinyfish succeeded (1178ms);模型总结读到 README 实质内容(「一切皆插件」理念、Cordis 架构、开发者预览阶段、npx @deepseek-ai/dsh web 启动方式、社区入口)——只可能来自真实抓取
TC-067纯文本 raw 资源真实网络通过2026-10-10raw.githubusercontent.com/.../master/README.md 4/4:TinyFish 2601 字符、Tavily 2634 字符。过程记录:首次误用不存在的 main 分支(实际为 master),该 URL 本身 404,两平台对 404 空正文均报「empty content」——这是正确的失败处理,不是能力缺陷;换有效 URL 后正常
TC-068内容质量对照真实网络通过2026-10-10同站点对照:TinyFish 2597 字符 / 0 处导航样板 / 0 个多余链接;Tavily 53091 字符 / 11 处导航样板 / 375 个链接。TinyFish 返回干净正文而 Tavily 返回整页转换——为需求里「TinyFish Fetch 优先」的排序提供了实测依据
TC-061客户端兼容回归DSH Desktop通过2026-10-10Desktop(fork-pj/deepseek-harness 开发态,profile 指向项目 .dsh/profiles/desktop)实测:区块文本索引 395 < 568 < 967(顺序与 Web 一致);刷新入口存在且 aria-label="刷新";配置区渲染、1 个账号读出、保存按钮在位
TC-062客户端兼容回归DSH Desktop通过2026-10-10点击前 12:26:05 → 点击中 disabled=true 且文案「刷新中」→ 完成后 12:26:44;状态序列仅 true|刷新中 → false|刷新。Desktop 的 connection 链路同样承载新增的 refresh 端点
TC-063客户端兼容回归DSH Desktop + debug 日志通过2026-10-10日志级取证:[failover-search:fetch] local failed (22ms) → tinyfish succeeded (1537ms);模型回答正确总结 Tavily Extract API 文档(含 POST /extract、basic/advanced、results/failed_results 结构),主对话区无错误
TC-064客户端兼容回归DSH Desktop + debug 日志通过2026-10-10搜索侧日志 tinyfish/tinyfish-1 succeeded (1870ms) 与 (2001ms) 各一次——搜索仍走三方,新版 fetch provider 未干扰搜索链路
TC-058FNOS-010-08-AC-05插件详情页已打开核对用量区块的交互控件区块含「平台用量」标题与手动刷新入口;入口带可访问名称(aria-label 取自动态文案键);入口在加载、就绪、失败三种状态下都渲染P0
TC-059FNOS-010-08-AC-04已配置三方账号,快照就绪1.记录当前获取时间 2.点击刷新 3.观察按钮状态 4.等待完成点击后立即重查两个平台端点,获取时间更新为当前时刻;刷新期间入口禁用且文案变「刷新中」(不可重复触发);完成后回到「刷新」可点击P0
TC-060FNOS-010-08-AC-04刷新端点与只读端点分离1.检查契约常量 2.检查宿主 handlerusage 端点保持只读(读取不触发平台请求);refresh 是独立端点且复用后台的 refreshUsage(单个账号失败不使整次刷新失败)P1

FNOS-010-11 网页抓取三方兜底 ​

用例覆盖前置条件步骤预期优先级
TC-048FNOS-010-11-AC-01复合 provider + 真实 key1.本地跳抛错 2.调用 fetch转 TinyFish Fetch 取回正文(真实网络 ≥ 500 字符);本地成功时不产生任何平台调用P0
TC-049FNOS-010-11-AC-03DSH Web 会话 + 三方 key会话内让模型 web_fetch 抓一个本地被拒的 URL兜底取回正文、模型正常总结;调用方式与返回结构对模型透明P1
TC-050FNOS-010-11-AC-02复合 provider + 真实 keyTinyFish 不可用/失败后调用兜底落到 Tavily Extract;两平台都不可用时保留本地错误码(不静默、不挂起)P0
TC-055FNOS-010-11-AC-01复合 provider平台返回超长/空正文超长正文按平台原样返回(不额外截断);空正文判为失败并抛可读错误P2
TC-056FNOS-010-11-AC-03复合 provider调用中 abort立即中断整条链路,不再尝试后续跳;抛 WEB_ABORTEDP1
TC-057FNOS-010-11-AC-04契约 + 单测核对抓取与搜索的独立性与账号复用两能力面注册进各自 store(同 id 不冲突);账号读同一份 010-04 配置,无需重复填写;抓取失败不影响搜索P0

回归用例(本轮改动后新增) ​

本轮三项改动均属规范定义的「回归验证类」(新增 provider、迁移座位、重写组合行),因此单列回归用例并标注基线来源。

用例覆盖基线来源前置条件步骤预期(含可观察特征)优先级
TC-052抓取兜底不破坏搜索链路基线:dsh-v0.2.0-rc.2 默认行为 + FNOS-010-01 既有实现三方 key 已配置,复合 search 与 fetch 同时注册1.连续发起 3 次搜索 2.期间发起 2 次抓取 3.检查两侧日志搜索仍走三方:TinyFish 请求日志新增 3 条对应 query 且 result_count=10;抓取按本地→平台降级;无 WEB_DUPLICATE_PROVIDER 或 provider 解析异常;搜索延迟与改动前同量级P0
TC-053座位迁移后配置读写回归基线:FNOS-010-04 迁移前行为(bundle.config 座位)DSH Web 打开插件详情页1.新增账号 2.改备注名 3.删除账号 4.改超时 5.切换开关与迁移前逐项一致:账号落盘 cordis.patch.yml、掩码正确显示、保存按钮状态随草稿变化、开关即时生效;区块顺序为「包含的组件 → 平台账号 → …」P0
TC-054fetchProvider 接管后本地抓取语义回归基线:dsh-v0.2.0-rc.2 dsh-web-fetch-http 默认行为复合 fetch provider 已注册1.构造本地跳成功 2.核对 limits 3.对非 2xx 响应观察结果类型本地成功时零平台调用;limits 为官方默认(maxResponseBytes=5e6/maxBodyChars=1e5/timeoutMs=3e4/maxRedirects=5 + 官方 UA);非 2xx 仍作为 WebFetchResult 返回(statusCode 如实透出)而非抛错P0

测试执行结果 ​

自动化用例(包级,可重复执行) ​

命令:pnpm --filter @tnnevol/dsh-failover-search run test(vitest run)。113 个用例全部通过。

用例覆盖执行环境结果日期备注
TC-001FNOS-010-01-AC-01单元(mock fetch)通过2026-10-09仅 TinyFish 时官方零调用
TC-002FNOS-010-01-AC-02单元通过2026-10-09顺序最高来源胜出
TC-003FNOS-010-02-AC-01单元通过2026-10-09失败后转移,失败原因逐级记录
TC-004FNOS-010-02-AC-02单元通过2026-10-09未配置平台零请求
TC-005FNOS-010-02-AC-03单元通过2026-10-09抛带码错误,不返回空结果
TC-006行为约束单元通过2026-10-09中止直抛,不转移
TC-007FNOS-010-03-AC-01单元通过2026-10-09无三方 key 时直用官方
TC-008FNOS-010-03-AC-02单元通过2026-10-09保留 WEB_PROVIDER_CREDENTIAL_MISSING
TC-009FNOS-010-03单元通过2026-10-09读取用户既有官方设置,缺失回落默认值
TC-015FNOS-010-05-AC-01单元通过2026-10-09字段映射与相对重定向 URL 还原
TC-016FNOS-010-05-AC-01单元通过2026-10-09相对时间/英文日期/RFC2822/不可解析
TC-017FNOS-010-05-AC-02单元通过2026-10-09来源侧不裁剪,接缝负责截断
TC-019FNOS-010-06-AC-01契约(patch 文件)通过2026-10-09官方行未被禁用;fetchProvider 完整重述
TC-021FNOS-010-07-AC-01单元通过2026-10-096 次请求在 3 账号间轮转
TC-023FNOS-010-07-AC-02单元通过2026-10-09同平台优先,平台顺序不乱序
TC-024FNOS-010-07 退化单元通过2026-10-09单账号直连
TC-029FNOS-010-09-AC-01单元通过2026-10-09已满账号零请求
TC-030FNOS-010-09-AC-02单元通过2026-10-09过期/缺失快照不排除账号
TC-031FNOS-010-09-AC-03单元通过2026-10-09恢复后重新参与
TC-032行为约束单元通过2026-10-09探测关闭时不跳过
TC-033FNOS-010-10-AC-01契约(源码 + 产物)通过2026-10-09命名定义一致;产物导出运行时 Config

真实网络用例(真实 key) ​

命令:DSH_TINYFISH_KEYS=… DSH_TAVILY_KEYS=… pnpm tsx scripts/smoke.ts(9/9 通过)与 DSH_HOME=<repo>/.dsh … pnpm tsx scripts/e2e.ts(8/8 通过)。

用例覆盖执行环境结果日期备注
TC-001FNOS-010-01-AC-01真实网络通过2026-10-09TinyFish 返回 10 条,官方调用 0 次
TC-002FNOS-010-01-AC-02真实网络通过2026-10-09首个尝试来源 tinyfish
TC-003FNOS-010-02-AC-01真实网络通过2026-10-09tinyfish:failed → tavily:succeeded
TC-004FNOS-010-02-AC-02真实网络通过2026-10-09尝试序列仅 tavily
TC-007FNOS-010-03-AC-01真实网络通过2026-10-09无三方 key 时官方兜底
TC-009FNOS-010-03真实网络通过2026-10-09兜底级读用户官方设置(端点/模型/凭据引用)
TC-015FNOS-010-05-AC-01真实网络通过2026-10-0910 条均有 URL/标题/摘要;本次查询无 date,字段缺席而非乱码
TC-017FNOS-010-05-AC-02真实网络通过2026-10-09请求 maxResults=2,来源侧返回 10 条(截断归接缝)
TC-019FNOS-010-06-AC-01真实组合通过2026-10-09--dump-config:web 行指向本插件且 fetchProvider: http
TC-025FNOS-010-08-AC-01真实网络通过2026-10-09两个 Tavily 账号独立查询:套餐均为 Researcher,planUsage 分别为 0 / 1000 与 6 / 1000(计数器独立,证明分属不同账户)
TC-026FNOS-010-08-AC-02真实网络通过2026-10-09TinyFish 钱包余额 11.6 USD、自动充值 unconfigured
TC-029FNOS-010-09-AC-01真实网络通过2026-10-09两个真实账号下,快照标记已满的账号被跳过,请求落到另一账号
TC-022FNOS-010-07-AC-01真实网络 + 同平台两个真实账号(TinyFish 与 Tavily 各 2 个)通过2026-10-09TinyFish 连续 4 次请求分布 tinyfish-1, tinyfish-2, tinyfish-1, tinyfish-2(TinyFish 限流按 key 计,均摊效果可核对);Tavily 侧因用量计数非实时(见 BUG-08)无法用计数归因,两 key 是否同账户未核实

DSH Web UI 走查(本地 DSH_HOME=<repo>/.dsh,端口 8070,亮色主题) ​

用例覆盖执行环境结果日期备注
TC-010FNOS-010-04-AC-01DSH Web通过2026-10-09添加账号后写入 cordis.patch.yml;key 仅显示「key 已保存」,不回显
TC-011FNOS-010-04-AC-02DSH Web通过2026-10-09超时 15000→20000 保存后持久化;开关切换持久化
TC-012FNOS-010-04-AC-03DSH Web通过2026-10-09设置弹框无本插件入口
TC-013FNOS-010-04-AC-04DSH Web通过(含 2 处自绘)2026-10-09数值字段为官方 SettingsValueField(默认值 15000/5);布尔开关与账号列表自绘(官方字段原语无布尔与对象列表控件),理由见代码注释
TC-019FNOS-010-06-AC-01组合通过2026-10-09插件行「运行中」,官方行未被禁用
TC-028FNOS-010-08-AC-01/02DSH Web通过(亮色)2026-10-09用量区块同时显示两个平台共 4 个账号:TinyFish 钱包余额(11.6 USD / 10 USD)与搜索单价、Tavily 的 Key 用量、套餐与套餐用量,值与平台端点逐项一致;亮色主题部分完成,暗色主题未执行
TC-010FNOS-010-04-AC-01DSH Web通过2026-10-09两个真实账号分别显示各自掩码(sk-tin…KFFtn、sk-tin…H3NBH),新增输入框以当前掩码作占位;明文 key 不跨线
TC-035FNOS-010-04-AC-05DSH Web通过2026-10-09编辑 tinyfish-2 备注名为 tinyfish-renamed 后列表即时更新,cordis.patch.yml 中该账号的 key 与同平台另一账号均未变动(逐字段写入);改回原名同样生效
TC-036FNOS-010-04-AC-06DSH Web通过2026-10-09点击「复制 key」后按钮显示「已复制」瞬态反馈(仅当 host 返回明文且写入剪贴板成功);4 个账号各自独立触发,列表常态不展示明文
TC-018FNOS-010-05-AC-01/02DSH Web 会话通过2026-10-09真实会话(GLM-5.3-Flash)内三次联网问答均触发 web_search 工具;TinyFish 请求日志逐条记下轨迹里的原始 query(DeepSeek Harness 是什么、DeepSeek Harness GitHub),结果在回答中以标题 + 链接呈现
TC-037FNOS-010-01-AC-01DSH Web 会话通过2026-10-09意图级三方优先:接入后发一句需联网的问题,模型调用 web_search,TinyFish 请求日志出现该次 query(06:33:02/06:33:04 UTC 与轨迹时间戳对应,各 200、10 条结果),官方未被调用
TC-038FNOS-010-02-AC-02 / 03-AC-01DSH Web 会话通过2026-10-09意图级来源跳过:把 TinyFish 置空、仅留 Tavily 后再次联网提问,搜索仍正常返回;TinyFish 日志无新增条目(total=52、最新仍为 06:33:04),证明未配置来源被零请求跳过
TC-040FNOS-010-04-AC-01/04DSH Desktop通过2026-10-09详情页配置区正常渲染(8 个输入框、2 个自绘开关);经 Desktop 界面新增账号后列表显示掩码 sk-tin…KFFtn,配置落盘到 .dsh/profiles/desktop/cordis.patch.yml
TC-041FNOS-010-04-AC-05/06DSH Desktop通过2026-10-09账号行的「编辑 / 复制 key / 删除」在 Desktop 上渲染正常,与 Web 一致
TC-042FNOS-010-08-AC-01/02DSH Desktop通过2026-10-09用量区块在 Desktop 渲染,位置在「包含的组件」之前(平台用量 索引 < 包含的组件);未配置平台显示「未配置账号」
TC-043FNOS-010-01-AC-01DSH Desktop 会话通过2026-10-09意图级三方优先:Desktop 会话内提问联网搜索,TinyFish 请求日志新增两条(07:08:48 UTC 对应界面 15:08,各 200 / 10 条),total 52→54
TC-044FNOS-010-05-AC-01DSH Desktop 会话通过2026-10-09搜索卡片与来源链接在 Desktop 正常呈现(回答含标题 + 链接),与 Web 表现一致
TC-045FNOS-010-10-AC-02DSH Desktop通过2026-10-09插件管理页显示「Failover Search」与中文简介,与 Web、发布清单一致
TC-047FNOS-010-04-AC-04 / 08-AC-03(暗色主题)DSH Web通过2026-10-09按UI 测试清单在暗色主题逐项核对:① 对比度——配置区标题 17.45、提示/空状态 8.54、错误文案 5.55(均 ≥ 4.5);② border——账号卡与账号行的 .5px solid var(--dsw-alias-border-l3/l2) 在暗色下解析为 rgba(255,255,255,.16/.12),分隔可见、无「隐约中间态」;③ 硬编码残留——插件全部样式声明均为 --dsw-* 语义变量,未发现亮色残留(computed 里大量近白 border-color 为 border-top-style: none 的继承默认值,不绘制,经样式表声明逐条甄别排除);④ 毛玻璃——插件区块自身 0 条 backdrop-filter 链路,无重复叠加;⑤ 主题往返切换(暗→亮→暗)后全部自定义样式跟随,无残留;⑥ 错误态(无效 key 触发 HTTP 401 提示)在暗色下可读且信息不含 key 明文
TC-051FNOS-010-04-AC-04DSH Web通过2026-10-10详情页区块顺序按要求调整为「包含的组件 → 平台账号 → 转移与均摊 → 用量展示与探测 → 平台用量」:座位由 plugins.bundle.config 迁到 plugins.detail.section(上游 PackageDetail 的 bundle.config 恒在组件列表之前,无法满足该顺序),实测各区块文本索引 395 < 574 < 731 < 947;配置表单仍经 configForms 按入口 id 取得,保存按钮与表单状态正常
TC-058FNOS-010-08-AC-05DSH Web + 源码契约通过2026-10-10真实界面确认刷新入口存在(文案「刷新」、aria-label="刷新"、可点击);源码契约 3 条钉住「入口存在 + 有可访问名称 + 三态各渲染一次」——本条正是此前漏测的控件清单维度(原用例只断言数据呈现)
TC-059FNOS-010-08-AC-04DSH Web 真实界面通过2026-10-10点击前获取时间 10:56:53;点击瞬间入口 disabled=true 且文案变「刷新中」;完成后获取时间更新为 10:57:47——确认真实重查端点而非重读缓存;状态序列仅 true|刷新中 → false|刷新(无重复触发窗口)
TC-060FNOS-010-08-AC-04源码契约通过2026-10-10usage 仍为只读端点、新增独立 refresh 端点;宿主 refresh 分支复用 refreshUsage(失败折成日志,单账号失败不阻塞其它账号)
TC-052回归(抓取不破坏搜索)真实网络 + 单测通过2026-10-10既有 smoke 9/9 全过(新 provider 接管后搜索链路零回归:三方优先、来源转移、官方兜底、多账号均摊、探测跳过、两个用量端点);新增单测钉住「3 次搜索 + 2 次抓取交错执行互不阻塞」与「同 id 双注册合法(search 与 fetch 是独立 store)」
TC-053回归(座位迁移后配置读写)DSH Web 真实界面通过2026-10-10既有 2 个账号与掩码正确读出;新增账号 → 列表即时更新;改超时为 25000 → 保存按钮由禁用变可用、点击后落盘 .dsh/profiles/web/cordis.patch.yml(timeoutMs: 25000 与新增账号均写入);删除账号并把超时改回 20000 → 同样落盘。区块顺序 397 < 570 < 995。结论:换座位未影响配置读写路径;另有 3 条源码级单测钉住表单来源与 op 形状
TC-054回归(本地抓取语义)真实网络 + 单测通过2026-10-10本地成功时零平台调用、html body kind 不被改写;非 2xx 仍作为结果返回(statusCode: 404 透出且不转平台);limits 与官方默认逐项一致(5e6/1e5/3e4/5+官方 UA)——变异检验:改 maxBodyChars 或让本地把 4xx 当失败转平台,该用例均变红(证明断言有牙齿)
TC-055FNOS-010-11-AC-01单测通过2026-10-10平台返回 120000 字符长正文原样透出(复合层不额外截断、truncated 如实);空正文判为失败并抛含 empty 的可读错误
TC-056FNOS-010-11-AC-03单测通过2026-10-10abort 在平台跳执行前即被拦截(signal 已中止时平台零调用);中止抛 WEB_ABORTED
TC-057FNOS-010-11-AC-04单测通过2026-10-10未配置平台零调用(可用性先于取回判断);抓取失败后同一上下文内搜索照常成功(两面独立);账号复用 010-04 同一份配置
TC-048FNOS-010-11-AC-01真实网络通过2026-10-10复合 provider 单测 4 项全过;真实网络 4/4:本地失败→TinyFish Fetch 取回 11579 字符、TinyFish 不可用→Tavily Extract 取回 12832 字符、本地成功→平台零调用、全跳失败→保留本地错误码 WEB_INVALID_URL
TC-049FNOS-010-11-AC-03DSH Web 会话通过2026-10-10会话内让模型 web_fetch 抓 docs.tavily.com(本机 fake-ip 环境本地跳必被拒):debug 日志显示 local failed (42ms) → tinyfish succeeded (1562ms),模型拿到 HTTP 200 正文并正确总结——兜底对模型透明
TC-050FNOS-010-11-AC-02/04契约 + 单测通过2026-10-10契约测试钉住 patch 的 fetchProvider 指向本插件 id(写 http 会让平台兜底永不触发,已加反向断言);单测覆盖 TinyFish→Tavily 次序、平台间转移、逐跳事件(skipped 与 failed 区分)、账号复用 010-04 账号池
TC-046FNOS-010-01-AC-01(日志级归因)DSH Desktop 会话通过2026-10-09把 Cordis 控制台导出器阈值降到 debug(logger-console 插件,levels.default: 3)后,会话内搜索的宿主控制台出现插件自身尝试事件:[D] dsh-failover-search [failover-search] tinyfish/desktop-tinyfish succeeded (1125ms) 与 (1263ms) 两条;同一轮 TinyFish 请求日志 total 61→63,日志行数与平台新增条目数一致——插件内部「选账号 → 发请求 → 成功」的逐次事件与平台侧记录互相印证
TC-039FNOS-010-03-AC-02 / 02-AC-03DSH Web 会话通过2026-10-09意图级全链失败回落:三方凭据全部置为无效后提问,模型回报「联网搜索因缺少 DEEPSEEK_API_KEY 凭证而失败」——证明请求已走完三方并回落官方,官方按无凭据给出明确错误码,链路未挂起、未返回空结果
TC-020FNOS-010-06-AC-01DSH Web通过2026-10-09三方凭据全部失效时回落官方路径已在会话内实证(同 TC-039);组合行 searchProvider: dsh-failover-search 与 fetchProvider: http 由 --dump-config 确认(同 TC-019),禁用后回落 base 层官方配置机制不变
TC-022FNOS-010-07-AC-01DSH Web未完成—见下方缺口说明

文档与发布(文档站构建环境) ​

用例覆盖执行环境结果日期备注
TC-014FNOS-010-04-AC-07文档站通过2026-10-09文档配置项表与实现逐项一致(三组、8 项)
TC-033FNOS-010-10-AC-01构建产物通过2026-10-09lib/index.js 导出运行时 Config;client 以包名为模块 id 注册
TC-034FNOS-010-10-AC-02文档站 + DSH Web部分通过2026-10-09插件管理页、文档站、发布清单名称一致;测试执行中发现显示名未生效,见 BUG-01

结果缺口说明(不构成通过结论) ​

  1. TC-022(同平台多账号分摊效果)未完成 → 已补测通过:补入第二个真实 TinyFish key 后,真实网络下连续 4 次请求分布为 tinyfish-1, tinyfish-2, tinyfish-1, tinyfish-2,满足需求「使用同平台至少两个真实账号验证」的口径。
  2. TC-018(会话搜索卡片走查)未执行 → 已补做通过:本地 web profile 的模型路由(codebuddy / GLM-5.3-Flash)凭据有效,已在其真实会话内完成三次联网问答走查(TC-037/TC-038/TC-039),搜索卡片与来源链接正常呈现。
  3. TC-020(禁用插件后回落)未执行 → 已补做通过:全链失败后回落官方路径已在会话内实证(TC-039),官方按无凭据返回明确错误码而非空结果。
  4. DSH Desktop 全部用例未执行 → 已补做通过:按规范用 fork-pj/deepseek-harness 检出的开发态 Desktop(0.2.0-rc.2-639ed01,UA 含 @deepseek-ai/dsh-desktop/0.2.0-rc.2)、DSH_HOME 指向项目根 .dsh、CDP 9333 完成走查,结果见下方 TC-040~TC-045。用户全局 profile(~/.dsh/profiles/desktop)未被改动。
  5. 暗色主题未执行:TC-028 与 TC-013 的 UI 走查只在亮色主题下完成;UI 测试清单要求亮暗各执行一遍。
  6. 凭据清理:本地 profile 的测试账号写入 <repo>/.dsh/profiles/web/cordis.patch.yml(该目录已被 git 忽略,未进入仓库)。测试账号与用量刷新产生的配置仍留在本地 profile,验收完成后需删除。

Bug 记录 ​

Bug标题严重程度状态关联用例说明
BUG-01插件管理页卡片显示包名而非 displayName「Failover Search」低已关闭(非缺陷,上游现状)TC-034执行期观察到卡片标题为 @tnnevol/dsh-failover-search 而非 displayName。核对同一页面上的 @tnnevol/dsh-codebuddy(其 package.json 声明了 displayName: CodeBuddy)同样显示包名,说明官方插件管理页当前不消费 displayName。据此 FNOS-010-10-AC-02 的「三处一致」按仓库既有插件的口径成立:插件管理页与发布清单同为包名,文档站页面以插件显示名作为页面标题。
BUG-02TinyFish 结果 url 返回相对重定向包装,直接透传会产生错误链接中已修复TC-015真实调用(2026-10-09)发现同一 key 下部分查询返回 /url?opi=…&q=https://real-target&sa=U&ved=… 而非绝对地址;接缝要求 source.url 可引用,原样透传会让搜索卡片链接指向搜索平台自己的主机。已按 TDD 先写失败用例,再实现「还原 q 目标、无法还原则跳过该条目」,并为 Tavily 侧补上绝对地址防御性校验。
BUG-03配置区表单状态读一次快照,输入不回流、保存按钮恒禁用中已修复TC-010、TC-011首版配置区直接调用 model.field(...) 取值,表现为输入框打不进字、保存按钮始终禁用。改为经 SettingsFormModel.bind() 的订阅式 store 读取并 useSyncExternalStore 订阅后,真实页面上编辑超时可使保存按钮转为可用并持久化 timeoutMs: 20000。
BUG-04仅导出 Config schema 时详情页不出现配置区中已修复TC-013计划 T01-01 假设插件声明 Config 即由宿主自动生成表单,实测 [data-plugin-config] 区块数为 0:表单必须由插件注册 plugins.bundle.config 座位提供。已补该座位并承载标量字段与自绘交互。

| BUG-05 | 账号列表不显示已保存的 key,用户无法辨认每行配的是哪个 key | 中 | 已修复 | TC-010 | 执行期反馈「已配置平台账号,但表单没有数据显示」。根因是设计使然:key 声明为 secret 角色后宿主会把明文整体移出配置读取面,客户端根本拿不到 key,界面因此只能显示「已保存」标签、新增输入框也始终为空。修复方式是由持有明文的 host 经只读 RPC 送出不可还原掩码(保留首尾极短片段,同平台多账号可区分),账号列表展示掩码、新增输入框以当前掩码作占位;明文仍不跨线。 | | BUG-06 | 「平台用量」区块位于「包含的组件」下方,与期望顺序相反 | 低 | 已修复 | TC-028 | 首版把用量挂在 plugins.detail.section,而上游 PackageDetail 的固定顺序是「bundle.config → 包含的组件 → detail.section」,该座位永远排在组件列表之后。已改为在 plugins.bundle.config 内渲染「配置表单 → 平台用量」,两块的相对顺序因此可被插件控制。 |

| BUG-07 | 新增平台账号后,用量区块仍显示「未配置账号」 | 中 | 已修复 | TC-028 | 用量快照默认 5 分钟刷新一次,且客户端只在挂载时取一次;用户刚配好 Tavily 时快照里没有它,界面显示「未配置账号」,要等满一个刷新周期才更新,表现为「配置没生效」。修复:host 侧增加账号配置指纹(不含 key 明文),巡检发现配置变化立即重取快照;客户端把宿主配置修订号作为重取依赖。修复后实测新增两个 Tavily 账号,用量区块随即显示两个账号及其读数。 |

| BUG-08 | 无法用平台用量端点核对「某一次搜索由哪个来源服务」 | 低(测试方法受限期发现) | 不改实现 | TC-037 | Tavily 用量计数不是实时的:对同一 key 连续发起真实搜索后 key.usage 在 20 秒与 90 秒后均未变化,故无法用计数增量归因单次搜索。改用两条更强的证据:TinyFish 请求日志(GET https://api.search.tinyfish.ai/usage,含 query 与时间戳)与「三方凭据全部置无效后回落官方」的排除法。该现象也说明 FNOS-010-09 的 Tavily 探测依赖的是有延迟的快照,与需求「快照过期退化为响应式转移」的设计一致,不需改动。 |

| BUG-09 | e2e 脚本的组合行断言停留在旧契约,漏检 fetchProvider 变更 | 中 | 已修复 | TC-052 | 由本轮回归测试发现:既有 scripts/e2e.ts 硬编码断言 fetchProvider: http,而 FNOS-010-11 已按需求把该键改指本插件——脚本会报「fetch 配置丢失」。属测试资产与实现漂移,非产品缺陷。已改为断言「已声明且指向本插件」(保留原意:patch 整行替换不得漏掉 fetch 键),并说明写成 http 会让平台兜底永不触发。 |

| BUG-10 | 用量区块缺少手动刷新入口 | 中 | 已修复 | TC-058~TC-060 | 用户界面反馈(截图指出「刷新按钮功能丢失」)。核查结论:该入口从未实现,不是改坏——用量区块原设计只依赖后台定时刷新(refreshHint 文案即「后台自动刷新」),FNOS-010-08 原有三条 AC 也只要求展示、获取时间与失败提示,未含手动刷新,属需求与实现的共同遗漏。已补 AC-04(刷新行为)与 AC-05(交互控件清单),实现刷新入口并新增独立 refresh 端点(usage 保持只读)。测试为何漏掉:原有用例只覆盖数据呈现维度(值与端点一致、时间存在、失败提示可读),没有任何用例断言「区块应有哪些交互控件」——本次补的 AC-05 专门钉住控件清单,防止同类漏测。 |

| BUG-11 | inject 未声明 tools,导致 tools/pre-execute 订阅静默失效 | 高 | 已修复 | TC-071 | 门控实现后实测「关闭开关仍调用三方」(TinyFish 计数 88 → 90)。根因:插件 inject 只有 ['web'],Cordis 按 inject 门控服务与其事件,未声明 tools 时事件订阅不生效。已补 'tools' 并在契约测试中断言该声明。 | | BUG-12 | 门控挂在 tools/pre-execute 时作用域丢失,provider 读不到会话身份 | 高 | 已修复 | TC-071 | 补齐 inject 后仍失效,诊断日志给出决定性证据:钩子内读到 enabled=false、provider 内读到 enabled=true。根因:pre-execute 只返回一个决定,注册表拿到决定后另起调用链派发工具本体,钩子里建立的 AsyncLocalStorage 作用域覆盖不到工具执行。改用 tools/execute(around-dispatch 水位钩子,next() 即派发动作)后作用域正确覆盖。契约测试改为断言必须用 tools/execute 且不得用 pre-execute。 |

测试结论 ​

整体结论:通过。

  • 已通过:FNOS-010-01、02、03、06、09 的全部验收条件在单元、真实网络与会话内三层均通过;FNOS-010-11(网页抓取三方兜底)经单测、真实网络与会话内 debug 日志级三层验证通过(TC-048~TC-050);FNOS-010-04(配置区与账号管理)在 DSH Web 与 DSH Desktop 两个客户端通过;FNOS-010-05(结果归一、截断与卡片呈现)经真实会话走查通过;FNOS-010-07-AC-01 的真实多账号分摊经 TinyFish 双账号实测通过;FNOS-010-08(两个用量端点)在 Web 与 Desktop 渲染通过;FNOS-010-10(命名契约)通过。
  • 发布验收通过:插件 v0.2.0-rc.2.1 已上架 npm(dist-tags.next,发布时间 2026-10-10T05:45:38Z)。已发布包经下载解包核对(非仅凭版本号):智能搜索开关、用量区块手动刷新、网页抓取三方兜底、详情页座位迁移与区块顺序、会话级 toggle-set 端点全部在包内;cordis.patch.yml 的两个 provider 键均指向本插件;包内 package.json 版本与发布清单一致。
  • 遗留:compatibility.json 的包清单修正(10 → 16 个包)晚于本次发布,未进入 0.2.0-rc.2.1。该项约束宿主的兼容门禁提示、不影响插件运行,随下次插件版本发布。
  • 测试数据清理已完成:两份 profile 的测试账号已清空(tinyfishAccounts: [] / tavilyAccounts: []),配置原文按插件配置备份约定备份至 tmp/plugin-backup/dsh-failover-search-2026-10-09/(git 忽略,未进仓库);暗色主题走查已完成(TC-047)。
  • 已修正的前述结论:早期登记的「Desktop 全部用例未执行」、「会话搜索卡片未走查」、「禁用回落未走查」与「缺第二个真实账号」均已在本轮补齐(见 TC-037~TC-045)。
  • 遗留风险:
    • 上游响应契约不是稳定接口:url 的相对重定向包装(BUG-02)与 date 字段的时有时无都来自实测;平台变更格式时,相关条目会被跳过或省略日期,搜索本身仍成功。
    • FNOS-010-08 的 Tavily key 层 limit 在当前 key 档位为 null;实现据此不做 key 级触限判定、只按账户层 plan_usage >= plan_limit 判定,档位变化后需要复核该判定。
  • 会话级开关(FNOS-010-12)核心语义经平台侧计数验证:关闭后 TinyFish 平台计数不变(三方零调用)、开启后恢复增加,且同一会话内切换无需重启即生效。该功能的实现踩到两个真实接缝陷阱(inject 缺 tools、pre-execute 与 execute 的派发位置差异),均已修复并由契约测试钉住(BUG-11、BUG-12)。
  • 发布前修正:compatibility.json 的 dshPluginApi.packages 原先停留在 10 个包,未随本轮新增依赖同步——实测由 tooling/fnos-dsh-cli 的 keeps each compatibility package list aligned with source imports 用例守住(该用例从源码扫 import 并与声明面比对)。已补至 16 个包(覆盖 dsh-tools、dsh-agent、dsh-client-ui-conversation、dsh-web-fetch-http 等本轮新增依赖);同时修正 docs/plugins/dsh-failover-search.md 的安装命令版本(版本升级脚本未覆盖该页,由 keeps every documented install command on the released plugin version 用例发现)。
  • 控件维度已补测:BUG-10 暴露的漏测根因是「用例只测数据、不测控件」。已新增 AC-05 把用量区块的交互控件清单固化为验收条件,并补 TC-058(控件存在性 + 可访问名称 + 三态可见)。
  • 回归验证已在 DSH Web 与 DSH Desktop 两个客户端完成:本轮改动(新增复合 fetch provider、座位迁移到 detail.section、重写组合行 fetchProvider、新增刷新端点)均属规范定义的「回归验证类」。Desktop 侧由 TC-061~TC-064 覆盖——界面结构与 Web 逐项一致、刷新端点可用、抓取兜底经日志级取证生效、搜索链路未受影响。回归用例 TC-052~TC-054 全部通过——既有 smoke 9/9、e2e 8/8 在改动后仍全过;配置读写路径经真实界面操作确认与迁移前一致;本地抓取语义(零平台调用、html kind、非 2xx 语义、limits)经变异检验确认受断言保护。回归过程发现 BUG-09(e2e 断言陈旧),已修复。
  • 结果同时覆盖 DSH Web 与 DSH Desktop 两个客户端(插件兼容基线要求的最低组合),Desktop 侧使用 fork-pj/deepseek-harness 检出的开发态构建。
  • 归因方法分两层:早期用平台侧日志与排除法(TC-037~TC-039),最后一轮用插件自身的 debug 日志(TC-046)直接观测到逐次尝试事件。后者依赖两个临时手段——profile 插入 logger-console(levels.default: 3)并把该包 symlink 进 profile 的 node_modules(bare specifier 以 profile 目录为解析基准);两项都是测试期改动,验证后已还原,未进入仓库。
  • 剩余缺口只有 npm 发布一项(凭据在用户侧);在发布完成前 FNOS-010 仍不标记为「已完成」,需求状态维持「待完成」。

基于 VitePress 构建