22
33为 [ DORA] ( https://dora-rs.ai/ ) 数据流框架提供单元测试和集成测试支持的 Rust 工具库。
44
5-
65## 三层测试支持
76
87```
1312│ Layer 2: TestSource / TestSink — 集成测试 │
1413│ 扔进真实 YAML dataflow,端到端验证 │
1514├──────────────────────────────────────────────────┤
16- │ Layer 3: Record / Replay — 回归测试 (开发中) │
15+ │ Layer 3: Record / Replay — 回归测试 │
1716│ 录制一次真实运行 → 之后每次重放比对 │
1817└──────────────────────────────────────────────────┘
1918```
@@ -27,16 +26,16 @@ use dora_test_utils::NodeHarness;
2726
2827#[test]
2928fn test_my_node () {
30- let mut harness = NodeHarness :: new (). expect (" 创建 harness 失败 " );
29+ let mut harness = NodeHarness :: new (). expect (" failed to create harness " );
3130
32- // 往节点注入数据
31+ // Buffer input data (deferred init — node created on first tick)
3332 harness . send_data (" image" , serde_json :: json! ([1 , 2 , 3 ]));
3433
35- // 跑到结束,收集所有事件
34+ // Run to completion, collect all events
3635 let events = harness . run_to_completion ();
3736 assert! (! events . is_empty ());
3837
39- // 节点产生的输出也能拿到
38+ // Collect node outputs
4039 let outputs = harness . recv_output (" result" );
4140 assert! (outputs . is_some ());
4241}
@@ -53,51 +52,74 @@ fn test_my_node() {
5352| ` echo-node ` | 透传:收到啥发啥,用于验证链路通不通 |
5453| ` classifier-node ` | 按阈值分流:Int64 数值 > 阈值发到 high,否则发到 low |
5554
56- ** 用法示例** — 写一个 YAML dataflow:
57-
5855``` yaml
5956nodes :
6057 - id : test-source
6158 path : ./target/debug/test-source
6259 args : " --output data:source.json"
6360 outputs : [data]
64-
6561 - id : my-node
6662 path : ./target/debug/my-node
6763 inputs :
6864 data : test-source/data
6965 outputs : [result]
70-
7166 - id : test-sink
7267 path : ./target/debug/test-sink
7368 inputs :
7469 result : my-node/result
7570 args : " --expected-file expected.json --output-file result.json"
7671` ` `
7772
78- 然后一行命令跑起来:
79-
8073` ` ` bash
8174dora run my-dataflow.yml --stop-after 10s
8275cat result.json # {"match": true} 或 {"match": false, "differences": [...]}
8376```
8477
85- ** 多输出模式 ** (Week 8 新增):
78+ ### Layer 3: RecordSession / ReplaySession(回归测试)
8679
87- ``` bash
88- test-source --output data_a:a.json --output data_b:b.json
80+ 录制一次真实 dataflow 运行的输出,之后每次重放自动比对,检测回归:
81+
82+ ``` rust
83+ use dora_test_utils :: record :: RecordSession ;
84+ use std :: time :: Duration ;
85+
86+ // ── 录制基线 ──
87+ let recording = RecordSession :: attach (" dataflow.yml" )?
88+ . record_sink (" test-sink" , " sink_output.json" )
89+ . with_timeout (Duration :: from_secs (10 ))
90+ . run ()? ;
91+ recording . save (" baseline.json" )? ;
92+
93+ // ── 重放比对 ──
94+ let result = ReplaySession :: load (" baseline.json" )?
95+ . replay_sink (" test-sink" , " sink_output.json" )
96+ . with_timeout (Duration :: from_secs (10 ))
97+ . run ()? ;
98+
99+ if result . is_clean () {
100+ println! (" No regressions detected" );
101+ } else {
102+ println! (" {}" , result . diff ()); // structured diff report
103+ result . assert_no_regression (); // panics with formatted diff
104+ }
89105```
90106
91- 一条命令往两个输出通道发不同的数据。
107+ ** 二层比对** :
108+ - Layer 1: 快速 JSON 结构 diff
109+ - Layer 2: Arrow 语义比对(容忍 Int32→Int64 等类型差异)
110+
111+ ** DiffReport** 支持 ` Display ` + ` Serialize ` ,区分四种状态:
112+ - ` Match ` — 完全一致
113+ - ` Mismatch ` — 数据差异(含字段级路径和值)
114+ - ` Missing ` — 基线中有但重放中没有的 sink
115+ - ` Extra ` — 重放中有但基线中没有的 sink
92116
93117## 快速上手
94118
95119### 前置条件
96120
97121- Rust 工具链
98- - 本仓库 clone 到本地
99- - dora CLI(可通过 ` cargo install dora-cli --git https://github.com/dora-rs/dora.git ` 安装,或从 PATH 获取)
100- - 集成测试和 record/replay 测试需要 ` dora run ` 命令
122+ - dora CLI(从 PATH 获取,或 ` cargo install dora-cli --git https://github.com/dora-rs/dora.git ` )
101123
102124### 编译所有二进制
103125
@@ -108,35 +130,45 @@ cargo build --bin test-source --bin test-sink --bin echo-node --bin classifier-n
108130### 跑测试
109131
110132``` bash
111- # 库单元测试(42 个)
133+ # 库单元测试(52 个)
112134cargo test --lib
113135
114136# 端到端测试(5 个)
115137cargo test --test e2e
116138
139+ # Record e2e 测试(4 个,需要 dora CLI)
140+ cargo test --test e2e_record -- --test-threads=1
141+
142+ # Replay e2e 测试(11 个,需要 dora CLI)
143+ cargo test --test e2e_replay -- --test-threads=1
144+
117145# 集成测试(6 个,需要 dora CLI)
118146cargo test --test integration -- --test-threads=1
119147
148+ # 冒烟测试(3 个)
149+ cargo test --test smoke
150+
120151# 全部
121152cargo test
122153```
123154
124- ### 跑演示脚本
155+ ### 演示脚本
125156
126157``` bash
127- bash scripts/demo-week8 .sh
158+ bash scripts/demo-week12 .sh
128159```
129160
130- 一键跑通 3 个流水线(echo、multi-echo、classifier)+ 全部测试 。
161+ 一键展示:RecordSession 录制基线 → ReplaySession 验证无回归 → 制造变更 → 检测到回归 。
131162
132163## 项目结构
133164
134165```
135166src/
136- ├── lib.rs # crate 入口,模块声明
137- ├── harness.rs # NodeHarness — 单元测试驱动
138- ├── source.rs # TestSource — 数据注入库
139- ├── sink.rs # TestSink — 数据比对库
167+ ├── lib.rs # crate 入口,模块声明 + API 稳定性表格
168+ ├── harness.rs # NodeHarness — 单元测试驱动(deferred-init 模型)
169+ ├── source.rs # TestSource — 数据注入库(JSON → Arrow 转换)
170+ ├── sink.rs # TestSink — 数据比对库(语义比对 + 严格比对)
171+ ├── record.rs # RecordSession + ReplaySession + DiffReport
140172├── traits.rs # IntoInputData trait
141173├── mock/ # MockEventStream、MockOutputSender
142174└── bin/
@@ -146,34 +178,48 @@ src/
146178tests/
147179├── fixtures/ # YAML dataflow、测试数据文件
148180├── echo-node.rs # echo-node 二进制(透传)
149- ├── e2e.rs # 端到端测试 (5)
181+ ├── e2e.rs # NodeHarness 端到端测试 (5)
182+ ├── e2e_record.rs # RecordSession e2e 测试 (4)
183+ ├── e2e_replay.rs # ReplaySession e2e 测试 (11)
150184├── integration.rs # 集成测试 (6)
151185└── smoke.rs # 冒烟测试 (3)
152186docs/ # 设计文档、进度记录、upstream PR 计划
187+ scripts/ # Demo 脚本
153188```
154189
190+ ## API 稳定性
191+
192+ | API | 状态 | 说明 |
193+ | -----| ------| ------|
194+ | ` NodeHarness ` | ** Stable** | 单元测试驱动,deferred-init 模型 |
195+ | ` TestSource ` / ` TestSink ` | ** Stable** | JSON/Arrow 数据注入和比对 |
196+ | ` MockEventStream ` / ` MockOutputSender ` | ** Stable** | 无 daemon mock 测试 |
197+ | ` IntoInputData ` trait | ** Stable** | 数据注入 trait |
198+ | ` RecordSession ` / ` Recording ` | ** Experimental** | 录制 dataflow 输出为基线 |
199+ | ` ReplaySession ` / ` ReplayResult ` | ** Experimental** | 重放比对,检测回归 |
200+ | ` DiffReport ` / ` SinkDiff ` / ` FieldDiff ` | ** Experimental** | 结构化差异报告 |
201+
155202## 测试统计(Week 10)
156203
157- | 类别 | 数量 |
158- | ------| ------|
159- | 库单元测试 | 45 |
160- | 端到端测试 (e2e) | 5 |
161- | Record/Replay 测试 (e2e_record) | 4 |
162- | 集成测试 | 6 |
163- | 冒烟测试 | 3 |
164- | Mock 测试 | 6 |
165- | ** 总计** | ** 62** |
166- | CI jobs | 5(check / test / clippy / fmt / integration) |
204+ | 类别 | 数量 | 位置 |
205+ | ------| ------| ------|
206+ | 库单元测试 | 52 | ` src/*.rs ` |
207+ | 端到端测试 (e2e) | 5 | ` tests/e2e.rs ` |
208+ | Record e2e (e2e_record) | 4 | ` tests/e2e_record.rs ` |
209+ | Replay e2e (e2e_replay) | 11 | ` tests/e2e_replay.rs ` |
210+ | 集成测试 | 6 | ` tests/integration.rs ` |
211+ | 冒烟测试 | 3 | ` tests/smoke.rs ` |
212+ | ** 总计** | ** 81** | |
167213
168214## CI
169215
170- 5 个 CI jobs,全绿:
216+ 6 个 CI jobs,全绿:
171217
172218- ** check** — ` cargo check `
173- - ** test** — ` cargo test --lib `
219+ - ** test** — ` cargo test --lib ` + ` cargo test --test e2e ` + ` cargo test --test smoke `
174220- ** clippy** — ` cargo clippy -- -D warnings `
175221- ** fmt** — ` cargo fmt --check `
176- - ** integration-test** — 编译 dora CLI + 跑集成测试
222+ - ** integration-test** — 编译 dora CLI + test 二进制 + 集成测试 + e2e_record + e2e_replay
177223
178224GitHub Actions 配置在 ` .github/workflows/ci.yml ` 。
179225
@@ -187,8 +233,11 @@ GitHub Actions 配置在 `.github/workflows/ci.yml`。
187233| 6 | Echo 流水线 + 集成测试 | ✅ |
188234| 7 | 边界测试 + CI 集成 | ✅ |
189235| 8 | 多输出 + classifier + 3 条流水线 | ✅ |
190- | 9 | flume→tokio mpsc + Record/Replay 设计 | ⏳ |
191- | 10+ | Record/Replay 实现 | ⏳ |
236+ | 9 | flume→tokio mpsc + RecordSession | ✅ |
237+ | 10 | ReplaySession + code review 修复 | ✅ |
238+ | 11 | Upstream PR (a) 代码准备 | ✅ |
239+ | 12 | Demo + 边界测试 + docs polish | 🚧 |
240+ | 13 | Final submission | ⏳ |
192241
193242详见 [ ` docs/PROGRESS.md ` ] ( docs/PROGRESS.md ) 。
194243
0 commit comments