mcpField
T
testing-tui-runtime

testing-tui-runtime

github.com/subsy测试与质量2.5k67 次安装2 天前更新
代码仓库

关于 testing-tui-runtime

此技能用于在真实的 ralph-tui 终端界面中验证运行时和端到端测试结果。它提供隔离的测试环境、模拟 Agent、交互场景,以及解读会话、信号和锁行为的指引。

主要功能

  1. 01构建并运行已发布的 CLI
  2. 02创建独立临时项目和任务存储
  3. 03使用无需 AI 凭据的模拟 Agent
  4. 04测试 TUI 控件和监视行为
  5. 05检查信号、会话和锁文件

适用场景

  1. 01验证暂停、恢复和退出改动
  2. 02测试监视模式下的任务拾取
  3. 03排查信号处理和过期锁
  4. 04与 main 工作树构建进行行为对比

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/subsy/ralph-tui --skill testing-tui-runtime

安装后在 Claude Code 中输入/testing-tui-runtime确认已加载。

详细介绍

ralph-tui TUI 的运行时/E2E 测试

当需要在真实 TUI 中验证更改(watch 模式、引擎状态标签、暂停/恢复、关闭/退出路径),而不是通过单元测试验证时,请使用此流程。

构建并运行

cd /path/to/ralph-tui && bun run build      # produces dist/cli.js

运行构建后的 CLI(不要使用 bun run dev),以确保行为与发布版本一致:

bun /path/to/ralph-tui/dist/cli.js run [flags]

临时任务跟踪器(切勿触碰仓库自己的 .beads)

创建一个带有独立 beads 存储的临时项目目录,并使用 bd 执行各个场景:

export RALPH_TEST_DIR="$(mktemp -d "${TMPDIR:-/tmp}/ralph-tui-run.XXXXXX")" && cd "$RALPH_TEST_DIR"
bd init            # or `bd create ...` which bootstraps the store
bd create "Watch mode epic" --type epic -p 1          # note the epic id bd prints
bd create "Alpha task" --type task -p 2 --parent <epic-id>

每次运行都要使用一个全新目录,不要固定使用同一个目录:复用目录会保留 .beads 任务、.ralph-tui 会话文件、锁和保护机制——而这些正是此处大多数场景要验证的内容。因此,来自上一次运行的 Recovered stale session 横幅或过期锁提示,会被误认为是当前受测构建的问题。唯一的目录还会为本次运行生成唯一的 epic id,可通过它定位对应进程(见下方的信号 trap)。

注意事项:

  • bd 命令必须在临时目录中运行,否则会出现“no beads database found”。
  • 使用 --tracker beads 时,TUI 必须指定 --epic <id>;否则 TUI 会显示“No epics found”,而交互式 epic 选择会阻塞自动化。
  • 即使 AGENTS.md 中提到了 br(beads-rust),它也可能未安装;bd 可以正常使用。

模拟 Agent(无需 AI 凭据)

将插件放入 ~/.config/ralph-tui/plugins/agents/:

  • echo.ts — 立即返回 echo-agent: local fake response\n<promise>COMPLETE</promise>。
  • slowecho.ts — 与上面相同,但会以可中断的 500 ms 间隔休眠约 25 秒。测试迭代中途退出/暂停时必不可少;即时响应的 Agent 不会留给你中断的时间。

使用 --agent echo / --agent slowecho 进行选择。

让时序在屏幕上清晰可见

包装 CLI,以便在录制中证明退出延迟:

#!/bin/bash
cd "${RALPH_TEST_DIR:?export RALPH_TEST_DIR to the scratch dir first}"
export PATH="$PATH:$HOME/.local/bin"
echo "START $(date +%H:%M:%S.%3N)  args: $*"
bun /path/to/ralph-tui/dist/cli.js run "$@"
echo "=== PROCESS EXITED code=$? at $(date +%H:%M:%S.%3N) ==="
exec bash

典型调用: ./runwatch.sh --watch --poll 60 --agent echo --tracker beads --epic <epic-id> --no-setup --force

