LibreChat E2E 测试体系实战:Mock 假模型、Redis 流传输分片与 Bombadil 属性化浏览器探索
【免费下载链接】LibreChatEnhanced ChatGPT Clone: Features Agents, MCP, Skills, DeepSeek, Anthropic, AWS, OpenAI, Responses API, Azure, Groq, o1, GPT-5, Mistral, OpenRouter, Vertex AI, Gemini, Artifacts, AI model switching, message search, Code Interpreter, langchain, DALL-E-3, OpenAPI Actions, Functions, Secure Multi-User Auth, Presets, open-source for self-hosting. Active项目地址: https://gitcode.com/GitHub_Trending/li/LibreChat
本文以 LibreChat 仓库的 e2e/README.md 为核心,系统讲解其 Playwright 端到端测试体系的三层能力:免真实凭据的 Mock 测试画像(Profile)、内存/Redis 双流存储模式与 CI 分片策略、基于 Bombadil 的属性化浏览器探索测试,以及用 Playwright codegen 录制并落地为可维护规格(spec)的完整工作流。读完本文,你可以直接在本仓库运行完整的 mock e2e 套件、切换 Redis 传输验证流式保真度、复现并归档 Bombadil 发现的缺陷,并掌握从浏览器录制到提交级测试的转化方法。
一、Mock E2E 画像:免真实 LLM 凭据的安全默认
Mock e2e 画像是生成式测试最安全的默认选择:它使用 e2e/config/librechat.e2e.yaml 启动 LibreChat,通过LIBRECHAT_TEST_RUN_HOOK环境变量注入一个进程内假 LLM,自动创建一个已认证的 e2e 用户,全程不触碰任何真实 Provider 凭据。
1.1 从 npm 脚本到测试服务器的启动链
根目录 package.json 中定义的入口脚本为:
npm run e2e:mock该脚本实际展开为两步:
npm run e2e:prepare # 等价于 npm run frontend,构建>npm run e2e:mock:redis从 package.json 可见,该命令等价于cross-env E2E_STREAM_STORE=redis playwright test --config=e2e/playwright.config.mock.ts。
2.2 环境变量如何决定存储模式
e2e/setup/env.ts 中的getStreamStoreEnv()是模式分发的核心,支持三种取值:
E2E_STREAM_STORE | 行为 |
|---|---|
memory(默认) | USE_REDIS=false、USE_REDIS_STREAMS=false、E2E_REQUIRE_REDIS_STREAMS=false,显式禁用 Redis |
redis | USE_REDIS=true、USE_REDIS_STREAMS=true、E2E_REQUIRE_REDIS_STREAMS=true,REDIS_URI默认redis://127.0.0.1:6379/15(数据库 15),REDIS_KEY_PREFIX默认取自E2E_REDIS_KEY_PREFIX,缺省为LibreChatE2E |
redis-cluster | 同上但USE_REDIS_CLUSTER=true,REDIS_URI默认为 7001/7002/7003 三节点 |
E2E_REQUIRE_REDIS_STREAMS=true就是文档所说的"fail closed"机制:e2e/setup/start-server.js 启动时会 ping Redis(超时 10 秒),并验证生成 job 管理器没有静默回退到内存,否则测试直接失败而不是带病运行。
2.3 CI 分片与 Redis 传输专项套件
Pull Request CI 的策略是:完整 mock 套件跑内存模式、分 3 片,外加一条聚焦的 Redis 传输套件:
npx playwright test --config=e2e/playwright.config.mock.ts --shard=1/3 npm run e2e:mock:redis:transport分片安全性的依据来自 playwright.config.mock.ts:workers: 1保证每个 shard 只有一个 worker,从而不与同一 shard 内的其他测试争抢同一个认证用户和数据库;CI 下retries: 2且forbidOnly生效。
e2e:mock:redis:transport使用派生配置 e2e/playwright.config.redis.ts,它展开 mock 配置后只保留 10 个"跨越流存储边界"的场景,与 README 所列覆盖范围一一对应:
testMatch: [ /completion\.spec\.ts/, // 完成 /deferred-tools-hitl\.spec\.ts/, // HITL 审批 /model-spec-icons\.spec\.ts/, // 模型图标 /steering\.spec\.ts/, // steering(运行中转向) /steering-escalation\.spec\.ts/, // steering 升级 /streaming\.spec\.ts/, // 流式保真 /subagent-activity\.spec\.ts/, /thread-fold\.spec\.ts/, // 线程折叠 /tool-approvals\.spec\.ts/, // 工具审批 /usage\.spec\.ts/, // 用量 ]Redis 泳道还放宽了断言预算:expect.timeout从 10 秒提到 20 秒、CI 重试 3 次——因为每个流事件都要走真实 Redis 往返,暂停/恢复与重新水合(整 job 重放)路径最接近超时预算。夜间调度和手动 workflow 则会在两种流模式下各跑完整套件、每种模式 2 片。
三、Bombadil 属性化浏览器测试:让随机探索替你找 bug
Bombadil 是 e2e/bombadil/ 目录实现的属性化(property-based)浏览器测试框架,它对核心聊天回路、消息分支、并行多会话响应、模型切换、页面重载和侧边栏会话生命周期操作进行随机序列探索,而不是沿着固定脚本点击。
3.1 基础探索命令
npm run e2e:bombadil底层由 e2e/playwright.config.bombadil.ts(testDir: 'bombadil'、testMatch: 'harness.spec.ts')驱动 e2e/bombadil/harness.spec.ts,该 harness 会:
- 把 e2e/bombadil/specification.ts 复制为运行时副本,并把登录占位符
__BOMBADIL_E2E_USER_EMAIL__/__BOMBADIL_E2E_USER_PASSWORD__替换为实际 e2e 用户凭据; - spawn
node_modules/.bin/bombadil二进制,执行browser test --headless --output-path e2e/.generated/bombadil-output --instrument-javascript inline --exit-on-violation --time-limit 90s; - 对新运行前,若
e2e/.generated/bombadil-output已存在,先归档到e2e/.generated/bombadil-history/(带时间戳)。
调长探索时间:设置BOMBADIL_TIME_LIMIT(harness 支持90s、5m、2h这类数字+ s/m/h/d格式),本地或定时任务可放心用更长的值。
3.2 失败复现与归档闭环
失败会留下可复现 trace:
BOMBADIL_REPRODUCE=e2e/.generated/bombadil-output npm run e2e:bombadil:run复现时 harness 会向 bombadil 传入--reproduce <path>而不是--time-limit,并把 Playwright 超时放宽到 30 分钟。按设计,复现出一个真实违反(invariant violation)时测试应当失败;但如果流式时序变化导致行为发散,复现结果可能偏离,Bombadil 会明确报告这一点。
3.3 CI 集成:非阻塞探索 + 工件
CI 在Bombadil Property Explorationworkflow 中做 5 分钟宽泛探索(非阻塞)。属性失败时:
- 下载
bombadil-reproduction-*工件到e2e/.generated/bombadil-output/,本地执行上面的复现命令; - 配套的
bombadil-diagnostics-*工件包含 CI 日志、Playwright HTML 报告与测试结果; - Bombadil 失败只产生 workflow 警告,不阻塞合并。
3.4 JavaScript 插桩范围
默认只对inlineJS 做插桩,因为对 LibreChat 完整 Vite bundle 插桩会在有状态长运行中超过 Bombadil 的 driver 超时(harness.spec.ts 中的注释明确说明了这一点)。需要做更短周期的覆盖率引导实验时,可以设置:
BOMBADIL_INSTRUMENT_JAVASCRIPT=files,inline npm run e2e:bombadil3.5 五个可独立运行的生命周期属性
分支重载、fork 提交、模型/会话、HITL 暂停/恢复、运行中 steering 的生命周期属性可以单独跑:
npm run e2e:bombadil:branch-reload npm run e2e:bombadil:fork-lifecycle npm run e2e:bombadil:model-lifecycle npm run e2e:bombadil:hitl npm run e2e:bombadil:steeringpackage.json 中每条命令都通过BOMBADIL_SPECIFICATION指定对应的 specification 文件(如hitl-lifecycle.specification.ts),并内置时间上限(30s/30s/30s/45s/75s 分别对应 branch-reload/fork/model/hitl/steering)。这些聚焦命令是诊断性属性:复现出产品不变量违反时以非零码退出。对应的 trace 输出目录按 specification 区分(如e2e/.generated/bombadil-output-hitl),复现命令也要匹配目录与:run脚本:
BOMBADIL_REPRODUCE=e2e/.generated/bombadil-output-hitl npm run e2e:bombadil:hitl:run各属性的语义(摘自 e2e/README.md):
- HITL:驱动一次真实的
ask_user_question检查点走 answer/resume 控制器——问题暂停时重载页面、只回答一次、再重载已完成会话; - Steering:在一次慢速 MCP 支撑的运行中提交 in-flight steering,断言它恰好从 composer 锚点移动到响应中的工具边界一次,并重载已应用状态;
- Model lifecycle:作为"应通过的对照(passing control)";
- Branch reload 与 fork:保留其最小失败 trace 作为回归锚点。
由于 harness 使用的是免凭据 mock-LLM 画像,探索过程永远不会发出计费的 Provider 请求。
四、录制测试:把探索性浏览器会话变成草稿 spec
4.1 一键录制(Mock 画像)
npm run e2e:record对照 e2e/setup/record.js 的源码,这条命令的实际行为是:
npm run e2e:prepare构建应用;- 若
E2E_BASE_URL(npm 脚本固定为http://localhost:3333,避免与 3080 上的常规开发服务器冲突)未就绪,spawn e2e/setup/start-server.js 启动带进程内假 LLM 的测试服务器; writeStorageState()驱动 headless Chromium 完成注册(失败则回退登录),把认证态写入e2e/storageState.json;- 以
--target=playwright-test --test-id-attribute=data-testid --load-storage e2e/storageState.json打开 Playwright codegen,起始页/c/new。
原始录制写入e2e/recordings/,该目录被 git 忽略。
4.2 本地真实配置画像
要对真实的本地 LibreChat 配置(而非 mock 画像)录制:
npm run e2e:record:local4.3 record.js 的完整参数
node e2e/setup/record.js --url=http://localhost:3080/c/new node e2e/setup/record.js --profile=local --no-output node e2e/setup/record.js --auth-only node e2e/setup/record.js --output=e2e/recordings/settings-draft.spec.ts结合源码中的parseArgs()与printHelp(),完整参数语义为:
| 参数 | 默认值 | 说明 |
|---|---|---|
--profile mock\|local | mock | mock 画像会生成运行时配置、清洗凭据并注入假模型钩子;local 直接使用本地环境 |
--url <url> | <baseURL>/c/new | codegen 打开的起始 URL |
--output <path> | e2e/recordings/recording-<时间戳>.spec.ts | 原始录制输出路径 |
--storage <path> | e2e/storageState.json | 认证存储态路径 |
--no-output | — | 只在 codegen 面板展示生成代码,不落盘 |
--auth-only | — | 启动服务器、写入存储态后直接退出(不打开 codegen) |
五、LLM 辅助循环:从录制草稿到可提交的测试
仓库给"让 LLM 用 Computer Use 操作 headed Playwright 浏览器来录制"的完整工作流定了 7 步标准:
- 启动
npm run e2e:record; - 让 LLM 用 Computer Use 操作该 headed 浏览器;
- 捕获完工作流后停止 codegen;
- 把
e2e/recordings/中有价值的部分搬进e2e/specs/mock/下的已提交 spec; - 用 role、label、文本或
data-testid定位器替换脆弱的生成选择器(codegen 本身就以--test-id-attribute=data-testid启动,优先产出 testid 定位器); - 添加证明行为成立的断言,而不仅是断言被点击的路径;
- 用
npm run e2e:mock -- <spec name>运行完成的 spec 验证。
关键原则:生成的录制是草稿,不是最终测试。提交版本应尽可能复用 e2e/specs/mock/helpers.ts 中的共享辅助函数,等待网络或可见 UI 状态而不是固定 sleep,并保持测试数据确定性——这与 mock 画像"每个 spec 使用全新注册用户 + 单 worker"的隔离设计是一致的:确定性数据配合e2e/recordings/之外的受控输入(fake-model 的标记协议),才能被 CI 稳定复现。
六、关键文件地图与延伸阅读
| 关注点 | 文件 |
|---|---|
| 本文主体文档 | e2e/README.md |
| Mock 画像主配置 | e2e/playwright.config.mock.ts |
| Redis 传输专项配置 | e2e/playwright.config.redis.ts |
| 零凭据配置模板 | e2e/config/librechat.e2e.yaml |
| 流存储环境变量分发 | e2e/setup/env.ts |
| 进程内假 LLM(标记协议) | e2e/setup/fake-model.js |
| 测试服务器启动(fail-closed Redis 检查) | e2e/setup/start-server.js |
| 录制脚本 | e2e/setup/record.js |
| Bombadil harness | e2e/bombadil/harness.spec.ts |
| Mock spec 目录(约 60 个场景) | e2e/specs/mock/ |
| npm 脚本入口 | package.json |
适用前提与限制小结:本地运行需先构建前端(e2e:prepare),MongoDB 默认连mongodb://127.0.0.1:27017/LibreChat-e2e(可用MONGO_URI覆盖,或依赖E2E_USE_MEMORY_MONGO自动内存模式);Redis 泳道要求 6379 端口有可用 Redis;模型 fixture 录制模式(E2E_MODEL_FIXTURES=record)是唯一会接触真实 Provider 的路径,且被 playwright.config.mock.ts 中的多重防护严格限制在指定 spec 与命名白名单内,普通开发者日常只需面对零凭据的 mock 泳道即可。
【免费下载链接】LibreChatEnhanced ChatGPT Clone: Features Agents, MCP, Skills, DeepSeek, Anthropic, AWS, OpenAI, Responses API, Azure, Groq, o1, GPT-5, Mistral, OpenRouter, Vertex AI, Gemini, Artifacts, AI model switching, message search, Code Interpreter, langchain, DALL-E-3, OpenAPI Actions, Functions, Secure Multi-User Auth, Presets, open-source for self-hosting. Active项目地址: https://gitcode.com/GitHub_Trending/li/LibreChat
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考