agent-file-tools 0.26.2

Agent File Tools — tree-sitter powered code analysis for AI agents
Documentation
use std::fs;
use std::path::PathBuf;

use aft::semantic_index::SemanticIndex;

fn write_source_fixture(project_root: &std::path::Path) -> PathBuf {
    let source_file = project_root.join("src/lib.rs");
    fs::create_dir_all(source_file.parent().expect("source parent")).expect("create src dir");
    fs::write(
        &source_file,
        "pub fn handle_request(token: &str) -> bool {\n    !token.is_empty()\n}\n\npub fn normalize_user_id(input: &str) -> String {\n    input.trim().to_lowercase()\n}\n",
    )
    .expect("write source file");
    source_file
}

#[test]
fn build_returns_backend_http_errors_verbatim() {
    let project = tempfile::tempdir().expect("create project dir");
    let source_file = write_source_fixture(project.path());
    let files = vec![source_file];
    let mut embed = |_texts: Vec<String>| {
        Err::<Vec<Vec<f32>>, String>(
            "openai compatible request failed (HTTP 401): Unauthorized".to_string(),
        )
    };

    let error = match SemanticIndex::build(project.path(), &files, &mut embed, 16) {
        Err(error) => error,
        Ok(_) => panic!("expected backend HTTP error"),
    };

    assert_eq!(
        error,
        "openai compatible request failed (HTTP 401): Unauthorized"
    );
}

#[test]
fn build_returns_error_when_embedding_backend_returns_no_vectors() {
    let project = tempfile::tempdir().expect("create project dir");
    let source_file = write_source_fixture(project.path());
    let files = vec![source_file];
    let mut embed = |_texts: Vec<String>| Ok::<Vec<Vec<f32>>, String>(vec![]);

    let error = match SemanticIndex::build(project.path(), &files, &mut embed, 16) {
        Err(error) => error,
        Ok(_) => panic!("expected empty-vector validation error"),
    };

    assert_eq!(error, "embedding backend returned no vectors for 3 inputs");
}

#[test]
fn build_returns_error_when_embedding_dimension_changes_across_batches() {
    let project = tempfile::tempdir().expect("create project dir");
    let source_file = write_source_fixture(project.path());
    let files = vec![source_file];
    let mut call_count = 0usize;
    let mut embed = |_texts: Vec<String>| {
        call_count += 1;
        match call_count {
            1 => Ok::<Vec<Vec<f32>>, String>(vec![vec![1.0; 384]]),
            2 => Ok::<Vec<Vec<f32>>, String>(vec![vec![1.0; 512]]),
            _ => panic!("unexpected extra embedding batch"),
        }
    };

    let error = match SemanticIndex::build(project.path(), &files, &mut embed, 1) {
        Err(error) => error,
        Ok(_) => panic!("expected dimension mismatch validation error"),
    };

    assert_eq!(
        error,
        "embedding dimension changed across batches: expected 384, got 512"
    );
}

#[test]
fn build_returns_error_when_embedding_backend_returns_too_few_vectors() {
    let project = tempfile::tempdir().expect("create project dir");
    let source_file = write_source_fixture(project.path());

    let files = vec![source_file];
    let mut embed = |texts: Vec<String>| {
        Ok::<Vec<Vec<f32>>, String>(
            texts
                .into_iter()
                .skip(1)
                .map(|_| vec![1.0, 0.0, 0.0])
                .collect(),
        )
    };

    let error = match SemanticIndex::build(project.path(), &files, &mut embed, 16) {
        Err(error) => error,
        Ok(_) => panic!("expected vector count validation error"),
    };

    assert_eq!(error, "embedding backend returned 2 vectors for 3 inputs");
}