使用较长的 --poll(60s),让暂停/退出卡住的问题一目了然;若希望快速自动拾取,则使用较短的间隔(10s)。

TUI 按键

s 启动 · p 暂停/恢复 · q → 退出对话框 → y · d 仪表板 · r 刷新。 标题栏状态标签:Waiting(watch 空闲)、Selecting、Executing、Paused、Complete。 watch 空闲时,仪表板会显示 Waiting for new tasks。

已知陷阱

  • 中断(Ctrl+C)路径:在包含 #431 修复的构建中(src/tui/utils/keyboard-shortcuts.ts
    • RunApp.tsx 中的 onInterruptRequest + run.tsx 中的 exitSignals: ['SIGQUIT','SIGABRT']), 键盘 Ctrl+C 和外部 kill -INT <pid> 都会显示 ⚠ Interrupt Ralph? 对话框,且画面保持不变;按 n/Esc 可取消,按 y 会在约 2 秒内以退出码 0 退出,并将当前任务重置为 open;约 1 秒内再次按 Ctrl+C 会通过 process.exit(1) 强制退出(包装器 code=1, 终端画面不会重绘;在 exit 处理器调用 releaseLockSync 的构建中,强制退出后 不再残留锁文件,旧版本则会残留)。 在旧版本中,Ctrl+C 完全无效,而 kill -INT 会清空画面并导致进程挂起,因此 在归咎于某项更改之前,务必确认当前使用的是哪个构建。
  • 在大多数终端中,Ctrl+Shift+C 与 Ctrl+C 无法区分:konsole 对两者都发送字节 0x03 (可用 stty -isig; cat -v 验证——Ctrl+Shift+C 会打印 ^C,Alt+C 会打印 ^[c),因此当 终端本身未拦截该快捷键时,“复制”快捷键会打开中断对话框(konsole 仅在存在 konsole 级别的文本选区时才会拦截)。Alt+C 则能安全地区分开。 在没有 xclip/xsel/wl-paste 的机器上,无法验证剪贴板内容(这就是 src/utils/clipboard.ts 在 Linux 上调用的命令)——请记录为未测试剪贴板写入。
  • 启动早期的信号需要把握时机,且行为特殊:应用的 SIGINT/SIGTERM 处理器大约在启动后 ~0.7 秒才会安装。使用以下命令驱动: ( sleep 0.5; kill -INT "$(pgrep -f "cli\.js run.*--epic $EPIC_ID")" ) & ./runwatch.sh ..., 其中 $EPIC_ID 是本次运行的 epic——发送信号前,检查匹配模式是否精确匹配一个 pid (pgrep -cf ...),并注意包装脚本与其启动的 CLI 是不同的进程,因此应向匹配到的 pid 发送信号,而不是包装器。不要使用 pkill -f 'dist/cli.js run':它会命中机器上的所有 ralph 运行,包括并行测试或你正在比较的 main worktree 构建,并修改它们的会话和任务状态。 历史行为(src/session/lock.ts 中的 #434 修复之前):在该时间窗口内收到 SIGINT 会以 130 退出,但留下陈旧锁;SIGTERM 则会送达但被忽略(TUI 挂载后继续运行)。在由 registerLockCleanupHandlers 接管启动信号的构建中(受 process.listenerCount(signal) > 1 保护),无论在窗口内延迟多久,SIGTERM 都应返回 code=143,SIGINT 都应返回 code=130,且 .ralph-tui/ralph.lock 应被删除;在 acquireLockWithPrompt 之前(约 0.05 秒)送达的信号会采用默认处置方式,但不会留下锁。 务必测试多个延迟(0.05/0.2/0.3/0.4/0.5/0.7 秒),记录信号确实已发送,并在每次测试后检查 ls .ralph-tui/ralph.lock 以及下次启动时显示的横幅。在该窗口内快速连续发送两个信号, 仍应只产生一次干净的 130/143,绝不能是 code=1。
  • Recovered stale session 与 Stale lock detected 并非一回事:锁文件可以被正确删除,但 .ralph-tui/session.json 中仍保留 status: "running",此时每次后续启动都会打印 ⚠ Recovered stale session / Session status set to "interrupted" (resumable) ——即使是正常执行 q/y 退出(code=0)之后也是如此。在将该横幅归因于锁或信号更改之前, 请检查 jq .status .ralph-tui/session.json 和 session-meta.json;这可能是一个独立的会话持久化问题。
  • 在 TUI 模式下,非监视模式中所有任务完成后,进程不会自行退出;它会停留在 Complete 状态,直到你退出。要证明“工作完成后进程会退出”,请使用无头模式: --no-tui(别名 --headless),它会以退出码 0 退出并打印运行摘要。无头监视模式会 记录 [watch] All tasks complete — polling every Ns 并继续运行。
  • 被终止的会话可能留下陈旧锁(旧版本;锁层接管启动信号且 exit 处理器同步释放锁后已修复):下次启动时,会在 TUI 启动之前提示 "Remove the stale lock and continue? (Y/n)"。先回答 y;过早发送的按键(例如 s)会被该提示接收。优先正常执行 q/y 退出,以避免此问题。
  • 在外部创建任务后,请等待至少一个完整的轮询间隔再多几秒,然后再判断自动获取任务是否失败。
  • 锁保护文件(.ralph-tui/ralph.lock.guard)和 lockId:在会串行化锁变更的构建中,.ralph-tui/ralph.lock 包含一个 lockId 随机数标识,且每次创建、清理陈旧锁、--force 和释放操作都会通过使用 O_EXCL 创建的保护文件进行。实用的观察辅助工具:打印锁的 pid/lockId、保护文件是否存在(及其内容)以及通过 pgrep 查到的 ralph pid,并在每种场景前后采样。正常执行期间不应观察到保护文件存在——它只应在变更期间出现。值得手动构造的情况:空保护文件或内容为 not-json 的保护文件(约 250ms 后应作为格式错误文件回收,运行正常启动)、指定了已退出 pid 的保护文件(应回收),以及指定了仍存活 pid 的保护文件(应保留:等待约 2 秒后获取失败,并显示 Timed out waiting for the session lock (another ralph-tui process may be starting or exiting))。在此类构建中,如果保护文件在退出时一直被占用,超过同步等待预算,预期行为是留下锁供之后作为陈旧锁恢复,而不是删除它——此时留下锁应视为通过;挂起、停顿数秒或仍在运行的进程的锁消失,才是问题。确保手动构造的“仍存活 pid”确实处于运行状态(kill -0);已退出的 pid 会静默地将测试转变为“已退出 pid”情况。
  • run --resume 可能失败,而独立的 resume 可以正常工作:run.tsx 会先获取会话锁,再调用同样会获取锁并因锁已存在而拒绝的 resumeSession——表现为 Resuming previous session... / Failed to resume session,退出码为 1;而在同一中断会话上执行 ralph-tui resume 则能正常启动。此问题在 main 上也能复现,因此除非被测构建声称已修复,否则应视为既存问题;将其称为回归之前,务必与 main 的 git worktree 构建交叉验证。
  • 注意是否存在没有锁文件的孤立运行:曾观察到一次——TUI 完全没有显示任何内容,但引擎仍在执行迭代,且 .ralph-tui/ralph.lock 不存在。这是未捕获异常处理器释放锁却未退出的特征。后续尝试未能复现;如果遇到空白 TUI,请在终止进程前,通过 script -q -c "./runwatch.sh ..." /tmp/run.log 包装运行,以捕获 stdout/stderr(这会保留 TTY,与管道不同)。注意 runwatch.sh 以 exec bash 结束,因此用 script 包装的运行会让你进入一个内层 shell——输入 exit 才能继续脚本循环。

Devin 所需密钥

无——虚假 Agent 插件和本地 bd 存储无需任何外部凭据。

评论

支持 Markdown