use super::*;
use std::sync::Mutex;
static TOKEN_TEST_MUTEX: Mutex<()> = Mutex::new(());
#[test]
fn test_init_and_check_modes() {
init(true, false, true);
assert!(is_compact());
assert!(!is_verbose());
assert!(should_show_tokens());
init(false, true, false);
assert!(!is_compact());
assert!(is_verbose());
assert!(!should_show_tokens());
}
#[test]
fn test_token_tracking() {
let _lock = TOKEN_TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
reset_tokens();
record_tokens(100, 50);
record_tokens(200, 100);
let (prompt, completion) = get_total_tokens();
assert_eq!(prompt, 300);
assert_eq!(completion, 150);
}
#[test]
fn test_reset_tokens() {
let _lock = TOKEN_TEST_MUTEX.lock().unwrap_or_else(|e| e.into_inner());
reset_tokens();
record_tokens(100, 50);
reset_tokens();
let (prompt, completion) = get_total_tokens();
assert_eq!(prompt, 0);
assert_eq!(completion, 0);
}
#[test]
fn test_task_progress_creation() {
let progress = TaskProgress::new(&["Planning", "Executing", "Verifying"]);
assert_eq!(progress.phases.len(), 3);
assert_eq!(progress.overall_progress(), 0.0);
}
#[test]
fn test_task_progress_phases() {
let mut progress = TaskProgress::new(&["Phase 1", "Phase 2"]);
progress.start_phase();
assert_eq!(progress.phases[0].status, PhaseStatus::Active);
progress.complete_phase();
assert_eq!(progress.phases[0].status, PhaseStatus::Completed);
assert_eq!(progress.phases[1].status, PhaseStatus::Active);
assert!((progress.overall_progress() - 0.5).abs() < 0.01);
progress.complete_phase();
assert_eq!(progress.overall_progress(), 1.0);
}
#[test]
fn test_task_progress_update() {
let mut progress = TaskProgress::new(&["Build"]);
progress.start_phase();
progress.update_progress(0.5);
assert!((progress.phases[0].progress - 0.5).abs() < 0.01);
progress.update_progress(1.5);
assert!((progress.phases[0].progress - 1.0).abs() < 0.01);
}
#[test]
fn test_task_progress_failure() {
let mut progress = TaskProgress::new(&["Test"]);
progress.start_phase();
progress.fail_phase();
assert_eq!(progress.phases[0].status, PhaseStatus::Failed);
}
#[test]
fn test_semantic_summary_file_read() {
let args = serde_json::json!({"path": "src/main.rs"});
let summary = semantic_summary("file_read", &args, None, true, 50);
assert!(summary.contains("Read"));
assert!(summary.contains("src/main.rs"));
}
#[test]
fn test_semantic_summary_file_write() {
let args = serde_json::json!({"path": "src/lib.rs"});
let summary = semantic_summary("file_write", &args, None, true, 50);
assert!(summary.contains("Wrote"));
assert!(summary.contains("src/lib.rs"));
}
#[test]
fn test_semantic_summary_file_edit() {
let args = serde_json::json!({"path": "src/main.rs"});
let summary = semantic_summary("file_edit", &args, None, true, 50);
assert!(summary.contains("Edited"));
assert!(summary.contains("src/main.rs"));
}
#[test]
fn test_semantic_summary_shell_exec() {
let args = serde_json::json!({"command": "cargo build"});
let summary = semantic_summary("shell_exec", &args, None, true, 100);
assert!(summary.contains("Ran"));
assert!(summary.contains("cargo build"));
}
#[test]
fn test_semantic_summary_cargo_check() {
let args = serde_json::json!({});
let summary = semantic_summary("cargo_check", &args, None, true, 200);
assert_eq!(summary, "Cargo check passed");
}
#[test]
fn test_semantic_summary_grep_search() {
let args = serde_json::json!({"pattern": "pub fn"});
let summary = semantic_summary("grep_search", &args, None, true, 30);
assert!(summary.contains("Searched"));
assert!(summary.contains("pub fn"));
}
#[test]
fn test_semantic_summary_git_status() {
let args = serde_json::json!({});
let summary = semantic_summary("git_status", &args, None, true, 20);
assert!(summary.contains("Git status"));
}
#[test]
fn test_semantic_summary_unknown_tool() {
let args = serde_json::json!({});
let summary = semantic_summary("unknown_tool", &args, None, true, 150);
assert!(summary.contains("unknown_tool"));
assert!(summary.contains("150ms"));
}
#[test]
fn test_format_number() {
assert_eq!(format_number(500), "500");
assert_eq!(format_number(1500), "1.5K");
assert_eq!(format_number(1_500_000), "1.5M");
}
#[test]
fn test_extract_path() {
let args = serde_json::json!({"path": "src/main.rs"});
assert_eq!(extract_path(&args), Some("src/main.rs"));
let args2 = serde_json::json!({"file_path": "lib.rs"});
assert_eq!(extract_path(&args2), Some("lib.rs"));
let empty = serde_json::json!({});
assert_eq!(extract_path(&empty), None);
}
#[test]
fn test_extract_command() {
let args = serde_json::json!({"command": "cargo test"});
assert_eq!(extract_command(&args), Some("cargo test"));
let empty = serde_json::json!({});
assert_eq!(extract_command(&empty), None);
}
#[test]
fn test_extract_pattern() {
let args = serde_json::json!({"pattern": "pub fn"});
assert_eq!(extract_pattern(&args), Some("pub fn"));
let args2 = serde_json::json!({"query": "search term"});
assert_eq!(extract_pattern(&args2), Some("search term"));
}
#[test]
fn test_semantic_summary_file_delete() {
let args = serde_json::json!({"path": "old_file.txt"});
let summary = semantic_summary("file_delete", &args, None, true, 10);
assert_eq!(summary, "Deleted old_file.txt");
}
#[test]
fn test_semantic_summary_cargo_clippy_clean() {
let args = serde_json::json!({});
let summary = semantic_summary("cargo_clippy", &args, None, true, 100);
assert_eq!(summary, "Clippy: clean");
}
#[test]
fn test_semantic_summary_cargo_clippy_warnings() {
let args = serde_json::json!({});
let summary = semantic_summary("cargo_clippy", &args, None, false, 100);
assert_eq!(summary, "Clippy: warnings");
}
#[test]
fn test_semantic_summary_cargo_fmt() {
let args = serde_json::json!({});
assert_eq!(
semantic_summary("cargo_fmt", &args, None, true, 50),
"Formatted code"
);
assert_eq!(
semantic_summary("cargo_fmt", &args, None, false, 50),
"Format check failed"
);
}
#[test]
fn test_semantic_summary_symbol_search() {
let args = serde_json::json!({"pattern": "MyStruct"});
let summary = semantic_summary("symbol_search", &args, None, true, 30);
assert_eq!(summary, "Symbol search 'MyStruct'");
}
#[test]
fn test_semantic_summary_http_request() {
let args = serde_json::json!({"method": "POST", "url": "https://api.example.com/data"});
let summary = semantic_summary("http_request", &args, None, true, 100);
assert!(summary.contains("HTTP POST"));
assert!(summary.contains("api.example.com"));
}
#[test]
fn test_semantic_summary_git_push() {
let args = serde_json::json!({"remote": "origin"});
let result_str = r#"{"success": true, "remote": "origin", "branch": "main"}"#;
let summary = semantic_summary("git_push", &args, Some(result_str), true, 50);
assert_eq!(summary, "Pushed main to origin");
}
#[test]
fn test_semantic_summary_git_checkpoint() {
let args = serde_json::json!({"message": "before refactor"});
let summary = semantic_summary("git_checkpoint", &args, None, true, 50);
assert_eq!(summary, "Checkpoint: before refactor");
}
#[test]
fn test_semantic_summary_process_start() {
let args = serde_json::json!({"command": "node server.js"});
let summary = semantic_summary("process_start", &args, None, true, 50);
assert_eq!(summary, "Started node server.js");
}
#[test]
fn test_semantic_summary_container_run() {
let args = serde_json::json!({"image": "nginx:latest"});
let summary = semantic_summary("container_run", &args, None, true, 50);
assert_eq!(summary, "Container run nginx:latest");
}
#[test]
fn test_semantic_summary_npm_install() {
let args = serde_json::json!({"package": "express"});
let summary = semantic_summary("npm_install", &args, None, true, 50);
assert_eq!(summary, "npm install express");
}
#[test]
fn test_semantic_summary_npm_install_all() {
let args = serde_json::json!({});
let summary = semantic_summary("npm_install", &args, None, true, 50);
assert_eq!(summary, "npm install");
}
#[test]
fn test_semantic_summary_browser_fetch() {
let args = serde_json::json!({"url": "https://example.com"});
let summary = semantic_summary("browser_fetch", &args, None, true, 50);
assert_eq!(summary, "Fetch https://example.com");
}
#[test]
fn test_semantic_summary_knowledge_add() {
let args = serde_json::json!({"name": "Rust"});
let summary = semantic_summary("knowledge_add", &args, None, true, 50);
assert_eq!(summary, "Knowledge add 'Rust'");
}
#[test]
fn test_semantic_summary_knowledge_query() {
let args = serde_json::json!({"query": "error handling"});
let summary = semantic_summary("knowledge_query", &args, None, true, 50);
assert_eq!(summary, "Knowledge query 'error handling'");
}
#[test]
fn test_semantic_summary_git_status_with_changes() {
let args = serde_json::json!({});
let result_str =
r#"{"branch":"main","staged":["a.rs"],"unstaged":["b.rs"],"untracked":["c.rs"]}"#;
let summary = semantic_summary("git_status", &args, Some(result_str), true, 20);
assert_eq!(summary, "Git status (3 changed)");
}
#[test]
fn test_semantic_summary_git_status_clean() {
let args = serde_json::json!({});
let result_str = r#"{"branch":"main","staged":[],"unstaged":[],"untracked":[]}"#;
let summary = semantic_summary("git_status", &args, Some(result_str), true, 20);
assert_eq!(summary, "Git status (clean)");
}
#[test]
fn test_tool_activity_message_file_ops() {
let args = serde_json::json!({"path": "src/main.rs"});
assert!(tool_activity_message("file_read", &args).contains("Reading"));
assert!(tool_activity_message("file_write", &args).contains("Writing"));
assert!(tool_activity_message("file_edit", &args).contains("Editing"));
assert!(tool_activity_message("file_delete", &args).contains("Deleting"));
assert!(tool_activity_message("file_create", &args).contains("Writing"));
}
#[test]
fn test_tool_activity_message_shell() {
let args = serde_json::json!({"command": "echo hello"});
assert!(tool_activity_message("shell_exec", &args).contains("Running"));
}
#[test]
fn test_tool_activity_message_cargo() {
let args = serde_json::json!({});
assert!(tool_activity_message("cargo_test", &args).contains("tests"));
assert!(tool_activity_message("cargo_check", &args).contains("Checking"));
assert!(tool_activity_message("cargo_clippy", &args).contains("clippy"));
assert!(tool_activity_message("cargo_fmt", &args).contains("Formatting"));
}
#[test]
fn test_tool_activity_message_search() {
let args = serde_json::json!({"pattern": "fn main"});
assert!(tool_activity_message("grep_search", &args).contains("Searching"));
assert!(tool_activity_message("ripgrep_search", &args).contains("Searching"));
assert!(tool_activity_message("symbol_search", &args).contains("Searching"));
}
#[test]
fn test_tool_activity_message_git() {
let args = serde_json::json!({});
assert!(tool_activity_message("git_status", &args).contains("git status"));
assert!(tool_activity_message("git_diff", &args).contains("diff"));
assert!(tool_activity_message("git_log", &args).contains("git log"));
assert!(tool_activity_message("git_commit", &args).contains("Committing"));
assert!(tool_activity_message("git_push", &args).contains("Pushing"));
assert!(tool_activity_message("git_checkpoint", &args).contains("checkpoint"));
}
#[test]
fn test_tool_activity_message_directory() {
let args = serde_json::json!({"path": "src"});
assert!(tool_activity_message("directory_tree", &args).contains("Listing"));
let glob_args = serde_json::json!({"pattern": "*.rs"});
assert!(tool_activity_message("glob_find", &glob_args).contains("Finding"));
}
#[test]
fn test_tool_activity_message_http_process() {
let args = serde_json::json!({});
assert!(tool_activity_message("http_request", &args).contains("HTTP"));
assert!(tool_activity_message("process_start", &args).contains("Starting"));
assert!(tool_activity_message("process_stop", &args).contains("Stopping"));
assert!(tool_activity_message("process_list", &args).contains("Listing"));
assert!(tool_activity_message("process_logs", &args).contains("logs"));
assert!(tool_activity_message("process_restart", &args).contains("Restarting"));
}
#[test]
fn test_tool_activity_message_container() {
let args = serde_json::json!({});
assert!(tool_activity_message("container_run", &args).contains("container"));
assert!(tool_activity_message("container_stop", &args).contains("container"));
}
#[test]
fn test_tool_activity_message_package() {
let args = serde_json::json!({});
assert!(tool_activity_message("npm_install", &args).contains("Installing"));
assert!(tool_activity_message("pip_install", &args).contains("Installing"));
assert!(tool_activity_message("yarn_install", &args).contains("Installing"));
assert!(tool_activity_message("npm_run", &args).contains("Running"));
}
#[test]
fn test_tool_activity_message_browser() {
let args = serde_json::json!({});
assert!(tool_activity_message("browser_fetch", &args).contains("Fetching"));
assert!(tool_activity_message("browser_screenshot", &args).contains("screenshot"));
}
#[test]
fn test_tool_activity_message_knowledge() {
let args = serde_json::json!({});
assert!(tool_activity_message("knowledge_add", &args).contains("knowledge"));
assert!(tool_activity_message("knowledge_query", &args).contains("knowledge"));
}
#[test]
fn test_tool_activity_message_fallback() {
let args = serde_json::json!({});
let msg = tool_activity_message("unknown_tool", &args);
assert!(msg.contains("unknown_tool"));
}
#[test]
fn test_verification_report_verbose() {
init(false, true, false); verification_report("All tests pass", true);
verification_report("Test failed: X", false);
}
#[test]
fn test_verification_report_compact() {
init(true, false, false); verification_report("All pass", true);
verification_report("Failure report", false);
}
#[test]
fn test_verification_report_normal() {
init(false, false, false); verification_report("Passed", true);
verification_report("Failed report text", false);
}
#[test]
fn test_task_progress_estimated_remaining_none_early() {
let progress = TaskProgress::new(&["Phase 1"]);
assert!(progress.estimated_remaining().is_none());
}
#[test]
fn test_task_progress_format_eta_minutes() {
let mut progress = TaskProgress::new(&["A", "B", "C", "D"]);
progress.start_phase();
progress.complete_phase();
let _ = progress.format_eta();
}
#[test]
fn test_phase_status_equality() {
assert_eq!(PhaseStatus::Pending, PhaseStatus::Pending);
assert_ne!(PhaseStatus::Pending, PhaseStatus::Active);
}
#[test]
fn test_semantic_summary_various() {
let args = serde_json::json!({"path": "test.rs"});
let s = semantic_summary("file_edit", &args, None, true, 10);
assert!(s.contains("Edited"));
let s = semantic_summary("file_delete", &args, None, true, 10);
assert!(s.contains("Deleted"));
let s = semantic_summary("directory_tree", &args, None, true, 10);
assert!(s.contains("Listed"));
}
#[test]
fn test_semantic_summary_cargo() {
let args = serde_json::json!({});
assert!(semantic_summary("cargo_check", &args, None, true, 10).contains("passed"));
assert!(semantic_summary("cargo_check", &args, None, false, 10).contains("failed"));
assert!(semantic_summary("cargo_clippy", &args, None, true, 10).contains("clean"));
assert!(semantic_summary("cargo_clippy", &args, None, false, 10).contains("warnings"));
assert!(semantic_summary("cargo_fmt", &args, None, true, 10).contains("Formatted"));
assert!(semantic_summary("cargo_fmt", &args, None, false, 10).contains("failed"));
}
#[test]
fn test_semantic_summary_git() {
let args = serde_json::json!({});
assert_eq!(
semantic_summary("git_log", &args, None, true, 10),
"Git log"
);
assert_eq!(
semantic_summary("git_commit", &args, None, true, 10),
"Git commit"
);
}
#[test]
fn test_semantic_summary_fallback() {
let args = serde_json::json!({});
let s = semantic_summary("some_custom_tool", &args, None, true, 42);
assert!(s.contains("some_custom_tool"));
assert!(s.contains("42ms"));
}
#[test]
fn test_extract_helpers() {
let args = serde_json::json!({"path": "a.rs", "command": "echo", "pattern": "fn"});
assert_eq!(extract_path(&args), Some("a.rs"));
assert_eq!(extract_command(&args), Some("echo"));
assert_eq!(extract_pattern(&args), Some("fn"));
let empty = serde_json::json!({});
assert_eq!(extract_path(&empty), None);
assert_eq!(extract_command(&empty), None);
assert_eq!(extract_pattern(&empty), None);
}
#[test]
fn test_output_lock_prevents_interleaving() {
use std::sync::Arc;
use std::thread;
let buffer: Arc<Mutex<Vec<String>>> = Arc::new(Mutex::new(Vec::new()));
let handles: Vec<_> = (0..2)
.map(|thread_id| {
let buf = Arc::clone(&buffer);
thread::spawn(move || {
for i in 0..100 {
let _lock = OUTPUT_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let line = format!(
"THREAD_{thread_id}:iter_{i:03}:payload_{val}",
val = thread_id * 1000 + i
);
buf.lock().unwrap().push(line);
}
})
})
.collect();
for handle in handles {
handle.join().expect("Thread should complete without panic");
}
let lines = buffer.lock().unwrap();
assert_eq!(lines.len(), 200, "Expected 200 entries (100 per thread)");
for (idx, line) in lines.iter().enumerate() {
let is_t0 = line.starts_with("THREAD_0:");
let is_t1 = line.starts_with("THREAD_1:");
assert!(is_t0 || is_t1, "Line {idx} has unexpected prefix: {line}");
if is_t0 {
assert!(
!line.contains("THREAD_1"),
"Line {idx} from thread 0 contains thread 1 data: {line}"
);
} else {
assert!(
!line.contains("THREAD_0"),
"Line {idx} from thread 1 contains thread 0 data: {line}"
);
}
}
}
#[test]
fn test_concurrent_output_functions() {
use std::thread;
set_tui_active(false);
init(false, false, false);
let handles: Vec<_> = (0..5)
.map(|i| {
thread::spawn(move || {
for j in 0..10 {
thinking(&format!("Thinking from thread {} iter {}", i, j), false);
intent_without_action();
step_start(j + 1, &format!("test_step_{}", i));
}
})
})
.collect();
for handle in handles {
handle.join().expect("Thread should complete without panic");
}
}