# 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! ```