use crate::channels::telegram::handler::{FlowLine, render_flow_html};
fn tline(label: &str, context: &str) -> FlowLine {
FlowLine::Tool {
label: label.to_string(),
context: context.to_string(),
}
}
#[test]
fn empty_group_renders_nothing() {
assert_eq!(render_flow_html(&[]), "");
}
#[test]
fn single_tool_renders_plain_line_without_blockquote() {
let out = render_flow_html(&[tline("✅ bash", "git status")]);
assert_eq!(out, "<b>✅ bash</b> <code>git status</code>");
assert!(!out.contains("<blockquote"));
}
#[test]
fn single_tool_without_context_omits_trailing_space() {
let out = render_flow_html(&[tline("⚙️ web_search", "")]);
assert_eq!(out, "<b>⚙️ web_search</b>");
}
#[test]
fn multiple_tools_render_expandable_blockquote() {
let out = render_flow_html(&[
tline("✅ bash", "cargo fmt"),
tline("✅ read_file", "handler.rs"),
tline("❌ grep", "pattern"),
]);
assert!(out.starts_with("<blockquote expandable><b>3 tool calls</b>\n"));
assert!(out.ends_with("</blockquote>"));
assert!(out.contains("<b>✅ bash</b> <code>cargo fmt</code>"));
assert!(out.contains("<b>✅ read_file</b> <code>handler.rs</code>"));
assert!(out.contains("<b>❌ grep</b> <code>pattern</code>"));
}
#[test]
fn blocks_are_separated_by_blank_lines() {
let out = render_flow_html(&[
tline("✅ bash", "cargo fmt"),
FlowLine::Text("Reformatted three files.".to_string()),
tline("✅ read_file", "handler.rs"),
]);
assert!(out.starts_with("<blockquote expandable><b>2 tool calls</b>\n\n"));
assert!(out.contains("<b>✅ bash</b> <code>cargo fmt</code>\n\nReformatted three files."));
assert!(
out.contains("Reformatted three files.\n\n<b>✅ read_file</b> <code>handler.rs</code>")
);
}
#[test]
fn tool_context_renders_as_monospace() {
let out = render_flow_html(&[
tline("✅ read", "src/channels/telegram/handler.rs"),
tline("✅ bash", "cargo clippy --all-features"),
]);
assert!(out.contains("<b>✅ read</b> <code>src/channels/telegram/handler.rs</code>"));
assert!(out.contains("<b>✅ bash</b> <code>cargo clippy --all-features</code>"));
}
#[test]
fn intermediate_text_renders_inline_markdown() {
let out = render_flow_html(&[
tline("✅ bash", "grep foo"),
FlowLine::Text("Calling `analyze_image` then **committing** the *fix*.".to_string()),
]);
assert!(out.contains("<code>analyze_image</code>"));
assert!(out.contains("<b>committing</b>"));
assert!(out.contains("<i>fix</i>"));
assert!(!out.contains("`analyze_image`"));
assert!(!out.contains("**committing**"));
}
#[test]
fn never_emits_details_tags() {
let out = render_flow_html(&[tline("✅ a", "x"), tline("✅ b", "y")]);
assert!(!out.contains("<details>"));
assert!(!out.contains("<summary>"));
}
#[test]
fn escapes_html_in_labels_and_context() {
let out = render_flow_html(&[
tline("✅ bash", "grep '<details>' & \"stuff\""),
tline("✅ edit_file", "a < b > c"),
]);
assert!(out.contains("grep '<details>' & \"stuff\""));
assert!(out.contains("a < b > c"));
assert!(!out.contains("'<details>'"));
}
#[test]
fn tool_plus_text_folds_into_one_blockquote() {
let out = render_flow_html(&[
tline("✅ bash", "git status"),
FlowLine::Text("Checked the tree, all clean.".to_string()),
tline("✅ read_file", "handler.rs"),
]);
assert!(out.starts_with("<blockquote expandable><b>2 tool calls</b>\n"));
assert!(out.ends_with("</blockquote>"));
assert!(out.contains("<b>✅ bash</b> <code>git status</code>"));
assert!(out.contains("Checked the tree, all clean."));
assert!(out.contains("<b>✅ read_file</b> <code>handler.rs</code>"));
assert!(!out.contains("<details>"));
}
#[test]
fn text_only_flow_uses_processing_log_header() {
let out = render_flow_html(&[FlowLine::Text("Switching provider…".to_string())]);
assert!(out.starts_with("<blockquote expandable><b>Processing log</b>\n"));
assert!(out.contains("Switching provider…"));
assert!(!out.contains("tool calls"));
}
#[test]
fn intermediate_text_is_html_escaped_in_flow() {
let out = render_flow_html(&[
tline("✅ bash", "echo hi"),
FlowLine::Text("result: <b>bold</b> & <script>alert(1)</script>".to_string()),
]);
assert!(out.contains("<b>bold</b> & <script>"));
assert!(!out.contains("<script>"));
}
#[test]
fn blank_text_entries_are_dropped() {
let out = render_flow_html(&[tline("✅ bash", "x"), FlowLine::Text(" ".to_string())]);
assert_eq!(out, "<b>✅ bash</b> <code>x</code>");
}
#[test]
fn empty_flow_renders_nothing() {
assert_eq!(render_flow_html(&[]), "");
}