rustcodegraph 1.2.10

Local-first code intelligence graph and CLI for AI agents
Documentation
mod mcp_tool_improvements {
    use super::*;

    #[test]
    fn should_export_tool_handler_class() {
        if sqlite_unavailable() {
            return;
        }

        let mut handler = ToolHandler::new(false);
        assert!(!handler.has_default_code_graph());
        handler.close_all();
    }

    #[test]
    fn should_have_find_symbol_matches_and_truncate_output_as_private_methods() {
        if sqlite_unavailable() {
            return;
        }

        let mut handler = ToolHandler::new(false);
        let _find: fn(&mut ToolHandler, &mut CodeGraph, &str) -> Vec<Node> =
            ToolHandler::find_symbol_matches;
        let _truncate: fn(&str) -> String = truncate_output;
        handler.close_all();
    }

    #[test]
    fn should_truncate_output_exceeding_max_output_length() {
        if sqlite_unavailable() {
            return;
        }

        let short = "Hello world";
        assert_eq!(truncate_output(short), short);

        let long = "x".repeat(20_000);
        let result = truncate_output(&long);
        assert!(result.len() < long.len());
        assert!(result.contains("... (output truncated)"));
    }

    #[test]
    fn should_truncate_at_a_clean_line_boundary() {
        if sqlite_unavailable() {
            return;
        }

        let lines = (0..500)
            .map(|i| format!("Line {i}: {}", "a".repeat(50)))
            .collect::<Vec<_>>();
        let text = lines.join("\n");

        let result = truncate_output(&text);
        assert!(result.contains("... (output truncated)"));
        let before_truncation = result
            .split("\n\n... (output truncated)")
            .next()
            .expect("truncation split should have a prefix");
        assert!(before_truncation.ends_with('\n') || !before_truncation.contains('\0'));
    }

    mod find_symbol_disambiguation {
        use super::*;

        #[test]
        fn should_prefer_exact_name_matches() {
            if sqlite_unavailable() {
                return;
            }

            let tmp_dir = create_temp_dir();
            let src_dir = tmp_dir.path().join("src");
            fs::create_dir_all(&src_dir).expect("src fixture dir should be created");
            write_fixture(
                src_dir.join("a.ts"),
                r#"
export function getValue(): number { return 1; }
export function getValueFromCache(): number { return 2; }
"#,
            );

            let mut cg = CodeGraph::init_sync(tmp_dir.path()).expect("CodeGraph should initialize");
            let _ = cg.index_all(IndexOptions::default());

            let matches = find_symbol_matches(&mut cg, "getValue");
            assert_eq!(matches.len(), 1);
            assert_eq!(matches[0].name, "getValue");
            cg.destroy();
        }

        #[test]
        fn should_return_all_definitions_when_multiple_symbols_share_the_same_name() {
            if sqlite_unavailable() {
                return;
            }

            let tmp_dir = create_temp_dir();
            let src_dir = tmp_dir.path().join("src");
            fs::create_dir_all(&src_dir).expect("src fixture dir should be created");
            write_fixture(
                src_dir.join("a.ts"),
                r#"
export function handle(): void {}
"#,
            );
            write_fixture(
                src_dir.join("b.ts"),
                r#"
export function handle(): void {}
"#,
            );

            let mut cg = CodeGraph::init_sync(tmp_dir.path()).expect("CodeGraph should initialize");
            let _ = cg.index_all(IndexOptions::default());

            let matches = find_symbol_matches(&mut cg, "handle");
            assert_eq!(matches.len(), 2);
            assert!(matches.iter().all(|node| node.name == "handle"));
            cg.destroy();
        }

        #[test]
        fn should_return_no_matches_when_symbol_is_not_found() {
            if sqlite_unavailable() {
                return;
            }

            let tmp_dir = create_temp_dir();
            let src_dir = tmp_dir.path().join("src");
            fs::create_dir_all(&src_dir).expect("src fixture dir should be created");
            write_fixture(src_dir.join("a.ts"), "export function foo(): void {}");

            let mut cg = CodeGraph::init_sync(tmp_dir.path()).expect("CodeGraph should initialize");
            let _ = cg.index_all(IndexOptions::default());

            let matches = find_symbol_matches(&mut cg, "nonExistentSymbol");
            assert_eq!(matches.len(), 0);
            cg.destroy();
        }
    }
}