a3s-code-core 6.5.1

A3S Code Core - Embeddable AI agent library with tool execution
Documentation
use super::*;

#[tokio::test]
async fn preserves_order_and_isolates_file_errors() {
    let temp = tempfile::tempdir().unwrap();
    std::fs::write(temp.path().join("a.txt"), "alpha\n").unwrap();
    std::fs::write(temp.path().join("b.txt"), "bravo\n").unwrap();
    let ctx = ToolContext::new(temp.path().to_path_buf());

    let result = ReadTool
        .execute(
            &serde_json::json!({
                "files": [
                    {"path": "a.txt"},
                    {"path": "missing.txt"},
                    {"path": "b.txt"}
                ]
            }),
            &ctx,
        )
        .await
        .unwrap();

    assert!(result.success, "{}", result.content);
    let a = result.content.find("=== a.txt ===").unwrap();
    let missing = result.content.find("=== missing.txt ===").unwrap();
    let b = result.content.find("=== b.txt ===").unwrap();
    assert!(a < missing && missing < b);
    assert!(result.content.contains("alpha"));
    assert!(result.content.contains("bravo"));
    assert!(result.content.contains("Failed to read file missing.txt"));

    let metadata = result.metadata.unwrap();
    assert_eq!(
        metadata["source_anchors"],
        serde_json::json!(["a.txt", "b.txt"])
    );
    assert_eq!(metadata["batch"]["requested_files"], 3);
    assert_eq!(metadata["batch"]["successful_files"], 2);
    assert_eq!(metadata["batch"]["failed_files"], 1);
    assert_eq!(metadata["batch"]["truncated"], false);
}

#[tokio::test]
async fn continuation_is_bounded_and_lossless() {
    let temp = tempfile::tempdir().unwrap();
    let a = (0..8)
        .map(|index| format!("A{index:02}-{}", "x".repeat(700)))
        .collect::<Vec<_>>()
        .join("\n");
    let b = (0..4)
        .map(|index| format!("B{index:02}-{}", "y".repeat(700)))
        .collect::<Vec<_>>()
        .join("\n");
    std::fs::write(temp.path().join("a.txt"), a).unwrap();
    std::fs::write(temp.path().join("b.txt"), b).unwrap();
    let ctx = ToolContext::new(temp.path().to_path_buf());
    let mut files = serde_json::json!([
        {"path": "a.txt"},
        {"path": "b.txt"}
    ]);
    let mut combined = String::new();

    for _ in 0..16 {
        let result = ReadTool
            .execute(
                &serde_json::json!({
                    "files": files,
                    "max_output_bytes": 2048
                }),
                &ctx,
            )
            .await
            .unwrap();
        assert!(result.success, "{}", result.content);
        assert!(result.content.len() <= 2048, "{}", result.content.len());
        combined.push_str(&result.content);

        let metadata = result.metadata.unwrap();
        files = metadata["batch"]["continuation"].clone();
        if files.as_array().unwrap().is_empty() {
            break;
        }
    }

    assert!(
        files.as_array().unwrap().is_empty(),
        "continuation did not finish"
    );
    for prefix in (0..8)
        .map(|index| format!("A{index:02}-"))
        .chain((0..4).map(|index| format!("B{index:02}-")))
    {
        assert_eq!(
            combined.matches(&prefix).count(),
            1,
            "marker {prefix} was duplicated or skipped"
        );
    }
}

#[tokio::test]
async fn rejects_duplicate_paths() {
    let temp = tempfile::tempdir().unwrap();
    std::fs::write(temp.path().join("same.txt"), "content").unwrap();
    let ctx = ToolContext::new(temp.path().to_path_buf());

    let result = ReadTool
        .execute(
            &serde_json::json!({
                "files": [
                    {"path": "same.txt"},
                    {"path": "./same.txt"}
                ]
            }),
            &ctx,
        )
        .await
        .unwrap();

    assert!(!result.success);
    assert!(result.content.contains("Duplicate path"));
}

#[cfg(unix)]
#[tokio::test]
async fn escapes_control_characters_in_batch_headers() {
    let temp = tempfile::tempdir().unwrap();
    let filename = "actual\n=== injected.txt ===";
    std::fs::write(temp.path().join(filename), "safe content").unwrap();
    let ctx = ToolContext::new(temp.path().to_path_buf());

    let result = ReadTool
        .execute(&serde_json::json!({"files": [{"path": filename}]}), &ctx)
        .await
        .unwrap();

    assert!(result.success, "{}", result.content);
    assert!(result.content.contains(r"actual\n=== injected.txt ==="));
    assert!(!result
        .content
        .lines()
        .any(|line| line == "=== injected.txt === ==="));
    assert_eq!(
        result.metadata.unwrap()["source_anchors"],
        serde_json::json!([filename])
    );
}