test-runner

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Test Runner

测试运行器

Execute and manage Rust tests for the self-learning memory project.
执行并管理自学习记忆项目的Rust测试。

Test Categories

测试分类

CategoryCommand (preferred)FallbackScope
Unit
cargo nextest run --lib
cargo test --lib
Individual functions
Integration
cargo nextest run --test '*'
cargo test --test '*'
End-to-end workflows
Doc
cargo test --doc
(nextest unsupported)Documentation examples
All
cargo nextest run --all
cargo test --all
Complete validation
Mutation
cargo mutants -p memory-core
Test effectiveness
Snapshot
cargo insta test
Output regression
分类推荐命令备选命令测试范围
单元测试
cargo nextest run --lib
cargo test --lib
独立函数
集成测试
cargo nextest run --test '*'
cargo test --test '*'
端到端工作流
文档测试
cargo test --doc
(nextest不支持)文档示例
全部测试
cargo nextest run --all
cargo test --all
完整验证
突变测试
cargo mutants -p memory-core
测试有效性
快照测试
cargo insta test
输出回归

Execution Strategy

执行策略

Step 1: Quick Check (Unit Tests)

步骤1:快速检查(单元测试)

bash
cargo nextest run --lib
  • Fast feedback (< 30s), per-test process isolation
  • Catch basic logic errors
bash
cargo nextest run --lib
  • 快速反馈(< 30秒),每个测试独立进程隔离
  • 捕获基础逻辑错误

Step 2: Integration Tests

步骤2:集成测试

bash
cargo nextest run --test '*'
  • Tests database interactions
  • Requires Turso/redb setup
bash
cargo nextest run --test '*'
  • 测试数据库交互
  • 需要Turso/redb环境配置

Step 3: Full Suite

步骤3:完整测试套件

bash
cargo nextest run --all
cargo test --doc  # doctests separately (nextest limitation)
  • Complete validation before commit
bash
cargo nextest run --all
cargo test --doc  # 文档测试单独执行(nextest限制)
  • 提交前的完整验证

Step 4: Mutation Testing (Periodic)

步骤4:突变测试(定期执行)

bash
cargo mutants -p memory-core --timeout 120 --jobs 4 -- --lib
  • Verifies test suite catches real bugs
  • Run nightly or before releases (ADR-033)
bash
cargo mutants -p memory-core --timeout 120 --jobs 4 -- --lib
  • 验证测试套件能否发现真实bug
  • 在夜间或发布前运行(ADR-033)

Troubleshooting

问题排查

Async/Await Issues

Async/Await问题

Symptom: Test hangs
rust
#[tokio::test]
async fn test_async() {
    let result = async_fn().await;  // Don't forget .await
}
症状:测试挂起
rust
#[tokio::test]
async fn test_async() {
    let result = async_fn().await;  // 不要忘记.await
}

Database Connection

数据库连接

Symptom: Connection refused
  • Check TURSO_URL, TURSO_TOKEN
  • Use test database
症状:连接被拒绝
  • 检查TURSO_URL、TURSO_TOKEN配置
  • 使用测试数据库

Race Conditions

竞态条件

Symptom: Intermittent failures
bash
cargo test -- --test-threads=1
症状:间歇性失败
bash
cargo test -- --test-threads=1

redb Lock Errors

redb锁错误

Symptom: "Database is locked"
  • Use separate DB per test
  • Close transactions promptly
症状:"Database is locked"
  • 为每个测试使用独立数据库
  • 及时关闭事务

Coverage

覆盖率

bash
cargo install cargo-llvm-cov
cargo llvm-cov --html --output-dir coverage
bash
cargo install cargo-llvm-cov
cargo llvm-cov --html --output-dir coverage

Best Practices

最佳实践

  • Isolation: Each test independent
  • Cleanup: Remove test data
  • Speed: < 1s per unit test
  • Naming:
    test_<function>_<scenario>_<expected>
  • AAA pattern: Arrange-Act-Assert in every test
  • Single responsibility: Each test verifies ONE behavior
  • 隔离性:每个测试相互独立
  • 清理:移除测试数据
  • 速度:每个单元测试<1秒
  • 命名:
    test_<函数>_<场景>_<预期结果>
  • AAA模式:每个测试遵循Arrange-Act-Assert(准备-执行-断言)
  • 单一职责:每个测试仅验证一种行为

Advanced: Async Testing Patterns

进阶:异步测试模式

rust
// Time-based testing (paused clock)
#[tokio::test(start_paused = true)]
async fn test_timeout_behavior() {
    let start = tokio::time::Instant::now();
    tokio::time::sleep(Duration::from_secs(5)).await;
    assert!(start.elapsed().as_millis() < 100);
}

// Concurrent operations
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_concurrent_episodes() {
    let memory = Arc::new(setup_memory().await);
    let handles: Vec<_> = (0..10).map(|i| {
        let mem = memory.clone();
        tokio::spawn(async move {
            mem.start_episode(format!("Task {}", i), ctx, type_).await
        })
    }).collect();
    let results = futures::future::join_all(handles).await;
    assert_eq!(results.len(), 10);
}
rust
// 基于时间的测试(暂停时钟)
#[tokio::test(start_paused = true)]
async fn test_timeout_behavior() {
    let start = tokio::time::Instant::now();
    tokio::time::sleep(Duration::from_secs(5)).await;
    assert!(start.elapsed().as_millis() < 100);
}

// 并发操作
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
async fn test_concurrent_episodes() {
    let memory = Arc::new(setup_memory().await);
    let handles: Vec<_> = (0..10).map(|i| {
        let mem = memory.clone();
        tokio::spawn(async move {
            mem.start_episode(format!("Task {}", i), ctx, type_).await
        })
    }).collect();
    let results = futures::future::join_all(handles).await;
    assert_eq!(results.len(), 10);
}

