magi-code 0.61.0

Repository-aware CLI coding agent for terminal work
Documentation
use super::*;

#[test]
fn tool_settings_absolute_paths_default_omitted_and_false_values() {
    let defaults = ToolSettings::default();
    assert!(defaults.read.absolute_paths);
    assert!(defaults.view_image.absolute_paths);
    assert_eq!(defaults.view_image.max_image_bytes, 5 * 1024 * 1024);
    assert!(defaults.view_image.vision_model.is_none());
    assert!(defaults.hash_edit.absolute_paths);
    assert!(defaults.write.absolute_paths);
    assert!(defaults.grep.absolute_paths);
    assert!(defaults.find.absolute_paths);
    assert!(defaults.list_files.absolute_paths);
    assert!(defaults.bash.absolute_paths);
    assert!(defaults.bash.shell_expansion);
    assert!(defaults.subagents.absolute_paths);
    assert_eq!(defaults.subagents.max_depth, DEFAULT_SUBAGENT_MAX_DEPTH);
    assert!(!defaults.output_compression.enabled);

    let omitted: ToolSettings = serde_json::from_value(json!({
        "read": {},
        "view_image": {},
        "hash_edit": {},
        "write": {},
        "grep": {},
        "find": {},
        "list_files": {},
        "bash": {},
        "subagents": {},
        "output_compression": {}
    }))
    .unwrap();
    assert_eq!(omitted, defaults);

    let false_values: ToolSettings = serde_json::from_value(json!({
        "read": {"absolute_paths": false},
        "view_image": {"absolute_paths": false, "max_image_bytes": 1234, "vision_model": {"provider":"vision-provider","model":"vision-model"}},
        "hash_edit": {"absolute_paths": false},
        "write": {"absolute_paths": false},
        "grep": {"absolute_paths": false},
        "find": {"absolute_paths": false},
        "list_files": {"absolute_paths": false},
        "bash": {"absolute_paths": false, "shell_expansion": false},
        "subagents": {"absolute_paths": false, "max_depth": 1},
        "output_compression": {"enabled": true}
    }))
    .unwrap();
    assert!(!false_values.read.absolute_paths);
    assert!(!false_values.view_image.absolute_paths);
    assert_eq!(false_values.view_image.max_image_bytes, 1234);
    assert_eq!(
        false_values
            .view_image
            .vision_model
            .as_ref()
            .unwrap()
            .provider,
        "vision-provider"
    );
    assert!(!false_values.hash_edit.absolute_paths);
    assert!(!false_values.write.absolute_paths);
    assert!(!false_values.grep.absolute_paths);
    assert!(!false_values.find.absolute_paths);
    assert!(!false_values.list_files.absolute_paths);
    assert!(!false_values.bash.absolute_paths);
    assert!(!false_values.bash.shell_expansion);
    assert!(!false_values.subagents.absolute_paths);
    assert_eq!(false_values.subagents.max_depth, 1);
    assert!(false_values.output_compression.enabled);

    let temp = TempDir::new().unwrap();
    let runtime = ToolRuntime::new_with_settings(temp.path(), false_values)
        .unwrap()
        .clone_for_cwd_with_subagent_depth(temp.path(), 1)
        .unwrap();
    assert!(!runtime.read_absolute_paths);
    assert!(!runtime.view_image_absolute_paths);
    assert_eq!(runtime.view_image_max_image_bytes, 1234);
    assert_eq!(
        runtime.view_image_vision_model.as_ref().unwrap().model,
        "vision-model"
    );
    assert!(!runtime.hashline_absolute_paths);
    assert!(!runtime.write_absolute_paths);
    assert!(!runtime.grep_absolute_paths);
    assert!(!runtime.find_absolute_paths);
    assert!(!runtime.list_files_absolute_paths);
    assert!(!runtime.bash_absolute_paths);
    assert!(!runtime.bash_shell_expansion);
    assert!(!runtime.subagents_absolute_paths);
    assert_eq!(runtime.subagents_max_depth, 1);
    assert!(runtime.output_compression_settings().enabled);
    assert!(!runtime.subagents_schema_enabled());
}

#[test]
fn read_argument_validation_rejects_invalid_path_shapes() {
    let (_temp, runtime) = runtime();
    for args in [
        json!({}),
        json!({"path":"a.txt", "paths":["b.txt"]}),
        json!({"path":"   "}),
        json!({"paths":[]}),
        json!({"paths":["a.txt", ""]}),
        json!({"paths": vec!["a.txt"; FILE_READ_MAX_FILES + 1]}),
        json!({"paths":["a.txt"], "offset":0}),
        json!({"paths":["a.txt"], "limit":0}),
        json!({"paths":["a.txt"], "limit":FILE_READ_MAX_LINES + 1}),
    ] {
        let result = runtime.dispatch("read", args);
        assert!(!result.success, "accepted invalid args: {}", result.content);
    }
}

