Files
2026-05-08 01:59:04 +02:00

231 lines
5.7 KiB
Markdown

# Dirigent Protocol Tests
This directory contains comprehensive tests for the Dirigent protocol and OpenCode adapter.
## Test Files
### `protocol_tests.rs`
Core protocol translation tests that verify OpenCode events are correctly translated to Dirigent protocol events.
**Coverage:**
- Session creation and updates
- User and assistant messages
- Message parts (text, thinking, tool)
- Event stream parsing
- Protocol serialization/deserialization
**Run:** `cargo test --test protocol_tests`
### `deduplication_tests.rs`
Tests for the stateful adapter's deduplication logic, ensuring no duplicate messages or parts appear in the UI.
**Coverage:**
- Duplicate `MessageStarted` filtering
- Duplicate `MessageCompleted` filtering
- Part completion signal (`delta: null`) filtering
- Different part types not being filtered
- Streaming part updates working correctly
- Full tit-tat conversation flow
- Adapter state independence
**Run:** `cargo test --test deduplication_tests`
### `session_list_tests.rs`
Tests for parsing OpenCode session list responses.
**Coverage:**
- Session list array parsing
- Empty session list handling
- Single session deserialization
- Optional fields handling
- Timestamp parsing validation
**Run:** `cargo test --test session_list_tests`
## Fixtures
### `fixtures/sample_events.jsonl`
Sample OpenCode SSE events in JSONL format (one event per line). Used for parsing validation and event stream testing.
### `fixtures/opencode_session_response.json`
Real OpenCode session list response. Used for session deserialization tests.
**Source:** Copied from `/docs/building/opencode_session_response.json`
## Running All Tests
```bash
# Run all protocol tests
cargo test --package dirigent_protocol
# Run with output
cargo test --package dirigent_protocol -- --nocapture
# Run specific test
cargo test --package dirigent_protocol test_tit_tat_flow
# Run tests matching pattern
cargo test --package dirigent_protocol duplicate
```
## Adding New Tests
### For OpenCode Event Translation
Add to `protocol_tests.rs`:
```rust
#[test]
fn test_translate_new_feature() {
let adapter = OpenCodeAdapter::new();
// Create OpenCode event
let oc_event = oc::Event::YourEvent { ... };
// Translate
let result = adapter.translate_event(oc_event);
// Assert
assert!(result.is_ok());
match result.unwrap() {
Event::YourDirigentEvent(data) => {
assert_eq!(data.field, expected_value);
}
_ => panic!("Expected YourDirigentEvent"),
}
}
```
### For Deduplication Logic
Add to `deduplication_tests.rs`:
```rust
#[test]
fn test_new_deduplication_rule() {
let adapter = OpenCodeAdapter::new();
// Send first event
let result1 = adapter.translate_event(first_event);
assert!(result1.is_ok());
// Send duplicate event
let result2 = adapter.translate_event(duplicate_event);
assert!(result2.is_err());
assert!(matches!(result2.unwrap_err(), TranslationError::Duplicate));
}
```
### For New Fixtures
1. Place fixture file in `tests/fixtures/`
2. Use `include_str!` to load it:
```rust
let fixture = include_str!("fixtures/your_file.json");
```
## Test Principles
### Stateful Adapter Pattern
⚠️ **IMPORTANT:** The adapter maintains state, so:
```rust
// ✅ CORRECT: One adapter for entire event stream
let adapter = OpenCodeAdapter::new();
for event in events {
adapter.translate_event(event);
}
// ❌ WRONG: New adapter each time (loses state!)
for event in events {
let adapter = OpenCodeAdapter::new();
adapter.translate_event(event);
}
```
### Testing Duplicates
When testing deduplication:
1. Send the first event → should succeed
2. Send the duplicate event → should fail with `TranslationError::Duplicate`
3. Always use the SAME adapter instance
### Real-World Fixtures
Fixtures should come from actual OpenCode API responses when possible:
- Captures real-world edge cases
- Ensures compatibility with API changes
- Documents actual behavior
## CI Integration
These tests run automatically on:
- Every commit (via `cargo test`)
- Pull requests
- Before releases
**Status:** All tests should pass before merging.
## Coverage Report
```bash
# Install tarpaulin for coverage
cargo install cargo-tarpaulin
# Generate coverage report
cargo tarpaulin --package dirigent_protocol --out Html
```
## Related Documentation
- [SSE Deduplication](../../../docs/building/sse_deduplication.md) - How deduplication works
- [SSE Event Flow Analysis](../../../docs/building/sse_event_flow_analysis.md) - OpenCode event patterns
- [Protocol Abstraction Plan](../../../docs/building/protocol_abstraction_plan.md) - Adapter architecture
## Test Statistics
**Last Updated:** 2025-11-01
- **Total Tests:** 24
- **Deduplication Tests:** 7
- **Session Tests:** 5
- **Protocol Tests:** 12
- **All Passing:** ✅
## Troubleshooting
### Test Fails with "argument #1 of type &OpenCodeAdapter is missing"
**Problem:** You're calling `OpenCodeAdapter::translate_event(event)` as a static method.
**Solution:** Create an adapter instance first:
```rust
let adapter = OpenCodeAdapter::new();
let result = adapter.translate_event(event);
```
### Test Fails with "pattern does not mention field part_id"
**Problem:** The `MessagePartAdded` event now includes a `part_id` field.
**Solution:** Update your pattern match:
```rust
// Before
Event::MessagePartAdded { message_id, part, delta } => { ... }
// After
Event::MessagePartAdded { message_id, part_id: _, part, delta } => { ... }
```
### Deduplication Test Unexpectedly Passes
**Problem:** You're creating a new adapter for each event.
**Solution:** Create ONE adapter and reuse it:
```rust
let adapter = OpenCodeAdapter::new();
adapter.translate_event(event1); // First time
adapter.translate_event(event1); // Should be duplicate!
```