use std::sync::Arc;
use tokio::sync::Mutex;
fn format_preview(queued: &str, input_content_width: usize) -> String {
let flat = queued.replace('\n', " ");
let max_preview = input_content_width.saturating_sub(25);
if flat.chars().count() > max_preview {
let truncated: String = flat.chars().take(max_preview).collect();
format!("{}...", truncated)
} else {
flat
}
}
#[test]
fn preview_short_message_unchanged() {
let preview = format_preview("hello world", 80);
assert_eq!(preview, "hello world");
}
#[test]
fn preview_newlines_replaced_with_spaces() {
let preview = format_preview("line one\nline two\nline three", 80);
assert_eq!(preview, "line one line two line three");
}
#[test]
fn preview_long_message_truncated_with_ellipsis() {
let long = "a".repeat(200);
let preview = format_preview(&long, 80);
assert!(preview.ends_with("..."));
assert!(preview.len() <= 58);
}
#[test]
fn preview_multibyte_safe() {
let japanese = "こんにちは世界テスト文字列です";
let preview = format_preview(japanese, 40);
assert!(!preview.is_empty());
}
#[test]
fn preview_emoji_safe() {
let emoji = "🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀";
let preview = format_preview(emoji, 40);
assert!(preview.ends_with("..."));
}
#[test]
fn preview_empty_string() {
let preview = format_preview("", 80);
assert_eq!(preview, "");
}
#[test]
fn preview_only_newlines() {
let preview = format_preview("\n\n\n", 80);
assert_eq!(preview, " ");
}
#[test]
fn preview_exact_length_no_truncation() {
let msg = "a".repeat(25);
let preview = format_preview(&msg, 50);
assert_eq!(preview, msg);
assert!(!preview.contains("..."));
}
#[tokio::test]
async fn queue_set_and_take() {
let queue: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
assert!(queue.lock().await.is_none());
*queue.lock().await = Some("follow-up question".to_string());
assert!(queue.lock().await.is_some());
let taken = queue.lock().await.take();
assert_eq!(taken, Some("follow-up question".to_string()));
assert!(queue.lock().await.is_none());
}
#[tokio::test]
async fn queue_overwrite_replaces_previous() {
let queue: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
*queue.lock().await = Some("first".to_string());
*queue.lock().await = Some("second".to_string());
let taken = queue.lock().await.take();
assert_eq!(taken, Some("second".to_string()));
}
#[tokio::test]
async fn queue_recall_clears_both() {
let queue: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
let content = "queued msg".to_string();
*queue.lock().await = Some(content.clone());
let mut preview: Option<String> = Some(content);
let recalled = preview.take().unwrap();
*queue.lock().await = None;
assert_eq!(recalled, "queued msg");
assert!(preview.is_none());
assert!(queue.lock().await.is_none());
}
#[tokio::test]
async fn queue_flush_clears_preview() {
let queue: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
*queue.lock().await = Some("question".to_string());
let mut preview: Option<String> = Some("question".to_string());
if queue.lock().await.take().is_some() {
preview = None;
}
assert!(preview.is_none());
assert!(queue.lock().await.is_none());
}
#[test]
fn ordering_queued_after_tools_before_assistant() {
let messages: Vec<(&str, &str)> = vec![
("tool_group", "3 tool calls"),
("user", "follow-up question"),
("assistant", "Here's my response..."),
];
assert_eq!(messages[0].0, "tool_group");
assert_eq!(messages[1].0, "user");
assert_eq!(messages[2].0, "assistant");
}
#[test]
fn ordering_at_response_complete() {
let messages: Vec<(&str, &str)> = vec![
("tool_group", "2 tool calls"),
("user", "what about X?"),
("assistant", "About X..."),
];
assert_eq!(messages[0].0, "tool_group");
assert_eq!(messages[1].0, "user");
assert_eq!(messages[2].0, "assistant");
}
#[test]
fn ordering_no_queued_message() {
let messages: Vec<(&str, &str)> = vec![("tool_group", "1 tool call"), ("assistant", "Done.")];
assert_eq!(messages.len(), 2);
assert_eq!(messages[0].0, "tool_group");
assert_eq!(messages[1].0, "assistant");
}