devscout-rs 0.9.0

Fast code index for C# and TypeScript codebases -- map, find, refs, impact from the CLI.
use super::*;

const DUST: &str = "pub fn dust() {}\n";

#[test]
fn without_a_manifest_lib_and_main_are_roots() {
    let files = [
        ("src/lib.rs", "mod shelf;\nfn a() { shelf::dust(); }\n"),
        ("src/shelf.rs", DUST),
        ("app/main.rs", "mod tool;\nfn b() { tool::dust(); }\n"),
        ("app/tool.rs", DUST),
    ];
    assert_eq!(
        edges_for(&files),
        vec![
            "uses-member src/lib.rs:2 -> src/shelf.rs#dust",
            "uses-member app/main.rs:2 -> app/tool.rs#dust",
        ]
    );
}

#[test]
fn an_unreached_file_is_its_own_root_with_or_without_a_manifest() {
    let lone = (
        "src/lone.rs",
        "mod inner;\nfn a() { inner::dust(); crate::b(); }\nfn b() {}\n",
    );
    let inner = ("src/inner.rs", DUST);
    let want = vec![
        "uses-member src/lone.rs:2 -> src/inner.rs#dust",
        "uses-member src/lone.rs:2 -> src/lone.rs#b",
    ];
    assert_eq!(edges_for(&[lone, inner]), want);
    let manifest = ("Cargo.toml", "[package]\nname = \"p\"\n");
    let lib = ("src/lib.rs", "fn c() {}\n");
    assert_eq!(edges_for(&[manifest, lib, lone, inner]), want);
}

#[test]
fn a_declaration_resolves_beside_a_root_or_mod_rs_and_below_any_other_file() {
    let files = [
        (
            "src/lib.rs",
            "mod a;\nmod foo;\nfn t() { a::b::dust(); foo::bar::dust(); }\n",
        ),
        ("src/a/mod.rs", "pub mod b;\n"),
        ("src/a/b.rs", DUST),
        ("src/foo.rs", "pub mod bar;\n"),
        ("src/foo/bar.rs", DUST),
        ("src/bar.rs", DUST),
        ("src/b.rs", DUST),
    ];
    assert_eq!(
        edges_to(&files, "src/a/b.rs#dust"),
        vec!["uses-member src/lib.rs:3 -> src/a/b.rs#dust"]
    );
    assert_eq!(
        edges_to(&files, "src/foo/bar.rs#dust"),
        vec!["uses-member src/lib.rs:3 -> src/foo/bar.rs#dust"]
    );
    assert!(edges_to(&files, "src/bar.rs#dust").is_empty());
    assert!(edges_to(&files, "src/b.rs#dust").is_empty());
}

#[test]
fn an_inline_module_nests_and_its_out_of_line_child_resolves_one_directory_deeper() {
    let files = [
        ("src/lib.rs", "mod outer {\n    pub mod inner;\n    pub fn here() {}\n}\nfn t() { outer::inner::dust(); outer::here(); }\n"),
        ("src/outer/inner.rs", DUST),
        ("src/inner.rs", DUST),
    ];
    assert_eq!(
        edges_for(&files),
        vec![
            "uses-member src/lib.rs:5 -> src/outer/inner.rs#dust",
            "uses-member src/lib.rs:5 -> src/lib.rs#outer::here",
        ]
    );
}

#[test]
fn a_missing_or_doubled_module_file_reaches_nothing() {
    let files = [
        (
            "src/lib.rs",
            "mod gone;\nmod twice;\nfn t() { gone::dust(); twice::dust(); }\n",
        ),
        ("src/twice.rs", DUST),
        ("src/twice/mod.rs", DUST),
    ];
    assert!(edges_for(&files).is_empty());
}

#[test]
fn the_first_position_to_reach_a_file_places_it() {
    let files = [
        ("Cargo.toml", "[package]\nname = \"p\"\n"),
        ("src/lib.rs", "mod shared;\npub fn origin() {}\n"),
        (
            "src/main.rs",
            "mod shared;\npub fn origin() {}\nfn t() { shared::dust(); }\n",
        ),
        ("src/shared.rs", "pub fn dust() { crate::origin(); }\n"),
    ];
    let got = edges_for(&files);
    assert!(
        got.contains(&"uses-member src/shared.rs:1 -> src/lib.rs#origin".to_string()),
        "{got:?}"
    );
    assert!(
        !got.iter().any(|e| e.ends_with("src/main.rs#origin")),
        "{got:?}"
    );
    assert!(got.contains(&"uses-member src/main.rs:3 -> src/shared.rs#dust".to_string()));
}