magi-code 0.63.1

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

#[test]
fn write_still_creates_files() {
    let (temp, runtime) = runtime();

    let write = runtime.dispatch(
        "write",
        json!({"path":"dir/file.txt","content":"alpha beta"}),
    );
    assert!(write.success, "{}", write.content);
    assert_eq!(
        fs::read_to_string(temp.path().join("dir/file.txt")).unwrap(),
        "alpha beta"
    );
}

#[cfg(unix)]
#[test]
fn write_rejects_final_symlink_and_symlink_parent_escape() {
    use std::os::unix::fs::symlink;

    let (temp, runtime) = runtime();
    let outside = TempDir::new().unwrap();
    fs::write(outside.path().join("target.txt"), "outside").unwrap();
    fs::write(temp.path().join("inside.txt"), "inside").unwrap();
    symlink(
        temp.path().join("inside.txt"),
        temp.path().join("inside-link.txt"),
    )
    .unwrap();
    symlink(
        outside.path().join("target.txt"),
        temp.path().join("link.txt"),
    )
    .unwrap();

    let inside_final_symlink = runtime.dispatch(
        "write",
        json!({"path":"inside-link.txt","content":"changed"}),
    );
    assert!(!inside_final_symlink.success);
    assert_eq!(
        fs::read_to_string(temp.path().join("inside.txt")).unwrap(),
        "inside"
    );

    let final_symlink = runtime.dispatch("write", json!({"path":"link.txt","content":"escaped"}));
    assert!(!final_symlink.success);
    assert_eq!(
        fs::read_to_string(outside.path().join("target.txt")).unwrap(),
        "outside"
    );

    symlink(outside.path(), temp.path().join("outside-dir")).unwrap();
    let parent_symlink = runtime.dispatch(
        "write",
        json!({"path":"outside-dir/new.txt","content":"escaped"}),
    );
    assert!(!parent_symlink.success);
    assert!(!outside.path().join("new.txt").exists());
}

#[test]
fn write_revalidates_created_parent_inside_cwd() {
    let (temp, runtime) = runtime();
    let result = runtime.dispatch(
        "write",
        json!({"path":"new/nested/file.txt","content":"allowed"}),
    );

    assert!(result.success, "{}", result.content);
    assert_eq!(
        fs::canonicalize(temp.path().join("new/nested")).unwrap(),
        temp.path().join("new/nested").canonicalize().unwrap()
    );
    assert_eq!(
        fs::read_to_string(temp.path().join("new/nested/file.txt")).unwrap(),
        "allowed"
    );
}

#[test]
fn mutation_lock_poisoning_returns_tool_errors() {
    let (temp, runtime) = runtime();
    let registry = Arc::clone(&runtime.mutation_locks);
    let _ = std::thread::spawn(move || {
        let _guard = registry.lock().unwrap();
        panic!("poison mutation lock registry");
    })
    .join();

    let registry_error = runtime.dispatch("write", json!({"path":"a.txt","content":"x"}));
    assert!(!registry_error.success);
    assert!(
        registry_error
            .content
            .contains("mutation lock registry mutex poisoned"),
        "{}",
        registry_error.content
    );

    let file_runtime = ToolRuntime::new(temp.path()).unwrap();
    let target = file_runtime.resolve_for_write("b.txt").unwrap();
    let lock = file_runtime.file_lock(&target).unwrap();
    let poisoned = Arc::clone(&lock);
    let _ = std::thread::spawn(move || {
        let _guard = poisoned.lock().unwrap();
        panic!("poison file mutation lock");
    })
    .join();

    let file_error = file_runtime.dispatch("write", json!({"path":"b.txt","content":"x"}));
    assert!(!file_error.success);
    assert!(
        file_error.content.contains("file mutation lock poisoned"),
        "{}",
        file_error.content
    );
}

#[test]
fn write_enforces_content_bounds() {
    let (_temp, runtime) = runtime();
    let too_large_write = runtime.dispatch(
        "write",
        json!({"path":"too-large.txt", "content":"x".repeat(FILE_WRITE_MAX_BYTES + 1)}),
    );
    assert!(!too_large_write.success);
    assert!(
        too_large_write.content.contains("write content"),
        "{}",
        too_large_write.content
    );
}

#[test]
fn cloned_runtimes_serialize_mutations_to_same_path() {
    let (temp, runtime) = runtime();
    let left = runtime.clone();
    let right = runtime.clone();
    let a = std::thread::spawn(move || {
        left.dispatch("write", json!({"path":"same.txt","content":"a"}))
    });
    let b = std::thread::spawn(move || {
        right.dispatch("write", json!({"path":"same.txt","content":"b"}))
    });
    assert!(a.join().unwrap().success);
    assert!(b.join().unwrap().success);
    let final_text = fs::read_to_string(temp.path().join("same.txt")).unwrap();
    assert!(final_text == "a" || final_text == "b");
}