//! 摘要与格式化辅助的测试。 //! Tests for summary & formatting helpers. use focus_json::JsonValue; use focus_tui::format::{ caret_position, display_width, format_duration, format_tokens, summarize_tool, truncate, truncate_lines, wrap_text, }; fn obj(pairs: &[(&str, JsonValue)]) -> JsonValue { let mut o = JsonValue::obj(); for (k, v) in pairs { o.insert(*k, v.clone()).ok(); } o } #[test] fn tool_summaries_show_concrete_content() { // read → 路径 + 行范围。 // read → path + line range. let args = obj(&[ ("path", "src/main.rs".into()), ("startLine", 10u64.into()), ("endLine", 20u64.into()), ]); assert_eq!( summarize_tool("read", &args), "read src/main.rs lines 10-20" ); // write → 路径 + 字节数。 // write → path + byte count. let args = obj(&[("path", "a.txt".into()), ("content", "hello world".into())]); assert_eq!(summarize_tool("write", &args), "write a.txt (11 bytes)"); // edit → 路径 + 编辑数。 // edit → path + edit count. let edits = JsonValue::Arr(vec![JsonValue::obj(), JsonValue::obj()]); let args = obj(&[("path", "b.rs".into()), ("edits", edits)]); assert_eq!(summarize_tool("edit", &args), "edit b.rs (2 edits)"); // shell → 命令(截断)。 // shell → the command (truncated). let args = obj(&[("command", "cargo test --workspace".into())]); assert_eq!( summarize_tool("shell", &args), "shell: cargo test --workspace" ); // 未知工具 → 紧凑 JSON。 // Unknown tool → compact JSON. let args = obj(&[("x", 1u64.into())]); assert_eq!(summarize_tool("weird", &args), "weird {\"x\":1}"); } #[test] fn durations() { assert_eq!(format_duration(0), "0ms"); assert_eq!(format_duration(450), "450ms"); assert_eq!(format_duration(12_300), "12.3s"); assert_eq!(format_duration(125_000), "2m 05s"); } #[test] fn token_formatting() { assert_eq!(format_tokens(0), "0"); assert_eq!(format_tokens(999), "999"); assert_eq!(format_tokens(12_345), "12,345"); assert_eq!(format_tokens(1_000_000), "1,000,000"); } #[test] fn truncation() { assert_eq!(truncate("hello", 10), "hello"); assert_eq!(truncate("hello world", 5), "hello…"); let multi = "a\nb\nc\nd\ne\nf\ng"; let out = truncate_lines(multi, 4, 100); assert!(out.contains("truncated"), "got: {}", out); assert!(out.contains('a')); assert!(out.contains('g')); } #[test] fn wrapping_and_width() { assert_eq!(display_width("abc"), 3); assert_eq!(display_width("中文"), 4); let wrapped = wrap_text("aaaaaa", 3); assert_eq!(wrapped, vec!["aaa".to_string(), "aaa".to_string()]); // 保留显式换行。 // Preserve explicit newlines. let wrapped = wrap_text("ab\ncd", 10); assert_eq!(wrapped, vec!["ab".to_string(), "cd".to_string()]); } #[test] fn caret_positioning() { // 单行:caret 在末尾。 // Single line: caret at the end. let (row, col, total) = caret_position("hello", 5, 10); assert_eq!((row, col, total), (0, 5, 1)); // 换行后:caret 在第二行开头。 // After a newline: caret at the start of line 2. let (row, col, total) = caret_position("ab\ncd", 3, 10); assert_eq!((row, col, total), (1, 0, 2)); // 超宽行会折行。 // Over-wide lines wrap. let (row, col, total) = caret_position("aaaaaa", 6, 3); assert_eq!((row, col, total), (1, 3, 2)); } #[test] fn args_human_format_no_raw_json() { use focus_tui::format::format_args_human; // shell:命令按原样。 // shell: the command verbatim. let args = obj(&[("command", "ls -la".into())]); assert_eq!( format_args_human(&args), vec!["command: ls -la".to_string()] ); // read:路径。 // read: the path. let args = obj(&[("path", "/a/b.txt".into())]); assert_eq!(format_args_human(&args), vec!["path: /a/b.txt".to_string()]); // edit:多组修改以人类可读展示(非 JSON)。 // edit: multiple edits shown human-readably (not JSON). let mut e1 = JsonValue::obj(); e1.insert("oldString", "foo".into()).ok(); e1.insert("newString", "bar".into()).ok(); let edits = JsonValue::Arr(vec![e1]); let args = obj(&[("path", "f.txt".into()), ("edits", edits)]); let lines = format_args_human(&args); assert_eq!(lines[0], "path: f.txt"); assert_eq!(lines[1], "edits: 共 1 处修改"); assert!(lines[2].contains("foo"), "got {:?}", lines); assert!(lines[2].contains("bar"), "got {:?}", lines); }