231 lines
5.7 KiB
Markdown
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!
|
|
```
|