#[test]
fn absolute_paths_default_for_hash_edit_write_grep_and_false_rejects_outside() {
    let (temp, runtime) = runtime();
    let outside = TempDir::new().unwrap();
    let outside_edit = outside.path().join("edit.txt");
    let outside_write = outside.path().join("write.txt");
    let outside_grep = outside.path().join("grep.txt");
    fs::write(
        &outside_edit,
        "alpha
beta
",
    )
    .unwrap();
    fs::write(
        &outside_grep,
        "find me
",
    )
    .unwrap();
    let tag = runtime.hashline_snapshots.lock().unwrap().record(
        outside_edit.canonicalize().unwrap(),
        "alpha
beta
",
        [1, 2],
    );

    let edited = runtime.dispatch(
        "hash_edit",
        json!({"input": format!("[{}#{tag}]
SWAP 2.=2:
+gamma", outside_edit.display())}),
    );
    assert!(edited.success, "{}", edited.content);
    assert_eq!(
        fs::read_to_string(&outside_edit).unwrap(),
        "alpha
gamma
"
    );

    let written = runtime.dispatch(
        "write",
        json!({"path": outside_write, "content":"outside write"}),
    );
    assert!(written.success, "{}", written.content);

    let grep = runtime.dispatch("grep", json!({"pattern":"find", "path": outside.path()}));
    assert!(grep.success, "{}", grep.content);

    let restricted = ToolRuntime::new_with_settings(
        temp.path(),
        ToolSettings {
            hash_edit: HashEditToolSettings {
                absolute_paths: false,
            },
            write: WriteToolSettings {
                absolute_paths: false,
            },
            grep: GrepToolSettings {
                absolute_paths: false,
            },
            ..ToolSettings::default()
        },
    )
    .unwrap();
    assert!(
        !restricted
            .dispatch(
                "hash_edit",
                json!({"input": format!("[{}#{tag}]
SWAP 2.=2:
+delta", outside_edit.display())})
            )
            .success
    );
    assert!(
        !restricted
            .dispatch(
                "write",
                json!({"path": outside.path().join("blocked.txt"), "content":"blocked"})
            )
            .success
    );
    assert!(
        !restricted
            .dispatch("grep", json!({"pattern":"find", "path": outside.path()}))
            .success
    );
    assert!(
        restricted
            .dispatch("write", json!({"path":"inside.txt", "content":"ok"}))
            .success
    );
}

#[test]
fn existing_path_policy_covers_relative_absolute_and_out_of_root_paths() {
    let (temp, runtime) = runtime();
    fs::write(temp.path().join("inside.txt"), "inside").unwrap();
    let outside = TempDir::new().unwrap();
    let outside_file = outside.path().join("outside.txt");
    fs::write(&outside_file, "outside").unwrap();

    let inside = runtime
        .resolve_existing_path(
            "inside.txt",
            crate::tools::fs::ExistingPathPolicy::read(false),
        )
        .unwrap();
    assert_eq!(
        inside,
        temp.path().join("inside.txt").canonicalize().unwrap()
    );

    let allowed = runtime
        .resolve_existing_path(
            outside_file.to_str().unwrap(),
            crate::tools::fs::ExistingPathPolicy::grep(true),
        )
        .unwrap();
    assert_eq!(allowed, outside_file.canonicalize().unwrap());

    let blocked = runtime
        .resolve_existing_path(
            outside_file.to_str().unwrap(),
            crate::tools::fs::ExistingPathPolicy::grep(false),
        )
        .unwrap_err()
        .to_string();
    assert!(blocked.contains("tools.grep.absolute_paths"), "{blocked}");
    assert!(blocked.contains("escapes cwd"), "{blocked}");
}

#[test]
fn search_tools_preserve_find_cwd_policy_and_grep_absolute_policy() {
    let (temp, runtime) = runtime();
    fs::create_dir(temp.path().join("src")).unwrap();
    fs::write(temp.path().join("src/inside.txt"), "needle inside\n").unwrap();
    let outside = TempDir::new().unwrap();
    fs::write(outside.path().join("outside.txt"), "needle outside\n").unwrap();

    let find_relative = runtime.dispatch("find", json!({"query":"inside", "path":"src"}));
    assert!(find_relative.success, "{}", find_relative.content);

    let find_outside = runtime.dispatch("find", json!({"query":"outside", "path": outside.path()}));
    assert!(find_outside.success, "{}", find_outside.content);
    assert!(find_outside.content.contains("outside.txt"));

    let grep_outside = runtime.dispatch(
        "grep",
        json!({"pattern":"needle", "path": outside.path(), "limit": 1}),
    );
    assert!(grep_outside.success, "{}", grep_outside.content);

    let restricted = ToolRuntime::new_with_settings(
        temp.path(),
        ToolSettings {
            grep: GrepToolSettings {
                absolute_paths: false,
            },
            find: FindToolSettings {
                absolute_paths: false,
            },
            ..ToolSettings::default()
        },
    )
    .unwrap();
    let blocked = restricted.dispatch(
        "grep",
        json!({"pattern":"needle", "path": outside.path(), "limit": 1}),
    );
    assert!(!blocked.success);
    assert!(blocked.content.contains("tools.grep.absolute_paths"));

    let blocked_find =
        restricted.dispatch("find", json!({"query":"outside", "path": outside.path()}));
    assert!(!blocked_find.success);
    assert!(blocked_find.content.contains("tools.find.absolute_paths"));
}

#[test]
fn bash_argument_validation_accepts_command_and_cmd_alias() {
    let (_temp, runtime) = runtime();
    let command = runtime.dispatch("bash", json!({"command":"printf command"}));
    assert!(command.success, "{}", command.content);
    assert!(
        command.metadata["stdout"]
            .as_str()
            .unwrap()
            .contains("command")
    );

    let cmd = runtime.dispatch("bash", json!({"cmd":"printf cmd"}));
    assert!(cmd.success, "{}", cmd.content);
    assert!(cmd.metadata["stdout"].as_str().unwrap().contains("cmd"));

    let precedence = runtime.dispatch(
        "bash",
        json!({"command":"printf command", "cmd":"printf cmd"}),
    );
    assert!(precedence.success, "{}", precedence.content);
    assert_eq!(precedence.metadata["stdout"], "command");
}

#[test]
fn bash_argument_validation_rejects_empty_command_and_invalid_timeout() {
    let (_temp, runtime) = runtime();
    let empty = runtime.dispatch("bash", json!({"command":"   "}));
    assert!(!empty.success);
    assert!(empty.content.contains("command must not be empty"));

    let empty_cmd = runtime.dispatch("bash", json!({"cmd":""}));
    assert!(!empty_cmd.success);
    assert!(empty_cmd.content.contains("command must not be empty"));

    for timeout in [0, 301] {
        let result = runtime.dispatch(
            "bash",
            json!({"command":"printf no-run", "timeout": timeout}),
        );
        assert!(!result.success, "accepted timeout {timeout}");
        assert!(result.content.contains("timeout must be between 1 and 300"));
    }
}

#[test]
fn runtime_argument_validation_rejects_empty_tasks() {
    let (_temp, runtime) = runtime();
    let result = runtime.dispatch("subagents", json!({"tasks":[]}));
    assert!(!result.success);
    assert!(
        result.content.contains("at least one task"),
        "{}",
        result.content
    );
}

#[test]
fn runtime_argument_validation_rejects_unknown_fields() {
    let (temp, runtime) = runtime();
    fs::write(temp.path().join("a.txt"), "alpha").unwrap();

    let read = runtime.dispatch("read", json!({"path":"a.txt", "unexpected": true}));
    assert!(!read.success);
    assert!(read.content.contains("unknown field"), "{}", read.content);

    let bash = runtime.dispatch(
        "bash",
        json!({"command":"printf no-run", "unexpected": true}),
    );
    assert!(!bash.success);
    assert!(bash.content.contains("unknown field"), "{}", bash.content);

    let parallel = runtime.dispatch(
        "subagents",
        json!({"tasks":[{"intent":"inspect","unexpected":true}],"concurrency":1}),
    );
    assert!(!parallel.success);
    assert!(
        parallel.content.contains("unknown field"),
        "{}",
        parallel.content
    );

    let web = runtime.dispatch(
        "web_search",
        json!({"queries":["rust"], "unexpected": true}),
    );
    assert!(!web.success);
    assert!(web.content.contains("unknown field"), "{}", web.content);

    let code = runtime.dispatch("code_search", json!({"query":"rust", "unexpected": true}));
    assert!(!code.success);
    assert!(code.content.contains("unknown field"), "{}", code.content);
}