matrixcode-core 0.4.8

MatrixCode Agent Core - Pure logic, no UI
Documentation
use matrixcode_core::tools::Tool;
use matrixcode_core::tools::multi_edit::MultiEditTool;
use serde_json::json;
use std::fs;
use tempfile::TempDir;

fn tmp_file(content: &str) -> (TempDir, std::path::PathBuf) {
    let dir = TempDir::new().unwrap();
    let path = dir.path().join("f.txt");
    fs::write(&path, content).unwrap();
    (dir, path)
}

#[tokio::test]
async fn test_multi_edit_definition() {
    let tool = MultiEditTool;
    let def = tool.definition();
    assert_eq!(def.name, "multi_edit");
    let required = def.parameters["required"].as_array().unwrap();
    assert!(required.contains(&json!("path")));
    assert!(required.contains(&json!("edits")));
}

#[tokio::test]
async fn test_multi_edit_applies_all_sequentially() {
    let (_d, path) = tmp_file("alpha beta gamma");
    let out = MultiEditTool
        .execute(json!({
            "path": path.to_str().unwrap(),
            "edits": [
                {"old_string": "alpha", "new_string": "A"},
                {"old_string": "beta", "new_string": "B"},
                {"old_string": "gamma", "new_string": "C"}
            ]
        }))
        .await
        .unwrap();
    assert!(out.contains("Applied 3 edit(s)"));
    assert_eq!(fs::read_to_string(&path).unwrap(), "A B C");
}

#[tokio::test]
async fn test_multi_edit_sees_prior_edits() {
    let (_d, path) = tmp_file("foo");
    MultiEditTool
        .execute(json!({
            "path": path.to_str().unwrap(),
            "edits": [
                {"old_string": "foo", "new_string": "bar"},
                {"old_string": "bar", "new_string": "baz"}
            ]
        }))
        .await
        .unwrap();
    assert_eq!(fs::read_to_string(&path).unwrap(), "baz");
}

#[tokio::test]
async fn test_multi_edit_atomic_on_failure() {
    let (_d, path) = tmp_file("hello world");
    let err = MultiEditTool
        .execute(json!({
            "path": path.to_str().unwrap(),
            "edits": [
                {"old_string": "hello", "new_string": "hi"},
                {"old_string": "nonexistent", "new_string": "x"}
            ]
        }))
        .await
        .expect_err("should fail");
    assert!(err.to_string().contains("not found"));
    assert_eq!(
        fs::read_to_string(&path).unwrap(),
        "hello world",
        "file must not be modified when any edit fails"
    );
}

#[tokio::test]
async fn test_multi_edit_rejects_non_unique() {
    let (_d, path) = tmp_file("aa aa aa");
    let err = MultiEditTool
        .execute(json!({
            "path": path.to_str().unwrap(),
            "edits": [
                {"old_string": "aa", "new_string": "b"}
            ]
        }))
        .await
        .expect_err("should fail");
    assert!(err.to_string().contains("found 3 times"));
}

#[tokio::test]
async fn test_multi_edit_empty_edits() {
    let (_d, path) = tmp_file("x");
    let err = MultiEditTool
        .execute(json!({
            "path": path.to_str().unwrap(),
            "edits": []
        }))
        .await
        .expect_err("should fail");
    assert!(err.to_string().contains("at least one"));
}