Freebuff CLI 端到端测试实战:基于 tmux 与 Codebuff SDK 的双层测试体系
Freebuff CLI 端到端测试实战基于 tmux 与 Codebuff SDK 的双层测试体系【免费下载链接】freebuffThe free coding agent项目地址: https://gitcode.com/GitHub_Trending/cod/freebuff本文档是 Freebuff 开源仓库中 CLI 端到端E2E测试体系的完整技术指南。它围绕 freebuff/e2e/README.md 展开详细讲解如何通过 tmux 驱动编译产物进行确定性断言如何使用 Codebuff SDK 让 AI 测试代理自动验证复杂行为以及如何对生产后端执行真实一轮对话的冒烟测试。读完本文你将掌握 Freebuff E2E 测试的完整架构、核心工具类用法、构建运行流程并能够按既有规范新增一条端到端测试。为什么 CLI 需要端到端测试Freebuff 是一个终端交互的编码代理 CLIThe free coding agent其核心体验全部发生在 TUI终端用户界面之中启动时的 ASCII 启动画面、模型选择器、/help等斜杠命令、流式对话输出。单元测试可以覆盖内部逻辑却无法回答编译出的二进制在真实终端里能不能正常启动、按键是否被正确捕获、输出是否如期渲染这类问题。freebuff/e2e/README.md 给出的答案是用 tmux 模拟真实终端会话让测试代码像真实用户一样与编译后的二进制交互——发送文本、发送按键、抓取屏幕输出、断言行为——从而验证 CLI 的端到端正确性。整个 E2E 测试目录结构如下freebuff/e2e/ ├── README.md ├── agent/ │ └── freebuff-tester.ts # SDK 测试代理定义 ├── tests/ # 各特性 E2E 测试 │ ├── ads-behavior.e2e.test.ts │ ├── agent-startup.e2e.test.ts │ ├── code-edit.e2e.test.ts │ ├── help-command.e2e.test.ts │ ├── knowledge-file.e2e.test.ts │ ├── live-turn.e2e.test.ts # 唯一的线上冒烟测试 │ ├── slash-commands.e2e.test.ts │ ├── startup.e2e.test.ts │ ├── terminal-command.e2e.test.ts │ └── version.e2e.test.ts └── utils/ ├── binary-helpers.ts # 二进制路径解析 ├── freebuff-session.ts # 核心会话封装类 ├── index.ts # 统一导出 ├── tmux-custom-tools.ts # SDK 自定义工具 └── tmux-helpers.ts # tmux 脚本底层封装测试架构三种互补的验证方式根据 README 的定义这套体系同时支持三种测试方式覆盖从快速确定性回归到真实后端链路的完整梯度方式核心机制适用场景1. 直接 tmux 测试用FreebuffSession类在 tmux 中启动二进制、发送命令、抓取输出并直接断言快速、确定性的功能回归如启动、版本、帮助命令2. SDK Agent 驱动测试通过 Codebuff SDK 运行一个测试代理代理借助自定义 tmux 工具与 CLI 交互AI 推理式验证复杂行为输出不再是匹配字符串而是理解语义3. 线上冒烟测试使用生产环境变量构建二进制真实调用后端完成一轮对话并结束会话发布前与定时任务中对线上链路的冒烟验证方式一直接 tmux 测试快速、确定性这是最常用的方式。测试代码通过FreebuffSession在 tmux 中启动二进制随后发送命令、捕获输出、断言结果import { describe, test, expect, afterEach } from bun:test import { FreebuffSession, requireFreebuffBinary } from ../utils describe(My Feature, () { let session: FreebuffSession | null null afterEach(async () { if (session) await session.stop() session null }) test(works correctly, async () { const binary requireFreebuffBinary() session await FreebuffSession.start(binary) await session.send(/help) const output await session.capture(2) expect(output).toContain(Shortcuts) }, 60_000) })注意测试运行在bun:test之上与仓库其余测试一致每个测试都声明了超时如60_000毫秒因为真实的终端交互与轮询需要时间。方式二SDK Agent 驱动测试AI 推理式验证当行为过于复杂、无法用简单的字符串包含断言时可以使用 Codebuff SDK 运行一个测试代理。代理通过自定义 tmux 工具与 Freebuff 交互理解CLI 输出并验证复杂行为import { describe, test, expect, afterEach } from bun:test import { CodebuffClient } from codebuff/sdk import { freebuffTesterAgent } from ../agent/freebuff-tester import { createFreebuffTmuxTools, requireFreebuffBinary } from ../utils describe(Agent Test, () { let cleanup: (() Promisevoid) | null null afterEach(async () { if (cleanup) await cleanup() cleanup null }) test(verifies startup, async () { const apiKey process.env.CODEBUFF_API_KEY if (!apiKey) return // Skip if no API key const binary requireFreebuffBinary() const tmuxTools createFreebuffTmuxTools(binary) cleanup tmuxTools.cleanup const client new CodebuffClient({ apiKey }) const result await client.run({ agent: freebuffTesterAgent.id, prompt: Start Freebuff and verify the branding is correct., agentDefinitions: [freebuffTesterAgent], customToolDefinitions: tmuxTools.tools, handleEvent: () {}, }) expect(result.output.type).not.toBe(error) }, 180_000) })无CODEBUFF_API_KEY时该测试直接跳过因此本地未配置密钥也不会阻塞测试套件。方式三线上冒烟测试真实后端链路freebuff/e2e/tests/live-turn.e2e.test.ts 是freebuff/e2e/tests/目录中唯一直接与真实后端通信的文件它在落地页选择器上定位到 DeepSeek V4.1 Flash 模型、启动会话、发送一条提示词、等待回答最后执行/end-session并校验后端不再存在打开的会话。它只有在设置了FREEBUFF_SMOKE_API_KEY或CODEBUFF_API_KEY时才运行否则跳过且需要一个以生产公共环境变量构建的二进制。该测试不在freebuff-e2e.yml矩阵中而是由.github/workflows/prod-smoke.yml按计划schedule以及每次发布前执行说明当前仓库快照的 .github/workflows 目录下仅可见ci.yml与pr-hygiene.ymlREADME 中描述的freebuff-e2e.yml、prod-smoke.yml属于文档记载的 CI 配置。NEXT_PUBLIC_CB_ENVIRONMENTprod NEXT_PUBLIC_CODEBUFF_APP_URLhttps://www.codebuff.com \ NEXT_PUBLIC_FREEBUFF_APP_URLhttps://freebuff.com NEXT_PUBLIC_SUPPORT_EMAILsupportcodebuff.com \ NEXT_PUBLIC_POSTHOG_API_KEYtest NEXT_PUBLIC_POSTHOG_HOST_URLhttp://127.0.0.1:9 \ NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEYtest NEXT_PUBLIC_STRIPE_CUSTOMER_PORTALhttp://127.0.0.1:9 \ NEXT_PUBLIC_WEB_PORT3000 bun freebuff/cli/build.ts 0.0.0-smoke CODEBUFF_API_KEYyour token bun test freebuff/e2e/tests/live-turn.e2e.test.ts --timeout300000两个关键设计细节配置隔离CLI 运行在自己的FREEBUFF_CONFIG_DIR下因此不会触碰本机的真实用户配置。从源码看freebuff/e2e/tests/live-turn.e2e.test.ts测试用fs.mkdtempSync创建临时配置目录写入settings.json设置freebucksIntroSeenAt以跳过首次运行卡片并通过FreebuffSession.start的env选项注入CODEBUFF_API_KEY与FREEBUFF_CONFIG_DIR。不留尾巴afterEach中若仍处于聊天界面且会话未结束会自动发送/end-session防止一次失败的断言在账号上留下长达一小时的开放会话源码注释明确记录了首个 CI 运行曾因此踩坑见 live-turn.e2e.test.ts。测试的提示词也刻意选择了不可靠记忆、但答案唯一的内容Reply with only the English word for the number 7, in lowercase.期望输出seven源码注释解释了为何不用算术题——Flash 曾把 41872359 答成 6536。前置条件运行 E2E 测试前需要准备tmux必须安装macOS 用brew install tmuxUbuntu 用sudo apt-get install tmuxFreebuff 二进制必须先构建bun freebuff/cli/build.ts 0.0.0-devSDK 构建产物仅 Agent 测试需要cd sdk bun run buildCODEBUFF_API_KEY仅 Agent 测试需要设置该环境变量。构建与运行测试先构建二进制bun freebuff/cli/build.ts 0.0.0-dev二进制路径的解析规则见 freebuff/e2e/utils/binary-helpers.ts优先读取环境变量FREEBUFF_BINARY否则回退到cli/bin/freebuffrequireFreebuffBinary()在文件不存在时会抛出带构建提示的错误信息Build with: bun freebuff/cli/build.ts 。运行全部测试bun test freebuff/e2e/tests/运行单个测试bun test freebuff/e2e/tests/version.e2e.test.ts bun test freebuff/e2e/tests/startup.e2e.test.ts bun test freebuff/e2e/tests/help-command.e2e.test.ts bun test freebuff/e2e/tests/agent-startup.e2e.test.ts使用自定义二进制路径FREEBUFF_BINARY/path/to/freebuff bun test freebuff/e2e/tests/核心工具FreebuffSession源码级详解FreebuffSession定义于 freebuff/e2e/utils/freebuff-session.ts是整个体系的基石。它封装了在 tmux 中启动二进制 → 与 TUI 交互 → 抓取输出 → 清理的全过程。API 一览方法说明FreebuffSession.start(binaryPath)在 tmux 中启动二进制返回会话对象session.send(text)发送文本输入默认按下回车session.sendKey(key)发送特殊按键如C-c、Escapesession.capture(waitSec?)抓取终端输出session.captureLabeled(label, waitSec?)抓取并保存到会话日志带标签session.waitForText(pattern, timeoutMs?)轮询直到终端出现指定文本session.stop()停止会话并清理资源start()临时工作目录与安全的密钥注入FreebuffSession.start的源码实现freebuff-session.ts值得仔细阅读创建临时项目目录fs.mkdtempSync在系统临时目录生成freebuff-e2e-*目录并写入一个最小README.md# E2E Test Project让 Freebuff 有真实项目可索引支持预置初始文件通过initialFiles选项可在启动前写入任意相对路径的文件含嵌套目录自动创建环境变量的安全注入env选项如密钥、配置目录被写入一个位于项目目录之外、权限为0600的env.sh文件再由 tmux shellsource加载源码见 freebuff-session.ts。这样密钥既不会落入 CLI 索引的项目目录也不会出现在会话记录的命令行中tmux 启动组装cd tmpDir binaryPath命令并调用tmuxStart默认等待 4 秒、窗口尺寸 120×30。会话内文件操作与轮询除终端交互外FreebuffSession还提供writeFile/readFile/fileExists直接在临时工作目录中读写文件以及waitForFileContent(relativePath, pattern, timeoutMs?)轮询等待某个文件出现指定内容默认超时 60 秒超时后抛出含终端输出的详细错误。这对于验证CLI 是否在项目中实际写出了文件的场景如代码编辑测试非常实用。启动就绪判断waitForReady与waitForBootSignalTUI 的启动存在多种形态源码为此提供了两套判断waitForReady(timeoutMs?, minLines?)轮询终端输出直到非空行数达到阈值默认 5 行表示 TUI 已完成初始渲染waitForBootSignal(timeoutMs?)轮询直到输出命中FREEBUFF_BOOT_SIGNALS中的任意一个已知启动标志。该常量数组freebuff-session.ts包含export const FREEBUFF_BOOT_SIGNALS [ █████╗ ██████╔╝, // ASCII logo (full or small variant) Start coding for free, Enter a coding task, Pick a model to start, Free mode isnt available, Press ENTER to login, Open this URL, will run commands on your behalf, ] as const源码注释解释了原因CI 运行环境经常落在模型选择器的 wordmarkStart coding for free而非完整的 ASCII 大 Logo所以相比只等待单个 Logo 行匹配一组标志更可靠。stop()幂等清理stop()先调用tmuxStop该脚本幂等会话已不存在时静默忽略错误再递归删除临时工作目录与环境目录。tmux 底层封装与脚本层FreebuffSession并不直接操作 tmux而是通过 freebuff/e2e/utils/tmux-helpers.ts 调用仓库根目录scripts/tmux/下的 shell 脚本tmux-start.sh、tmux-send.sh、tmux-capture.sh、tmux-stop.sh函数对应脚本说明tmuxStart(options)tmux-start.sh启动 tmux 会话支持--command、--name、--width、--height、--wait、--plaintmuxSend(session, text, opts?)tmux-send.sh发送文本支持--no-enter不回车、--wait-idle等待空闲、--forcetmuxSendKey(session, key)tmux-send.sh --key发送特殊按键tmuxCapture(session, opts?)tmux-capture.sh抓取输出支持--wait、--label带标签保存日志、--no-savetmuxStop(session)tmux-stop.sh停止会话错误被捕获幂等这些脚本同时被 scripts/tmux 目录下的其他工具复用构成一套独立的终端会话操作基础设施。SDK 自定义工具与测试代理createFreebuffTmuxTools(binaryPath)定义于 freebuff/e2e/utils/tmux-custom-tools.ts返回{ tools, cleanup }tools是四个自定义工具定义输入模式使用zod/v4schema 声明cleanup应在afterEach中调用以兜底停止会话工具名作用关键行为start_freebuff启动 CLI先检查会话是否已存在启动后调用waitForReady()并返回初始输出send_to_freebuff发送文本输入如同用户输入默认按下回车无会话时返回错误信息capture_freebuff_output抓取终端输出可选waitSeconds参数等待后再抓取stop_freebuff停止并清理幂等返回wasRunning状态freebuffTesterAgent专职 QA 测试代理freebuff/e2e/agent/freebuff-tester.ts 定义了测试代理id为freebuff-tester模型为anthropic/claude-sonnet-4.5可用的工具恰好是上述四个 tmux 工具。其系统提示词instructionsPrompt为代理规定了标准的操作流程调用start_freebuff启动 CLI用capture_freebuff_output带waitSeconds查看终端输出用send_to_freebuff输入命令或文本再次抓取输出验证行为完成后务必调用stop_freebuff。同时要求代理重点核查CLI 是否无错误/无崩溃启动、启动画面是否有可见内容、命令是否符合预期、错误信息是否对用户友好并清晰汇报测试项、观察结果与通过/失败结论。这套自定义工具 专用代理 结构化指令的模式正是 Freebuff 用 AI 验证 AI 产品的落地示范。测试用例剖析version.e2e.test.ts版本输出的三个断言freebuff/e2e/tests/version.e2e.test.ts 直接以execFileSync运行二进制不经 tmux验证三点--version输出应匹配/\d\.\d\.\d/semver 形式退出码为 0execFileSync遇非零退出码会抛异常不抛即通过忽略项目bunfig.toml的 preload测试在临时目录写入preload [$config/db]的bunfig.toml后仍能正常输出版本——这验证了--version这类无界面命令不会被项目配置干扰。startup.e2e.test.ts启动画面与优雅退出freebuff/e2e/tests/startup.e2e.test.ts 包含两个典型场景启动画面渲染waitForBootSignal()等待任一启动标志出现随后反向断言排除致命错误标记——输出不得包含Fatal error during startup、Internal error: tree-sitter.wasm not found、FATAL、panic、Segmentation fault。这是一种双重保险策略即便某个竞态导致错误与 Logo 同时出现也会被清晰指出而非埋没在原始输出中CtrlC 优雅退出启动并waitForReady()后发送C-c等待片刻再断言输出不含Unhandled与FATAL——验证 CLI 能干净地处理中断信号。help-command.e2e.test.ts品牌与用法校验freebuff/e2e/tests/help-command.e2e.test.ts 检查--help输出必须包含freebuff忽略大小写、匹配/usage|options|commands/i并且不得出现codebuff字样——后者是品牌正确性的回归测试防止帮助文案中泄露母公司品牌。新增测试的规范流程按 README 的指引新增一条 E2E 测试只需三步在freebuff/e2e/tests/下新建文件命名遵循feature.e2e.test.ts约定将测试名加入.github/workflows/freebuff-e2e.yml的矩阵README 记载的 CI 配置示例片段matrix: test: - version - startup - help-command - agent-startup - your-new-test # -- add here提交后该测试将在 CI 中与其他测试并行运行。CI 工作流设计根据 README 对.github/workflows/freebuff-e2e.yml的描述CI 流程为构建一次Freebuff 二进制linux-x64通过 GitHub Actions 的 matrix 策略并行运行每个测试文件失败时上传 tmux 会话日志用于调试这正是captureLabeled带标签保存日志的意义所在。触发时机包括每晚 PT 时间 6:00与手动触发workflow_dispatch。线上冒烟测试live-turn.e2e.test.ts则独立于矩阵之外由prod-smoke.yml在计划任务与每次发布前执行。最佳实践小结分层选择测试方式确定性行为用直接 tmux 断言快、稳、无需密钥需要语义理解的行为交给 SDK 测试代理线上链路用带密钥的冒烟测试且务必结束会话善用轮询而非固定 sleepwaitForText、waitForBootSignal、waitForFileContent都采用轮询 超时错误报告失败时附上终端输出便于定位注意密钥与配置隔离利用env选项 0600env.sh 独立FREEBUFF_CONFIG_DIR测试永不污染真实用户环境每个测试声明合理超时终端交互类测试通常以 60 秒为基准Agent 测试与线上冒烟可达 180300 秒反向断言防漏网对启动等关键路径除了正向匹配启动标志还应反向断言FATAL、panic等致命标记。至此你可以基于这套框架为 Freebuff 的任意 CLI 功能编写稳定、可并行、可调试的端到端测试并顺畅地融入其 CI 流水线。【免费下载链接】freebuffThe free coding agent项目地址: https://gitcode.com/GitHub_Trending/cod/freebuff创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

