use rho_providers::model::{ContentBlock, Message, ToolCall, ToolResult};
use serde_json::json;
use super::*;
use crate::tui::syntax::warm_syntax_set;
fn user_text(text: &str) -> Message {
Message::User(vec![ContentBlock::Text(text.into())])
}
fn tool_call_path(path: &str) -> Message {
Message::Assistant(vec![ContentBlock::ToolCall(ToolCall {
id: "call".into(),
name: "read_file".into(),
arguments: json!({"path": path}),
})])
}
#[test]
fn warmup_plan_collects_fences_and_skips_markdown() {
let plan = SyntaxWarmupPlan::from_messages(&[user_text(
"```rust\nfn x() {}\n```\n```md\n# h\n```\n```ts\nconst n = 1\n```\n```mermaid\ngraph TD\n```\n```plain\nhi\n```",
)]);
assert_eq!(
plan,
SyntaxWarmupPlan {
tokens: vec!["rust".into(), "ts".into()],
paths: Vec::new(),
}
);
}
#[test]
fn warmup_plan_uses_tool_call_path_not_result_body() {
let plan = SyntaxWarmupPlan::from_messages(&[
tool_call_path("src/lib.rs"),
Message::Assistant(vec![ContentBlock::ToolCall(ToolCall {
id: "edit".into(),
name: "edit".into(),
arguments: json!({"file_path": "foo.ts", "old_string": "bar.py"}),
})]),
tool_call_path("README.md"),
Message::ToolResult(ToolResult {
id: "call".into(),
ok: true,
content: r#"also ignored.ts and {"path":"leaked.go"}"#.into(),
}),
]);
assert_eq!(
plan,
SyntaxWarmupPlan {
tokens: Vec::new(),
paths: vec!["src/lib.rs".into(), "foo.ts".into()],
}
);
}
#[test]
fn warmup_names_dedup_skip_markdown_and_cap() {
warm_syntax_set();
let rust = syntax_name_for_language("rust").expect("bundled rust syntax");
assert_eq!(syntax_name_for_path("src/lib.rs"), Some(rust));
let candidates = [
"rust", "ts", "py", "go", "bash", "java", "c", "cpp", "ruby", "php", "swift", "kotlin",
"lua", "json", "yaml", "toml", "html", "css", "sql", "markdown",
];
let resolvable = candidates
.iter()
.filter(|token| should_warmup_token(token) && syntax_name_for_language(token).is_some())
.count();
assert!(
resolvable > MAX_WARMED_SYNTAXES,
"need more bundled languages than the cap, got {resolvable}"
);
let fences = candidates
.iter()
.map(|token| format!("```{token}\n"))
.collect::<String>();
let names: Vec<_> = planned_warmups(&SyntaxWarmupPlan::from_messages(&[
user_text(&fences),
tool_call_path("src/lib.rs"),
tool_call_path("README.md"),
]))
.into_iter()
.map(|(name, _)| name)
.collect();
assert_eq!(names.iter().filter(|name| **name == rust).count(), 1);
assert!(
names
.iter()
.all(|name| !name.eq_ignore_ascii_case("markdown")),
"{names:?}"
);
assert_eq!(names.len(), MAX_WARMED_SYNTAXES);
}