vtcode 0.137.0

A Rust-based terminal coding agent with modular architecture supporting multiple LLM providers
#![allow(missing_docs)]
use super::*;

#[tokio::test]
async fn build_tool_permissions_context_propagates_skip_confirmations() {
    let mut backing = TestContextBacking::new(2).await;
    let mut ctx = backing.turn_processing_context();

    let permissions = build_tool_permissions_context(&mut ctx);
    assert!(permissions.skip_confirmations);
    drop(permissions);

    ctx.skip_confirmations = false;

    let permissions = build_tool_permissions_context(&mut ctx);
    assert!(!permissions.skip_confirmations);
}

#[test]
fn low_signal_family_for_code_search_uses_limit_insensitive_loop_identity() {
    let first = low_signal_family_key(
        tool_names::CODE_SEARCH,
        &json!({
            "query": " Widget ",
            "file_types": [".rs", "python", "rust"],
            "result_types": ["path", "definition", "path"],
            "max_results": 5
        }),
    );
    let second = low_signal_family_key(
        tool_names::CODE_SEARCH,
        &json!({
            "query": "Widget",
            "path": ".",
            "file_types": ["rust", ".py"],
            "result_types": ["definition", "path"],
            "max_results": 100
        }),
    );

    assert_eq!(first, second);
}

#[test]
fn low_signal_family_for_code_search_distinguishes_query_and_filters() {
    let base = json!({
        "query": "Widget",
        "path": "src",
        "file_types": ["rust"],
        "result_types": ["definition"]
    });
    let base_key = low_signal_family_key(tool_names::CODE_SEARCH, &base);

    for changed in [
        json!({"query": "Other", "path": "src", "file_types": ["rust"], "result_types": ["definition"]}),
        json!({"query": "Widget", "path": "tests", "file_types": ["rust"], "result_types": ["definition"]}),
        json!({"query": "Widget", "path": "src", "file_types": ["python"], "result_types": ["definition"]}),
        json!({"query": "Widget", "path": "src", "file_types": ["rust"], "result_types": ["usage"]}),
    ] {
        assert_ne!(base_key, low_signal_family_key(tool_names::CODE_SEARCH, &changed));
    }
}

#[test]
fn spool_chunk_read_path_detects_spooled_read_calls() {
    let args = json!({
        "path": ".vtcode/context/tool_outputs/command_session_123.txt",
        "offset": 41,
        "limit": 40
    });
    let path = spool_chunk_read_path(tool_names::READ_FILE, &args);
    assert_eq!(path, Some(".vtcode/context/tool_outputs/command_session_123.txt"));
}

#[test]
fn spool_chunk_read_path_ignores_regular_reads() {
    let args = json!({
        "path": "src/main.rs",
        "offset": 1,
        "limit": 100
    });
    let path = spool_chunk_read_path(tool_names::READ_FILE, &args);
    assert_eq!(path, None);
}

#[test]
fn task_tracker_create_signature_matches_identical_payloads() {
    let first = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "n"
    });
    let second = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "n"
    });
    let sig1 = task_tracker_create_signature(tool_names::TASK_TRACKER, &first);
    let sig2 = task_tracker_create_signature(tool_names::TASK_TRACKER, &second);
    assert_eq!(sig1, sig2);
}

#[test]
fn task_tracker_create_signature_differs_for_payload_changes() {
    let first = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A"],
        "notes": "n"
    });
    let second = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "n"
    });
    let sig1 = task_tracker_create_signature(tool_names::TASK_TRACKER, &first);
    let sig2 = task_tracker_create_signature(tool_names::TASK_TRACKER, &second);
    assert_ne!(sig1, sig2);
}

#[test]
fn task_tracker_create_signature_differs_for_title_change() {
    let first = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "n"
    });
    let second = json!({
        "action": "create",
        "title": "Fix clippy warnings later",
        "items": ["A", "B"],
        "notes": "n"
    });
    let sig1 = task_tracker_create_signature(tool_names::TASK_TRACKER, &first);
    let sig2 = task_tracker_create_signature(tool_names::TASK_TRACKER, &second);
    assert_ne!(sig1, sig2);
}

#[test]
fn task_tracker_create_signature_differs_for_notes_change() {
    let first = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "n"
    });
    let second = json!({
        "action": "create",
        "title": "Fix clippy warnings",
        "items": ["A", "B"],
        "notes": "updated"
    });
    let sig1 = task_tracker_create_signature(tool_names::TASK_TRACKER, &first);
    let sig2 = task_tracker_create_signature(tool_names::TASK_TRACKER, &second);
    assert_ne!(sig1, sig2);
}

#[test]
fn task_tracker_create_signature_ignores_non_create_calls() {
    let args = json!({
        "action": "update",
        "index": 1,
        "status": "completed"
    });
    let sig = task_tracker_create_signature(tool_names::TASK_TRACKER, &args);
    assert!(sig.is_none());
}

#[test]
fn shell_run_signature_normalizes_run_pty_command_and_args() {
    let args = json!({
        "command": "  cargo   check  ",
        "args": ["-p", "vtcode-core"]
    });
    let signature = shell_run_signature(tool_names::RUN_PTY_CMD, &args);
    assert_eq!(signature, Some("command_session::cargo check -p vtcode-core".to_string()));
}

#[test]
fn shell_run_signature_handles_command_session_run_action() {
    let args = json!({
        "action": "run",
        "command": ["cargo", "check", "-p", "vtcode-core"]
    });
    let signature = shell_run_signature(tool_names::UNIFIED_EXEC, &args);
    assert_eq!(signature, Some("command_session::cargo check -p vtcode-core".to_string()));
}

#[test]
fn shell_run_signature_normalizes_trivial_shell_quoting_differences() {
    let first = shell_run_signature(
        tool_names::UNIFIED_EXEC,
        &json!({
            "action": "run",
            "command": "grep -n '-> Result' vtcode-tui/src/**/*.rs"
        }),
    );
    let second = shell_run_signature(
        tool_names::UNIFIED_EXEC,
        &json!({
            "action": "run",
            "command": "grep -n \"-> Result\" vtcode-tui/src/**/*.rs"
        }),
    );

    assert_eq!(first, second);
}

#[test]
fn shell_run_signature_ignores_non_run_command_session_action() {
    let args = json!({
        "action": "poll",
        "session_id": "run-123"
    });
    let signature = shell_run_signature(tool_names::UNIFIED_EXEC, &args);
    assert!(signature.is_none());
}

#[test]
fn tool_budget_exhausted_directive_demands_synthesis() {
    let exhaustion = ToolBudgetExhaustion { used: 32, max: 32, remaining: 0 };
    let directive = exhaustion.synthesis_directive_message();
    assert!(directive.contains("32/32"));
    assert!(directive.contains("Synthesize your final answer now"));
    assert!(exhaustion.skipped_call_message().contains("call skipped"));
}