Common Async Pitfalls

常见异步陷阱

BadGood
std::thread::sleep()
tokio::time::sleep().await
memory.start_episode()
memory.start_episode().await
Single-threaded for concurrency
multi_thread
runtime
错误做法正确做法
std::thread::sleep()
tokio::time::sleep().await
memory.start_episode()
memory.start_episode().await
单线程处理并发
multi_thread
运行时

nextest Profiles (.config/nextest.toml)

nextest配置文件(.config/nextest.toml)

toml
[profile.default]
retries = 0
slow-timeout = { period = "60s", terminate-after = 2 }
fail-fast = false

[profile.ci]
retries = 2
slow-timeout = { period = "30s", terminate-after = 3 }
failure-output = "immediate-final"
junit.path = "target/nextest/ci/junit.xml"

[profile.nightly]
retries = 3
slow-timeout = { period = "120s", terminate-after = 2 }
bash
cargo nextest run                  # default profile
cargo nextest run --profile ci     # CI with retries + JUnit
cargo nextest run --profile nightly  # nightly with extended timeouts
toml
[profile.default]
retries = 0
slow-timeout = { period = "60s", terminate-after = 2 }
fail-fast = false

[profile.ci]
retries = 2
slow-timeout = { period = "30s", terminate-after = 3 }
failure-output = "immediate-final"
junit.path = "target/nextest/ci/junit.xml"

[profile.nightly]
retries = 3
slow-timeout = { period = "120s", terminate-after = 2 }
bash
cargo nextest run                  # 使用默认配置
cargo nextest run --profile ci     # CI环境(带重试+JUnit报告)
cargo nextest run --profile nightly  # 夜间构建(带延长超时)

Snapshot Testing (insta)

快照测试(insta)

rust
#[test]
fn test_mcp_tool_response() {
    let response = build_tool_response("search_patterns", &params);
    insta::assert_json_snapshot!(response);
}
bash
cargo insta test     # run snapshot tests
cargo insta review   # accept/reject changes
rust
#[test]
fn test_mcp_tool_response() {
    let response = build_tool_response("search_patterns", &params);
    insta::assert_json_snapshot!(response);
}
bash
cargo insta test     # 运行快照测试
cargo insta review   # 接受/拒绝快照变更

Property-Based Testing

属性化测试

rust
proptest! {
    #[test]
    fn test_episode_id_uniqueness(
        tasks in prop::collection::vec(any::<String>(), 1..100)
    ) {
        let rt = tokio::runtime::Runtime::new().unwrap();
        rt.block_on(async {
            let memory = setup_memory().await;
            let mut ids = HashSet::new();
            for desc in tasks {
                let id = memory.start_episode(desc, ctx, type_).await;
                prop_assert!(ids.insert(id));
            }
        });
    }
}
rust
proptest! {
    #[test]
    fn test_episode_id_uniqueness(
        tasks in prop::collection::vec(any::<String>(), 1..100)
    ) {
        let rt = tokio::runtime::Runtime::new().unwrap();
        rt.block_on(async {
            let memory = setup_memory().await;
            let mut ids = HashSet::new();
            for desc in tasks {
                let id = memory.start_episode(desc, ctx, type_).await;
                prop_assert!(ids.insert(id));
            }
        });
    }
}

Advanced: Episodic Memory Testing

进阶:情景记忆测试

rust
#[tokio::test]
async fn test_complete_episode_lifecycle() {
    let memory = setup_memory().await;
    let id = memory.start_episode("Test task", ctx, TaskType::CodeGen).await;
    memory.log_execution_step(id.clone(), step1).await;
    memory.complete_episode(id.clone(), TaskOutcome::Success, None).await?;
    let episode = memory.get_episode(&id).await?;
    assert_eq!(episode.outcome, TaskOutcome::Success);
}
rust
#[tokio::test]
async fn test_complete_episode_lifecycle() {
    let memory = setup_memory().await;
    let id = memory.start_episode("Test task", ctx, TaskType::CodeGen).await;
    memory.log_execution_step(id.clone(), step1).await;
    memory.complete_episode(id.clone(), TaskOutcome::Success, None).await?;
    let episode = memory.get_episode(&id).await?;
    assert_eq!(episode.outcome, TaskOutcome::Success);
}

Performance Targets

性能指标

OperationTargetActual
Episode Creation< 50ms~2.5 µs
Step Logging< 20ms~1.1 µs
Pattern Extraction< 1000ms~10.4 µs
Memory Retrieval< 100ms~721 µs
操作目标实际值
情景创建< 50ms~2.5 µs
步骤日志记录< 20ms~1.1 µs
模式提取< 1000ms~10.4 µs
内存检索< 100ms~721 µs

References

参考资料

  • ADR-033: Modern Testing Strategy
  • TESTING.md — Full testing guide
Consolidated from these former skills (preserved in
_consolidated/
):
  • test-optimization
    — cargo-nextest, property testing, benchmarking
  • quality-unit-testing
    — AAA pattern, naming conventions, test quality
  • rust-async-testing
    — tokio test patterns, time-based testing
  • episodic-memory-testing
    — episode lifecycle, pattern extraction, reward scoring tests
  • ADR-033: Modern Testing Strategy
  • TESTING.md — 完整测试指南
整合自以下旧技能(已存档于
_consolidated/
):
  • test-optimization
    — cargo-nextest、属性化测试、基准测试
  • quality-unit-testing
    — AAA模式、命名规范、测试质量
  • rust-async-testing
    — tokio测试模式、基于时间的测试
  • episodic-memory-testing
    — 情景生命周期、模式提取、奖励评分测试