告别命令行:nTopology可视化建模快速生成Voronoi泡沫

告别命令行:nTopology可视化建模快速生成Voronoi泡沫

上周帮朋友调整一个鞋底中底的轻量化结构,他想做的东西很明确:三维Voronoi泡沫——一堆随机的胞元互相连通,看起来像海绵,踩上去又要能回弹。我原本打算用老路子,命令行加减Python脚本去跑scipy.spatial.Voronoi&#…

2026/11/7 18:25:53 阅读更多 →
AT89C51驱动数码管:74LS164串转并设计与Proteus仿真

AT89C51驱动数码管:74LS164串转并设计与Proteus仿真

简介:本资源是一套面向单片机初学者与嵌入式课程实践者的AT89C51驱动数码管仿真学习包,聚焦“硬件资源受限下扩展I/O实现静态显示”这一典型工程问题。内容涵盖Proteus电路设计(.dsn)、Keil C51完整工程(含main.c、.h头…

2026/11/7 4:20:28 阅读更多 →
信贷违约预测实战:逻辑回归与LightGBM双模型对比与调优

信贷违约预测实战:逻辑回归与LightGBM双模型对比与调优

信贷违约预测是我入行风控算法后做的第一个完整项目。当时组里让我用Kaggle上的信贷违约比赛数据练手,要求把LogisticRegression和LightGBM两条技术路线都跑通,最后出对比结论。我一开始觉得逻辑回归这种经典模型没什么好写的,但完整做完才发…

2026/11/1 18:55:12 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/11/6 15:21:35 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/11/7 12:08:40 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/11/6 22:01:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/11/6 22:01:11 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/11/7 12:08:40 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/11/6 22:01:09 阅读更多 →

日新闻

周新闻

月新闻