ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

RivetKit 测试实战指南:为 Rivet Actors 编写与运行可靠的驱动测试

RivetKit 测试实战指南:为 Rivet Actors 编写与运行可靠的驱动测试 RivetKit 测试实战指南为 Rivet Actors 编写与运行可靠的驱动测试【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors本文是 Rivet 开源仓库中 testing.md 的工程化详解面向在rivetkit-typescript/packages/rivetkit上做驱动driver层开发与测试的工程师。你将掌握如何用pnpm test精确过滤测试套件、如何在原生 Runtime 与 Wasm Runtime 之间用统一矩阵跑测试、如何定位 Parity Bug、如何规避 Vitest 过滤、[DBG]日志镜像与 Inspector 重放等已知坑点并了解 Rust 客户端测试的布局与助手函数。读完即可直接在本仓库中跑通并扩展 RivetKit 测试。一、RivetKit 测试的运行入口与基本姿势RivetKit 的 TypeScript 实现位于rivetkit-typescript/packages/rivetkit其package.json中把测试命令定义为test: vitest run, test:watch: vitest, test:platforms: pnpm run build RIVETKIT_INCLUDE_PLATFORM_TESTS1 vitest run tests/platforms --passWithNoTests因此所有测试都要在rivetkit-typescript/packages/rivetkit目录下执行基本命令形如# 在 rivetkit-typescript/packages/rivetkit 下执行 pnpm test driver-file-system -t .*Actor KV.*其中pnpm test filter中的filter是 Vitest 的文件名过滤只跑文件名匹配的测试文件-t .*Actor KV.*是测试名过滤进一步把范围收窄到某个具体test/describe块。仓库官方建议把测试输出先重定向到/tmp/下的文件再用 grep 分步检索而不是让日志直接在终端滚动这样可以在一次运行之后用不同关键字多次检索日志定位问题更快pnpm test driver-file-system -t .*Actor KV.* /tmp/rivetkit-driver.log 21 grep -i error /tmp/rivetkit-driver.log grep \[DBG\] /tmp/rivetkit-driver.log需要本地引擎时的启动方式当 RivetKit 驱动测试需要一个本地 engine 实例时使用仓库提供的脚本启动 RocksDB 引擎并放到后台./scripts/run/engine-rocksdb.sh /tmp/rivet-engine-startup.log 21 该脚本的源码engine-rocksdb.sh会切换到仓库根目录并通过cargo run -p rivet-engine --bin rivet-engine -- start启动引擎同时设置了完整的日志环境RUST_BACKTRACEfull \ RUST_LOG${RUST_LOG:-opentelemetry_sdkoff,opentelemetry-otlpinfo,tower::buffer::workerinfo,debug} \ RUST_LOG_TARGET1 \ cargo run -p rivet-engine --bin rivet-engine -- start 21 | tee -i /tmp/rivet-engine.log注意RUST_LOG默认值里包含debug意味着引擎启动时会输出大量调试日志如果后续做时序敏感的测试需要留意[DBG]镜像带来的日志噪音详见下文第四节。驱动测试的进度管理仓库强调RivetKit driver 开发应当按文件组one file group at a time推进进度记录在~/.agents/notes/driver-test-progress.md红绿循环要始终锚定在rivetkit-typescript/packages/rivetkit/tests/driver/下的driver-test-suite.test.ts这类驱动测试上而不是临时切换到只跑 native 的临时测试。这意味着驱动测试是核心 oracle其他测试都围绕它展开。二、驱动测试矩阵一条用例覆盖多种 Runtime 与编码驱动测试并不是每个文件手写一套而是通过describeDriverMatrix(...)把同一批用例自动展开成多维度矩阵。核心实现见 shared-matrix.ts 与 driver-registry-variants.ts。矩阵的三个维度见getDriverMatrixCellsshared-matrix.ts维度可选值说明runtimenative、wasm原生运行时与 Wasm 运行时sqliteBackendlocal、remoteSQLite 本地后端与远程后端encodingbare、cbor、json协议编码方式其中runtime wasm与sqliteBackend local的组合会被跳过Wasm 运行时强制使用remote后端所以默认矩阵实际展开为(native × local × 3 编码) (native × remote × 3 编码) (wasm × remote × 3 编码)共 9 个单元。矩阵还支持用环境变量覆盖任意维度便于 CI 或本机缩小范围见applyDriverMatrixEnv# 只跑 native 运行时 RIVETKIT_DRIVER_TEST_RUNTIMEnative pnpm test ... # 只跑 bare 编码 remote sqlite RIVETKIT_DRIVER_TEST_ENCODINGbare RIVETKIT_DRIVER_TEST_SQLITEremote pnpm test ... # 矩阵并行执行默认是 describe.sequential 串行 RIVETKIT_DRIVER_TEST_PARALLEL1 pnpm test ...RIVETKIT_DRIVER_TEST_RUNTIME允许native、wasm逗号分隔RIVETKIT_DRIVER_TEST_ENCODING允许bare、cbor、json逗号分隔RIVETKIT_DRIVER_TEST_SQLITE允许local、remote逗号分隔驱动测试文件的入口如actor-kv.test.ts、actor-workflow.test.ts等位于 tests/driver/会调用describeDriverMatrix(suiteName, defineTests, options)。测试名称会自动嵌套为三层结构suiteName └── static registry └── runtime (native) / sqlite (local) / encoding (bare) └── 具体 test ...Harness 如何拉起真实 Runtime矩阵的每个单元通过 shared-harness.ts 创建独立的运行时进程createNativeDriverTestConfig(...)会先getOrStartSharedEngine()获取/启动共享引擎然后startNativeDriverRuntime以node --import tsx方式 spawn 一个 native fixture 子进程tests/fixtures/driver-test-suite-runtime.ts注入RIVET_TOKEN、RIVET_NAMESPACE、RIVETKIT_DRIVER_REGISTRY_PATH、RIVETKIT_TEST_ENDPOINT、RIVETKIT_TEST_POOL_NAME、RIVETKIT_TEST_SQLITE_BACKEND等环境变量启动流程会先创建 namespacePOST /namespaces、upsert runner configPUT /runner-configs/...再轮询GET /envoys等待 envoy 注册完成30 秒超时见 shared-harness.tsstartWasmDriverRuntime则有意剔除RIVET_ENGINE_BINARY/RIVET_ENGINE_BINARY_PATH这两个环境变量避免 Wasm 运行时误以为自己可以再拉起一个引擎子进程见 shared-harness.ts 的注释harness 还提供hardCrashRuntime()SIGKILL用于崩溃恢复类测试以及getRuntimeOutput()用于断言运行时输出。此外共享引擎的超时与启动配置集中在 shared-engine.ts并导出一个TEST_ENGINE_TOKEN供所有子进程使用。三、Vitest 过滤陷阱-t正则必须包含外层套件名这是文档明确警告的高频坑点当用-t过滤单个驱动文件时正则里必须包含describeDriverMatrix(...)定义的外层套件名并且要在static registry encoding (...)之前出现否则 Vitest 会“愉快地”跳过整个文件而不报错。原因是驱动测试的测试名是嵌套的-t匹配的是完整测试路径suite/test 名称链。例如actor-kv.test.ts里是describeDriverMatrix(actor kv, (c) { ... });过滤时应当写pnpm test actor-kv -t actor kv.*Actor KV.*而不是只写# 错误示范Vitest 可能直接跳过整个文件 pnpm test actor-kv -t .*Actor KV.*一个实用做法是先用不带-t的方式跑一次把完整的测试路径名称打出来确认外层套件名后再构造正则。四、Parity Bug 工作流以 TypeScript 驱动测试为唯一 OracleRivetKit 存在 TS 实现与原生Rust/Wasm实现并存的阶段因此会有“同一种行为在两套实现下表现不一致”的 parity bug。仓库规定了一套严格的修复顺序先用 TypeScript 驱动套件复现问题rivetkit-typescript/packages/rivetkit下的 driver 测试对照原始 TypeScript 实现的行为其基准 ref 是feat/sqlite-vfs-v2修改 native/Rust 实现以匹配 TS 行为在补充底层 native 测试之前先重跑同一个 TypeScript 驱动测试确认修复生效。这条规则的核心思想是驱动测试是行为契约native 层只是实现任何修复都必须先通过“契约”验证再考虑是否追加 native 单测避免两边各写各的导致行为继续漂移。Harness 调试日志镜像shared-harness.ts 会把子进程的 stderr/stdout 完整镜像到内存中logs.stdout/logs.stderr其中包含 stderr 中以[DBG]开头的行供断言使用。当DRIVER_RUNTIME_LOGS1时这些输出还会以[RT.OUT]/[RT.ERR]前缀实时打印。因此在时序敏感的 driver 测试重跑之前务必先移除临时调试埋点比如临时加的[DBG]打印否则日志刷屏会导致 hibernation休眠类测试超时想要检查[DBG]内容时用第二节的“管道到 /tmp 再 grep”方式检索而不是直接依赖终端输出。五、Inspector 重放测试的三条经验Inspector 是观察 Actor 工作流状态的关键接口相关测试在 actor-inspector.test.ts还有 inspector-workflow-surface.test.ts。文档沉淀了三条必须遵守的经验POST /inspector/workflow/replay可能合法地返回空的工作流历史快照当从开头重放时该端点会先清空已持久化的历史再重启工作流因此断言“重放后历史不为空”会得到空快照这是正常行为不是 bug。对应测试见 actor-inspector.test.ts。判断“工作流是否在飞行中in flight”要用 inspector 的workflowState其取值是pending/running。不要依赖entryMetadata.status或runHandlerActive来判断因为它们在部分编码bare/cbor/json之间会滞后或互相不一致。相关断言示例actor-inspector.test.tsexpect([pending, running]).toContain(data.workflowState);查询型 inspector 端点在 Actor 启动阶段可能各自命中guard.actor_ready_timeout这是启动期的瞬时错误不代表测试失败。因此在写 active-workflow 驱动测试时要轮询你最终断言的那个具体端点而不是等一个 inspector 路由就绪后再对另一个路由做单次 fetch——否则会因为 ready 时序不同而误报超时。actor_ready_timeout的触发代码可见 actor-inspector.test.ts服务端的workflowState计算与/inspector/workflow/replay路由实现位于 native.ts 与 native.ts 附近。六、Rust 测试布局inline 测试的搬移与集成测试目录RivetKit 的 Rust 侧遵循以下布局约定inline 测试搬出src/时的 shim 技巧把 Rust inline 测试#[cfg(test)] mod tests从src/移到独立文件时必须在源码里保留一个极小的 shim#[cfg(test)] #[path tests/foo.rs] mod tests;这样搬移后的文件仍然能通过mod tests访问私有模块成员而不需要把内部符号的可见性扩大pub(crate)→pub到生产代码里避免污染公开 API。集成测试目录归属rivetkit-client的 Cargo 集成测试应放在rivetkit-rust/packages/client/tests/而src/tests/e2e.rs不会被 Cargo 编译它不在tests/目录也没有被mod引入不要误以为修改它就能跑起来。这是文档明确标注的布局约束。七、Rust 客户端测试助手fetch、事件订阅与元数据查找Rust 客户端rivetkit-rust/packages/client为测试提供了一组固定的 API 形态原始 HTTPhandle.fetch(...)客户端原始 HTTP 请求通过fetch(path, Method, HeaderMap, OptionBytes)发出底层由RemoteManager::send_request路由到 Actor Gateway 的/request端点签名见 remote_manager.rspub async fn send_request( self, target: GatewayTarget, path: str, method: Method, headers: HeaderMap, body: OptionBytes, ) - Resultreqwest::Response其中GatewayTarget决定请求如何路由GatewayTarget::Query直接由 gateway 从 URL 解析目标不携带 Actor 头而GatewayTarget::Direct { actor_id }会附加HEADER_RIVET_TARGET: actor与HEADER_RIVET_ACTOR: actor_id两个请求头。测试中通过RemoteManager的ActorQuery解析GetForKey/GetOrCreateForKey/Create来获得目标。事件订阅SubscriptionHandle与once_event事件订阅返回SubscriptionHandle见 connection.rs支持on_event持续监听与once_event只取一次pub async fn on_eventF(self: ArcSelf, event_name: str, callback: F) - SubscriptionHandle pub async fn once_eventF(...) - SubscriptionHandle文档强调once_event在收到第一个事件后应主动移除自己的监听器并发送 unsubscribe避免事件订阅在测试进程中残留、造成后续测试串扰。实现见 connection.rs。测试必须关闭元数据查找disable_metadata_lookup(true)Rust 客户端 mock 测试必须调用ClientConfig::disable_metadata_lookup(true)除非测试服务器实现了/metadata端点。否则客户端在首次请求时会尝试查询 namespace/pool 元数据而 mock 服务器通常没有实现该路由导致请求失败。API 定义与默认值见 client.rs 与 client.rspub fn disable_metadata_lookup(mut self, disable: bool) - Self { self.disable_metadata_lookup disable; self }RemoteManager在解析配置时会读取该开关见 remote_manager.rs关闭后直接使用显式提供的 endpoint/credentials不做额外的元数据解析。八、Fixtures 与前端测试Fixtures 的边界RivetKit 测试夹具fixtures应限定在 engine-only 运行时范围内不要引入外部依赖或混合多运行时矩阵的共享夹具。同时优先写针对性的集成测试位于rivetkit-typescript/packages/rivetkit/tests/下而不是把用例塞进共享的多驱动矩阵——矩阵是给 driver 行为契约用的针对性集成测试更适合验证具体集成点。fixtures 目录见 rivetkit-typescript/packages/rivetkit/fixtures/。前端测试与浏览器自动化仓库的前端测试约定使用agent-browser技能来交互并测试 examples 中的 Web UI实现自动化浏览器测试如果修改了前端 UI应在收尾前用 Agent Browser CLI 重新截图并附上简短说明。设计层规则真实基础设施禁用 mock根 CLAUDE.md 的 Testing Guidelines 定义了设计层红线禁止vi.mock/jest.mock及模块级 mock测试必须面向真实基础设施Docker 容器、真实数据库、真实文件系统LLM 调用可用copilotkit/llmock跑一个 mock LLM 服务器协议层的测试替身如 ACP adapter要写成以真实进程运行的手写脚本仅简单的回调跟踪可用vi.fn()。这与本文档“驱动测试以真实引擎 真实运行时为准”的取向完全一致。九、实践清单把以上规则浓缩成一份可直接照做的清单在rivetkit-typescript/packages/rivetkit下跑测试pnpm test file-filter -t 外层套件名.*目标用例.*需要本地引擎时./scripts/run/engine-rocksdb.sh /tmp/rivet-engine-startup.log 21 测试日志先落盘/tmp/再多次 grep 不同关键字修 parity bugTS driver 测试复现 → 对照feat/sqlite-vfs-v2→ 修 native/Rust → 先重跑 TS 测试再补 native 测试-t正则记得带上describeDriverMatrix的外层套件名时序敏感重跑前清掉[DBG]临时埋点inspector 断言用workflowStatepending/running重放空快照是合法行为ready 类测试轮询目标端点本身Rust 侧inline 测试搬移保留#[cfg(test)] #[path...] mod tests;shim集成测试放rivetkit-rust/packages/client/tests/mock 测试加disable_metadata_lookup(true)once_event用后即退订前端改动后用 agent-browser 做浏览器级验证并更新截图。这套约定让 RivetKit 在“TS 实现 ↔ native/Wasm 实现”并存、多编码多运行时交叉的复杂局面下仍然能用一个锚点driver 测试锁住行为契约值得在阅读源码时对照验证。【免费下载链接】actorsRivet Actors are the primitive for stateful workloads. Built for AI agents, collaborative apps, and durable execution.项目地址: https://gitcode.com/GitHub_Trending/riv/actors创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表