agent-file-tools 0.56.2

Agent File Tools — tree-sitter powered code analysis for AI agents
Documentation
use super::*;

#[test]
fn semantic_fill_has_no_derived_writes_or_checkpoint_and_reader_uses_owner() {
    let project = tempfile::tempdir().unwrap();
    let storage = tempfile::tempdir().unwrap();
    let source = b"pub fn example() {}\n";
    fs::write(project.path().join("lib.rs"), source).unwrap();
    for args in [
        vec!["init", "--quiet"],
        vec!["add", "."],
        vec![
            "-c",
            "user.name=Test",
            "-c",
            "user.email=test@example.com",
            "commit",
            "--allow-empty",
            "--quiet",
            "-m",
            "base",
        ],
    ] {
        assert!(std::process::Command::new("git")
            .current_dir(project.path())
            .args(args)
            .status()
            .unwrap()
            .success());
    }
    let mut request = AssemblyRequest {
        storage: storage.path().to_path_buf(),
        project_root: project.path().to_path_buf(),
        family: "fill-family".into(),
        scope: "fill-scope".into(),
        desired_head: "head".into(),
        changed_paths: BTreeSet::from([b"lib.rs".to_vec()]),
        semantic_keys: Default::default(),
        require_semantic: true,
        allow_blob_put: true,
    };
    let initial = publish_checkout(&request).unwrap();
    assert!(initial.published);
    assert_eq!(initial.pending_paths, BTreeSet::from([b"lib.rs".to_vec()]));
    let view = ViewStore::open(storage.path(), &request.scope).unwrap();
    let owner_path = view
        .derived_path(initial.generation.as_deref().unwrap())
        .unwrap();
    let connection = Connection::open(&owner_path).unwrap();
    connection.execute_batch("CREATE TRIGGER no_metadata_writes BEFORE INSERT ON meta BEGIN SELECT RAISE(ABORT, 'derived metadata rewritten'); END;").unwrap();
    drop(connection);
    let key =
        crate::blob_store::SemanticKey::for_current(source, b"lib.rs", "fixture-model").full_key();
    crate::blob_store::BlobStore::open(
        storage.path(),
        &request.family,
        crate::blob_store::BlobPlane::Semantic,
    )
    .unwrap()
    .put(&key, b"vector")
    .unwrap();
    request
        .semantic_keys
        .insert(b"lib.rs".to_vec(), key.to_hex());
    let mut prepared = prepare_checkout(&request, &mut |_| Ok(())).unwrap();
    assert!(
        prepared.derived_checkpoint.is_none(),
        "semantic fill scheduled a callgraph checkpoint"
    );
    assert_eq!(prepared.profile.materialization_call_ms, 0);
    let fill = prepared.commit().unwrap();
    assert!(fill.published);
    assert!(fill.pending_paths.is_empty());
    let reader = crate::callgraph_store::ReadonlyCallGraphStore::open_manifest_view(
        project.path().to_path_buf(),
        request.family,
        view.view_dir().to_path_buf(),
        fill.generation.as_deref().unwrap(),
        None,
    )
    .unwrap();
    assert_eq!(reader.sqlite_path(), owner_path);
}

#[test]
fn prepared_callgraph_retains_committed_wal_before_pointer_publication() {
    let project = tempfile::tempdir().unwrap();
    let storage = tempfile::tempdir().unwrap();
    fs::write(project.path().join("lib.rs"), "pub fn retained() {}\n").unwrap();
    for args in [
        vec!["init", "--quiet"],
        vec!["add", "."],
        vec![
            "-c",
            "user.name=Test",
            "-c",
            "user.email=test@example.com",
            "commit",
            "--quiet",
            "-m",
            "base",
        ],
    ] {
        assert!(std::process::Command::new("git")
            .current_dir(project.path())
            .args(args)
            .status()
            .unwrap()
            .success());
    }
    let request = AssemblyRequest {
        storage: storage.path().to_path_buf(),
        project_root: project.path().to_path_buf(),
        family: "wal-family".into(),
        scope: "wal-scope".into(),
        desired_head: "head".into(),
        changed_paths: BTreeSet::new(),
        semantic_keys: Default::default(),
        require_semantic: false,
        allow_blob_put: true,
    };
    let prepared = prepare_checkout(&request, &mut |_| Ok(())).unwrap();
    let (path, _) = prepared
        .derived_checkpoint
        .as_ref()
        .expect("graph checkpoint keeper");
    let wal = PathBuf::from(format!("{}-wal", path.display()));
    assert!(
        fs::metadata(&wal).is_ok_and(|metadata| metadata.len() > 32),
        "committed graph WAL was checkpointed on writer close before pointer publication"
    );
}