gobby-code 1.3.3

Fast Rust CLI for Gobby's code index — AST-aware search, symbol navigation, and dependency graph
Documentation
use super::super::{IndexDegradation, IndexDurations, IndexOutcome, IndexRequest};
use crate::models::{CallRelation, CallTargetKind};
use serde::Serialize;
use serde::de::DeserializeOwned;
use std::path::PathBuf;

fn assert_cli_independent_contract<T>()
where
    T: Serialize + DeserializeOwned,
{
    let type_name = std::any::type_name::<T>();
    assert!(!type_name.contains("commands::"), "{type_name}");
    assert!(!type_name.contains("output::"), "{type_name}");
    assert!(!type_name.contains("clap"), "{type_name}");
}

#[test]
fn library_api_is_cli_independent() {
    assert_cli_independent_contract::<IndexRequest>();
    assert_cli_independent_contract::<IndexOutcome>();
    assert_cli_independent_contract::<IndexDurations>();
    assert_cli_independent_contract::<IndexDegradation>();

    let request = IndexRequest {
        project_root: PathBuf::from("/tmp/project"),
        path_filter: Some(PathBuf::from("src")),
        explicit_files: vec![PathBuf::from("src/lib.rs")],
        full: true,
        require_cpp_semantics: false,
        sync_projections: true,
    };

    let json = serde_json::to_value(&request).expect("request serializes");
    assert_eq!(json["project_root"], "/tmp/project");
    assert_eq!(json["path_filter"], "src");
    assert_eq!(json["explicit_files"][0], "src/lib.rs");
}

#[test]
fn invalidate_postgres_deletes_are_project_scoped() {
    let source = include_str!("../lifecycle.rs");
    for expected in [
        "DELETE FROM code_symbols WHERE project_id = $1",
        "DELETE FROM code_indexed_files WHERE project_id = $1",
        "DELETE FROM code_content_chunks WHERE project_id = $1",
        "DELETE FROM code_imports WHERE project_id = $1",
        "DELETE FROM code_calls WHERE project_id = $1",
        "DELETE FROM code_indexed_projects WHERE id = $1",
    ] {
        assert!(
            source.contains(expected),
            "missing scoped delete: {expected}"
        );
    }
    let truncate_code = ["TRUNCATE", " code_"].concat();
    let drop_table = ["DROP", " TABLE"].concat();
    assert!(!source.contains(&truncate_code));
    assert!(!source.contains(&drop_table));
}

#[test]
fn call_relation_contract_uses_empty_optional_storage_values() {
    let resolved = CallRelation::new(
        "caller-1".to_string(),
        "foo".to_string(),
        "src/main.py".to_string(),
        12,
    )
    .with_symbol_target("callee-1".to_string());
    let unresolved = CallRelation::new(
        "caller-2".to_string(),
        "bar".to_string(),
        "src/main.py".to_string(),
        18,
    );

    assert_eq!(
        resolved.callee_symbol_id.as_deref().unwrap_or(""),
        "callee-1"
    );
    assert_eq!(unresolved.callee_symbol_id.as_deref().unwrap_or(""), "");
    assert_eq!(resolved.callee_target_kind, CallTargetKind::Symbol);
    assert_eq!(unresolved.callee_target_kind, CallTargetKind::Unresolved);
}