use super::*;
use hashbrown::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::sync::RwLock;
use vtcode_core::persistent_memory::MemoryCleanupStatus;
use vtcode_core::{EditorContextSnapshot, EditorFileContext};
fn sample_memory_status() -> PersistentMemoryStatus {
PersistentMemoryStatus {
enabled: true,
auto_write: true,
directory: PathBuf::from("/tmp/memory"),
summary_file: PathBuf::from("/tmp/memory/memory_summary.md"),
memory_file: PathBuf::from("/tmp/memory/MEMORY.md"),
preferences_file: PathBuf::from("/tmp/memory/preferences.md"),
repository_facts_file: PathBuf::from("/tmp/memory/repository-facts.md"),
notes_dir: PathBuf::from("/tmp/memory/notes"),
rollout_summaries_dir: PathBuf::from("/tmp/memory/rollout_summaries"),
summary_exists: true,
registry_exists: true,
pending_rollout_summaries: 0,
cleanup_status: MemoryCleanupStatus {
needed: false,
suspicious_facts: 0,
suspicious_summary_lines: 0,
},
}
}
#[test]
fn structured_resume_lines_preserve_tool_context() {
let mut assistant = uni::Message::assistant("cargo fmt completed successfully.".to_string());
assistant.reasoning = Some("Need to run formatter before checks.".to_string());
assistant.tool_calls = Some(vec![uni::ToolCall::function(
"call_123".to_string(),
"exec_command".to_string(),
"{\"cmd\":\"cargo fmt\"}".to_string(),
)]);
let tool_output =
r#"{"output":" Finished dev profile [unoptimized + debuginfo]\n","exit_code":0,"backend":"pipe"}"#;
let mut tool_response = uni::Message::tool_response("call_123".to_string(), tool_output.to_string());
tool_response.origin_tool = Some("exec_command".to_string());
let history = vec![
uni::Message::user("run cargo fmt".to_string()),
assistant,
tool_response,
];
let lines = build_structured_resume_lines(&history, true);
assert!(
lines
.iter()
.any(|line| { line.style == MessageStyle::User && line.text.contains("run cargo fmt") })
);
assert!(!lines.iter().any(|line| line.text == "You:"));
assert!(!lines.iter().any(|line| line.text == "Assistant:"));
assert!(lines.iter().any(|line| {
line.style == MessageStyle::Tool && line.text.contains("Tool exec_command [tool_call_id: call_123]:")
}));
assert!(
lines
.iter()
.any(|line| { line.style == MessageStyle::ToolDetail && line.text.contains("command: cargo fmt") })
);
assert!(
lines
.iter()
.any(|line| { line.style == MessageStyle::ToolOutput && line.text.contains("Finished dev profile") })
);
}
#[test]
fn legacy_style_inference_maps_common_prefixes() {
assert_eq!(infer_legacy_line_style(" [1] You:"), MessageStyle::User);
assert_eq!(infer_legacy_line_style(" [5] Assistant:"), MessageStyle::Response);
assert_eq!(infer_legacy_line_style("System: startup"), MessageStyle::Info);
assert_eq!(infer_legacy_line_style("Tool [tool_call_id: call_1]:"), MessageStyle::ToolOutput);
}
#[test]
fn structured_resume_lines_fallback_to_reasoning_details() {
let assistant = uni::Message::assistant("done".to_string())
.with_reasoning_details(Some(vec![serde_json::json!(r#"{"type":"reasoning.text","text":"detail trace"}"#)]));
let lines = build_structured_resume_lines(&[assistant], true);
assert!(
lines
.iter()
.any(|line| { line.style == MessageStyle::Reasoning && line.text.contains("detail trace") })
);
}
#[test]
fn structured_resume_lines_hide_reasoning_when_unsupported() {
let mut assistant = uni::Message::assistant("done".to_string());
assistant.reasoning = Some("trace".to_string());
let lines = build_structured_resume_lines(&[assistant], false);
assert!(!lines.iter().any(|line| line.style == MessageStyle::Reasoning));
}
#[test]
fn persistent_memory_guide_lines_show_standard_actions() {
let lines = persistent_memory_guide_lines(&sample_memory_status());
assert_eq!(lines.len(), 3);
assert!(lines[0].contains("Memory is enabled"));
assert!(lines[1].contains("remember"));
assert!(lines[2].contains("Auto-write is on"));
}
#[test]
fn persistent_memory_guide_lines_call_out_cleanup_when_needed() {
let mut status = sample_memory_status();
status.auto_write = false;
status.cleanup_status.needed = true;
let lines = persistent_memory_guide_lines(&status);
assert_eq!(lines.len(), 3);
assert!(lines[0].contains("one-time cleanup"));
assert!(lines[2].contains("Auto-write is off"));
}
#[test]
fn persistent_memory_header_badge_reflects_memory_mode() {
let badge = persistent_memory_header_badge(&sample_memory_status());
assert_eq!(badge.text, "Memory: On");
assert_eq!(badge.tone, InlineHeaderStatusTone::Ready);
}
#[test]
fn persistent_memory_header_badge_warns_on_cleanup() {
let mut status = sample_memory_status();
status.cleanup_status.needed = true;
let badge = persistent_memory_header_badge(&status);
assert_eq!(badge.text, "Memory: Needs cleanup");
assert_eq!(badge.tone, InlineHeaderStatusTone::Warning);
}
#[test]
fn apply_persistent_memory_header_guide_sets_badge_and_highlight() {
let mut header_context = InlineHeaderContext::default();
apply_persistent_memory_header_guide(&mut header_context, &sample_memory_status());
assert_eq!(header_context.persistent_memory.as_ref().map(|badge| badge.text.as_str()), Some("Memory: On"));
assert!(header_context.highlights.iter().any(|highlight| highlight.title == "Memory"));
}
#[test]
fn background_local_agent_visibility_hides_stopped_entries() {
let entry = vtcode_core::subagents::BackgroundSubprocessEntry {
id: "background-default".to_string(),
session_id: "session-456".to_string(),
exec_session_id: String::new(),
agent_name: "default".to_string(),
display_label: "default".to_string(),
description: "Default agent".to_string(),
source: "builtin".to_string(),
color: None,
status: vtcode_core::subagents::BackgroundSubprocessStatus::Stopped,
desired_enabled: false,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
started_at: None,
ended_at: None,
pid: None,
summary: None,
error: None,
archive_path: None,
transcript_path: None,
};
assert!(visible_background_local_agents(vec![entry]).is_empty());
}
#[test]
fn delegated_local_agent_preview_uses_queue_placeholder() {
let entry = SubagentStatusEntry {
id: "thread-1".to_string(),
session_id: "session-123".to_string(),
parent_thread_id: "main".to_string(),
agent_name: "rust-engineer".to_string(),
display_label: "rust-engineer".to_string(),
description: "Review Rust changes".to_string(),
source: "project".to_string(),
color: None,
status: vtcode_core::subagents::SubagentStatus::Queued,
background: false,
depth: 1,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
completed_at: None,
summary: None,
error: None,
transcript_path: None,
nickname: None,
};
assert_eq!(
delegated_local_agent_preview_placeholder(&entry),
"Agent is queued and has not emitted transcript output yet."
);
}
#[test]
fn delegated_local_agent_visibility_keeps_failed_entries() {
let entry = SubagentStatusEntry {
id: "thread-1".to_string(),
session_id: "session-123".to_string(),
parent_thread_id: "main".to_string(),
agent_name: "rust-engineer".to_string(),
display_label: "rust-engineer".to_string(),
description: "Review Rust changes".to_string(),
source: "project".to_string(),
color: None,
status: vtcode_core::subagents::SubagentStatus::Failed,
background: false,
depth: 1,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
completed_at: Some(chrono::Utc::now()),
summary: None,
error: Some("subagent failed".to_string()),
transcript_path: None,
nickname: None,
};
let visible = visible_delegated_local_agents(vec![entry]);
assert_eq!(visible.len(), 1);
}
#[test]
fn delegated_local_agent_preview_uses_failure_message() {
let entry = SubagentStatusEntry {
id: "thread-1".to_string(),
session_id: "session-123".to_string(),
parent_thread_id: "main".to_string(),
agent_name: "rust-engineer".to_string(),
display_label: "rust-engineer".to_string(),
description: "Review Rust changes".to_string(),
source: "project".to_string(),
color: None,
status: vtcode_core::subagents::SubagentStatus::Failed,
background: false,
depth: 1,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
completed_at: Some(chrono::Utc::now()),
summary: None,
error: Some("subagent failed".to_string()),
transcript_path: None,
nickname: None,
};
assert_eq!(delegated_local_agent_preview_placeholder(&entry), "subagent failed");
}
#[test]
fn background_local_agent_preview_uses_status_placeholder() {
let entry = vtcode_core::subagents::BackgroundSubprocessEntry {
id: "background-default".to_string(),
session_id: "session-456".to_string(),
exec_session_id: String::new(),
agent_name: "default".to_string(),
display_label: "default".to_string(),
description: "Default agent".to_string(),
source: "builtin".to_string(),
color: None,
status: vtcode_core::subagents::BackgroundSubprocessStatus::Starting,
desired_enabled: true,
created_at: chrono::Utc::now(),
updated_at: chrono::Utc::now(),
started_at: None,
ended_at: None,
pid: None,
summary: None,
error: None,
archive_path: None,
transcript_path: None,
};
assert_eq!(background_local_agent_preview_placeholder(&entry), "Waiting for the subprocess to emit output...");
}
#[test]
fn ide_context_status_label_respects_session_override() {
let workspace = assert_fs::TempDir::new().expect("workspace");
let mut context_manager =
context_manager::ContextManager::new("sys".into(), (), Arc::new(RwLock::new(HashMap::new())), None);
context_manager.set_workspace_root(workspace.path());
let snapshot = EditorContextSnapshot {
workspace_root: Some(PathBuf::from(workspace.path())),
active_file: Some(EditorFileContext {
path: workspace.path().join("src/main.rs").display().to_string(),
language_id: Some("rust".to_string()),
line_range: None,
dirty: false,
truncated: false,
selection: None,
}),
..EditorContextSnapshot::default()
};
context_manager
.set_editor_context_snapshot(Some(snapshot.clone()), Some(&vtcode_config::IdeContextConfig::default()));
assert_eq!(
ide_context_status_label(&context_manager, workspace.path(), None, Some(&snapshot), None).as_deref(),
Some("IDE Context (IDE): src/main.rs")
);
assert!(!context_manager.toggle_session_ide_context());
assert_eq!(ide_context_status_label(&context_manager, workspace.path(), None, Some(&snapshot), None), None);
}