# ADR-023: Multi-Layer Testing Strategy

**Status**: Accepted | Implemented
**Date**: 2024-11-01
**Deciders**: Quality Assurance Team
**Technical Story**: Building confidence through unit, integration, and real-database tests

---

## Decision

Implementar **multi-layer testing**: unit tests (inline), integration tests (tests/ dir), real DB connections.

---

## Rationale

1. **Unit Tests**: Fast feedback on logic changes
2. **Integration Tests**: Verify components work together
3. **Real DB Tests**: Catch database schema/query issues
4. **218+ Tests**: Comprehensive coverage across 13 crates

---

## Alternatives Considered

### ❌ Unit Tests Only
- **Pros**: Fast
- **Cons**: Miss integration bugs, schema issues

### ❌ Integration Tests Only
- **Pros**: Comprehensive
- **Cons**: Slow, harder to debug

### ✅ Multi-Layer (CHOSEN)
- All three layers catch different issues

---

## Trade-offs

**Pros**:
- ✅ Fast feedback (unit)
- ✅ Integration validation (integration)
- ✅ Real-world confidence (real DB)
- ✅ 218+ tests total coverage

**Cons**:
- ⚠️ Slow full test suite (~5 minutes)
- ⚠️ DB tests require test environment
- ⚠️ More test code to maintain

---

## Implementation

**Unit Tests (Inline)**:
```rust
// crates/vapora-agents/src/learning_profile.rs

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_expertise_score_empty() {
        let profile = TaskTypeLearning {
            agent_id: "test".to_string(),
            task_type: "architecture".to_string(),
            executions_total: 0,
            records: vec![],
            ..Default::default()
        };

        assert_eq!(profile.expertise_score(), 0.0);
    }

    #[test]
    fn test_confidence_weighting() {
        let profile = TaskTypeLearning {
            executions_total: 20,
            ..Default::default()
        };
        assert_eq!(profile.confidence(), 1.0);

        let profile_partial = TaskTypeLearning {
            executions_total: 10,
            ..Default::default()
        };
        assert_eq!(profile_partial.confidence(), 0.5);
    }
}
```

**Integration Tests**:
```rust
// crates/vapora-backend/tests/integration_tests.rs

#[tokio::test]
async fn test_create_project_full_flow() {
    // Setup: create test database
    let db = setup_test_db().await;
    let app_state = create_test_app_state(db.clone()).await;

    // Execute: create project via HTTP
    let response = app_state
        .handle_request(
            "POST",
            "/api/projects",
            json!({
                "title": "Test Project",
                "description": "A test",
            }),
        )
        .await;

    // Verify: response is 201 Created
    assert_eq!(response.status(), 201);

    // Verify: project in database
    let project = db
        .query("SELECT * FROM projects LIMIT 1")
        .await
        .unwrap()
        .take::<Project>(0)
        .unwrap()
        .unwrap();

    assert_eq!(project.title, "Test Project");
}
```

**Real Database Tests**:
```rust
// crates/vapora-backend/tests/database_tests.rs

#[tokio::test]
async fn test_multi_tenant_isolation() {
    let db = setup_real_surrealdb().await;

    // Create projects for two tenants
    let project_1 = db
        .create("projects")
        .content(Project {
            tenant_id: "tenant:1".to_string(),
            title: "Project 1".to_string(),
            ..Default::default()
        })
        .await
        .unwrap();

    let project_2 = db
        .create("projects")
        .content(Project {
            tenant_id: "tenant:2".to_string(),
            title: "Project 2".to_string(),
            ..Default::default()
        })
        .await
        .unwrap();

    // Query: tenant 1 should only see their project
    let results = db
        .query("SELECT * FROM projects WHERE tenant_id = 'tenant:1'")
        .await
        .unwrap()
        .take::<Vec<Project>>(0)
        .unwrap();

    assert_eq!(results.len(), 1);
    assert_eq!(results[0].title, "Project 1");
}
```

**Test Utilities**:
```rust
// crates/vapora-backend/tests/common/mod.rs

pub async fn setup_test_db() -> Surreal<Mem> {
    let db = Surreal::new::<surrealdb::engine::local::Mem>()
        .await
        .unwrap();

    db.use_ns("vapora").use_db("test").await.unwrap();

    // Initialize schema
    init_schema(&db).await.unwrap();

    db
}

pub async fn setup_real_surrealdb() -> Surreal<Ws> {
    // Connect to test SurrealDB instance
    let db = Surreal::new::<Ws>("ws://localhost:8000")
        .await
        .unwrap();

    db.signin(/* test credentials */).await.unwrap();
    db.use_ns("test").use_db("test").await.unwrap();

    db
}
```

**Running Tests**:
```bash
# Run all tests
cargo test --workspace

# Run unit tests only (fast)
cargo test --workspace --lib

# Run integration tests
cargo test --workspace --test "*"

# Run with output
cargo test --workspace -- --nocapture

# Run specific test
cargo test -p vapora-backend test_multi_tenant_isolation

# Coverage report
cargo tarpaulin --workspace --out Html
```

**Key Files**:
- `crates/*/src/` (unit tests inline)
- `crates/*/tests/` (integration tests)
- `crates/*/tests/common/` (test utilities)

---

## Verification

```bash
# Count tests across workspace
cargo test --workspace -- --list | grep "test " | wc -l

# Run all tests with statistics
cargo test --workspace 2>&1 | grep -E "^test |passed|failed"

# Coverage report
cargo tarpaulin --workspace --out Html
# Output: coverage/index.html
```

**Expected Output**:
- 218+ tests total
- All tests passing
- Coverage > 70%
- Unit tests < 5 seconds
- Integration tests < 60 seconds

---

## Consequences

### Testing Cadence
- Pre-commit: run unit tests
- PR: run all tests
- CI/CD: run all tests + coverage

### Test Environment
- Unit tests: in-memory databases
- Integration: SurrealDB in-memory
- Real DB: Docker container (CI/CD only)

### Debugging
- Unit test failure: easy to debug (isolated)
- Integration failure: check component interaction
- DB failure: verify schema and queries

---

## References

- [Rust Testing Documentation](https://doc.rust-lang.org/book/ch11-00-testing.html)
- `crates/*/tests/` (integration tests)
- `crates/vapora-backend/tests/common/` (test utilities)

---

**Related ADRs**: ADR-022 (Error Handling), ADR-004 (SurrealDB)
