From e4460467443194b8281e9dea606c905e53a83e25 Mon Sep 17 00:00:00 2001 From: Austin Schaefer Date: Tue, 18 Aug 2026 11:43:14 +0200 Subject: [PATCH] Add unit tests for the decomposed max-turns recovery helpers Covers truncate, assistant_text, tool_result_text, and partial_findings_from_history in isolation (fallback text, ordering, truncation, and filtering out non-text content). gather_findings/ write_report/research still need a live ollama client and aren't covered here. --- deep_research/Cargo.toml | 3 ++ deep_research/src/core.rs | 104 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 102 insertions(+), 5 deletions(-) diff --git a/deep_research/Cargo.toml b/deep_research/Cargo.toml index 9c4e5b8..179c84c 100644 --- a/deep_research/Cargo.toml +++ b/deep_research/Cargo.toml @@ -17,3 +17,6 @@ serde = { workspace = true } tokio = { workspace = true } tracing = { workspace = true } tracing-subscriber = { workspace = true } + +[dev-dependencies] +serde_json = "1" diff --git a/deep_research/src/core.rs b/deep_research/src/core.rs index 300ecaf..53ff559 100644 --- a/deep_research/src/core.rs +++ b/deep_research/src/core.rs @@ -121,11 +121,10 @@ fn partial_findings_from_history(chat_history: &[Message]) -> String { }) .collect(); - if sections.is_empty() { - "The researcher exhausted its turn budget before gathering any usable evidence." - .to_string() - } else { - sections.join("\n\n") + match sections.is_empty() { + true => "The researcher exhausted its turn budget before gathering any usable evidence." + .to_string(), + false => sections.join("\n"), } } @@ -313,3 +312,98 @@ async fn write_report( None => report, }) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn truncate_leaves_short_text_untouched() { + assert_eq!(truncate("hello", 10), "hello"); + } + + #[test] + fn truncate_leaves_exact_length_text_untouched() { + assert_eq!(truncate("hello", 5), "hello"); + } + + #[test] + fn truncate_cuts_long_text_and_marks_it() { + assert_eq!(truncate("hello world", 5), "hello [...truncated]"); + } + + #[test] + fn assistant_text_extracts_text_blocks() { + let message = Message::assistant("found it"); + assert_eq!(assistant_text(&message), vec!["found it".to_string()]); + } + + #[test] + fn assistant_text_ignores_tool_calls() { + let message = Message::Assistant { + id: None, + content: rig::OneOrMany::one(AssistantContent::tool_call( + "call-1", + "search_web", + serde_json::json!({ "query": "test" }), + )), + }; + assert!(assistant_text(&message).is_empty()); + } + + #[test] + fn assistant_text_ignores_non_assistant_messages() { + assert!(assistant_text(&Message::user("hi")).is_empty()); + assert!(assistant_text(&Message::system("be careful")).is_empty()); + } + + #[test] + fn tool_result_text_extracts_and_truncates() { + let short = Message::tool_result("call-1", "short result"); + assert_eq!(tool_result_text(&short, 100), vec!["short result".to_string()]); + + let long = Message::tool_result("call-2", "0123456789"); + assert_eq!(tool_result_text(&long, 5), vec!["01234 [...truncated]".to_string()]); + } + + #[test] + fn tool_result_text_ignores_non_tool_result_content() { + assert!(tool_result_text(&Message::user("plain text, no tool result"), 100).is_empty()); + assert!(tool_result_text(&Message::assistant("also ignored"), 100).is_empty()); + } + + #[test] + fn partial_findings_from_history_falls_back_when_nothing_usable() { + let history = vec![ + Message::system("preamble"), + Message::Assistant { + id: None, + content: rig::OneOrMany::one(AssistantContent::tool_call( + "call-1", + "search_web", + serde_json::json!({ "query": "test" }), + )), + }, + ]; + + assert_eq!( + partial_findings_from_history(&history), + "The researcher exhausted its turn budget before gathering any usable evidence." + ); + } + + #[test] + fn partial_findings_from_history_collects_assistant_text_and_tool_results_in_order() { + let history = vec![ + Message::assistant("Checking sources..."), + Message::tool_result("call-1", "Result A [1]"), + Message::assistant("Cross-checking..."), + Message::tool_result("call-2", "Result B [2]"), + ]; + + assert_eq!( + partial_findings_from_history(&history), + "Checking sources...\nResult A [1]\nCross-checking...\nResult B [2]" + ); + } +}