use super::ast::chunk_ast;
use super::document::{
chunk_json, chunk_markdown, chunk_plaintext, chunk_toml, chunk_xml, chunk_yaml,
};
use super::types::{chunk_text, ChunkType, RawChunk};
#[test]
fn test_overlapping_chunks() {
let content = (1..=200)
.map(|i| format!("line {i}"))
.collect::<Vec<_>>()
.join("\n");
let chunks = chunk_text("test.txt", &content, 150, 50);
assert!(chunks.len() >= 2);
assert_eq!(chunks[0].start_line, 1);
assert_eq!(chunks[1].start_line, 51);
}
#[test]
fn test_chunk_id_format() {
let chunks = chunk_text("src/main.txt", "line1\nline2\nline3", 150, 50);
assert!(chunks[0].id.starts_with("src/main.txt:"));
}
#[test]
fn test_rust_function_chunking() {
let src = r#"
fn alpha() {}
fn beta() -> i32 { 1 }
fn gamma(x: i32) -> i32 { x + 1 }
"#;
let (chunks, _ents) = chunk_ast("a.rs", src);
let fns: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Function)
.collect();
assert_eq!(fns.len(), 3, "expected 3 function chunks, got {fns:?}");
let names: Vec<_> = fns
.iter()
.map(|c| c.function_name.clone().unwrap_or_default())
.collect();
assert!(names.contains(&"alpha".to_string()));
assert!(names.contains(&"beta".to_string()));
assert!(names.contains(&"gamma".to_string()));
}
#[test]
fn test_rust_impl_method_qualified_name() {
let src = r#"
struct Foo;
impl Foo {
fn bar(&self) {}
}
"#;
let (chunks, _) = chunk_ast("foo.rs", src);
let method = chunks
.iter()
.find(|c| c.chunk_type == ChunkType::Method)
.expect("expected at least one Method chunk");
assert_eq!(method.function_name.as_deref(), Some("Foo::bar"));
}
#[test]
fn test_rust_calls_extraction() {
let src = r#"
fn main() {
foo();
bar(1, 2);
}
fn foo() {}
fn bar(_a: i32, _b: i32) {}
"#;
let (chunks, _) = chunk_ast("m.rs", src);
let main_chunk = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("main"))
.expect("main chunk");
assert!(
main_chunk.calls.contains(&"foo".to_string()),
"calls={:?}",
main_chunk.calls
);
assert!(
main_chunk.calls.contains(&"bar".to_string()),
"calls={:?}",
main_chunk.calls
);
}
#[test]
fn test_rust_entity_named_types() {
let src = r#"
use std::sync::Arc;
fn f() {
let _x: Arc<Vec<String>> = Arc::new(Vec::new());
}
"#;
let (_chunks, entities) = chunk_ast("t.rs", src);
let named: Vec<&str> = entities
.iter()
.filter(|e| e.entity_type == crate::core::entity::EntityType::NamedType)
.map(|e| e.text.as_str())
.collect();
assert!(named.contains(&"Arc"), "named_types={named:?}");
assert!(named.contains(&"Vec"), "named_types={named:?}");
assert!(named.contains(&"String"), "named_types={named:?}");
}
#[test]
fn test_large_function_splits() {
let mut body = String::new();
for i in 0..250 {
body.push_str(&format!(" let _v{i} = {i};\n"));
}
let src = format!("fn huge() {{\n{body}}}\n");
let (chunks, _) = chunk_ast("h.rs", &src);
let subs: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.parent_chunk_id.is_some())
.collect();
assert!(
!subs.is_empty(),
"expected sub-chunks for 250-line fn, got {chunks:#?}"
);
let parent_id = subs[0].parent_chunk_id.clone().unwrap();
let parent = chunks
.iter()
.find(|c| c.id == parent_id)
.expect("parent retained");
assert!(!parent.child_chunk_ids.is_empty());
}
#[test]
fn test_unknown_language_fallback() {
let content = "hello world\nfoo bar\nbaz";
let (chunks, entities) = chunk_ast("notes.unknownext", content);
assert!(entities.is_empty());
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0].chunk_type, ChunkType::Code);
}
#[test]
fn test_chunk_markdown_sections() {
let content = "# Title\n\nintro\n\n## Section A\n\nbody a\n\n## Section B\n\nbody b\n";
let chunks = chunk_markdown("doc.md", content);
assert!(
chunks.len() >= 2,
"expected multiple sections, got {chunks:#?}"
);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(names.iter().any(|n| n == "Section A"), "names={names:?}");
assert!(names.iter().any(|n| n == "Section B"), "names={names:?}");
for c in &chunks {
assert_eq!(c.language.as_deref(), Some("markdown"));
assert_eq!(c.chunk_type, ChunkType::Docstring);
}
}
#[test]
fn test_chunk_markdown_ignores_hash_in_code_fence() {
let content = "# Real Heading\n\nintro\n\n```\n## not a heading\n```\n\n## Next\n\nx\n";
let chunks = chunk_markdown("doc.md", content);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(names.iter().any(|n| n == "Real Heading"));
assert!(names.iter().any(|n| n == "Next"));
assert!(
!names.iter().any(|n| n == "not a heading"),
"should not split on # inside fenced code block: {names:?}"
);
}
#[test]
fn test_chunk_yaml_top_level_keys() {
let content = "name: foo\nversion: 1.0\n\ndeps:\n - a\n - b\n\nscripts:\n build: x\n";
let chunks = chunk_yaml("conf.yaml", content);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(names.iter().any(|n| n == "name"), "names={names:?}");
assert!(names.iter().any(|n| n == "deps"), "names={names:?}");
assert!(names.iter().any(|n| n == "scripts"), "names={names:?}");
for c in &chunks {
assert_eq!(c.language.as_deref(), Some("yaml"));
}
}
#[test]
fn test_chunk_toml_sections() {
let content = "[package]\nname = \"foo\"\nversion = \"1.0\"\n\n[dependencies]\nserde = \"1\"\n\n[[bin]]\nname = \"x\"\n";
let chunks = chunk_toml("Cargo.toml", content);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(names.iter().any(|n| n == "package"), "names={names:?}");
assert!(names.iter().any(|n| n == "dependencies"), "names={names:?}");
assert!(names.iter().any(|n| n == "bin"), "names={names:?}");
}
#[test]
fn test_chunk_json_small_file_single_chunk() {
let content = "{\n \"name\": \"foo\",\n \"version\": \"1.0\"\n}\n";
let chunks = chunk_json("a.json", content).expect("Some result");
assert_eq!(chunks.len(), 1);
assert_eq!(chunks[0].language.as_deref(), Some("json"));
}
#[test]
fn test_chunk_json_large_file_skipped() {
let big = (0..600)
.map(|i| format!(" \"k{i}\": {i},"))
.collect::<Vec<_>>()
.join("\n");
let content = format!("{{\n{big}\n}}\n");
let chunks = chunk_json("big.json", &content).expect("Some result");
assert!(chunks.is_empty(), "expected large JSON to be skipped");
}
#[test]
fn test_chunk_plaintext_paragraphs() {
let content = "First paragraph line 1.\nFirst paragraph line 2.\n\nSecond paragraph line 1.\nSecond paragraph line 2.\n\nThird paragraph.\n";
let chunks = chunk_plaintext("note.txt", content);
assert_eq!(
chunks.len(),
3,
"expected one chunk per paragraph, got {chunks:#?}"
);
for c in &chunks {
assert_eq!(c.language.as_deref(), Some("text"));
}
}
#[test]
fn test_chunk_plaintext_caps_at_50_lines() {
let content = (1..=130)
.map(|i| format!("line {i}"))
.collect::<Vec<_>>()
.join("\n");
let chunks = chunk_plaintext("big.log", &content);
assert!(
chunks.len() >= 3,
"expected at least 3 chunks for 130-line paragraph, got {}",
chunks.len()
);
for c in &chunks {
let line_count = c.end_line.saturating_sub(c.start_line) + 1;
assert!(line_count <= 50, "chunk too large: {line_count} lines");
assert_eq!(c.language.as_deref(), Some("log"));
}
}
#[test]
fn test_chunk_xml_top_level_children() {
let content = "<?xml version=\"1.0\"?>\n<library>\n <book id=\"1\">\n <title>A</title>\n </book>\n <book id=\"2\">\n <title>B</title>\n </book>\n <magazine>\n <title>C</title>\n </magazine>\n</library>\n";
let chunks = chunk_xml("data.xml", content);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(
names.iter().filter(|n| *n == "book").count() >= 2,
"names={names:?}"
);
assert!(names.iter().any(|n| n == "magazine"), "names={names:?}");
for c in &chunks {
assert_eq!(c.language.as_deref(), Some("xml"));
}
}
#[test]
fn test_chunk_xml_malformed_leading_close() {
let content = "</root>\n<other/>\n";
let chunks = chunk_xml("bad.xml", content);
for c in &chunks {
assert!(
c.content.contains("</root>"),
"unexpected partial chunk: {:?}",
c.content
);
}
}
#[test]
fn test_chunk_xml_malformed_extra_closes() {
let content = "<root>\n</child>\n</child>\n</child>\n</root>\n";
let chunks = chunk_xml("extra_closes.xml", content);
assert!(
chunks.len() <= 2,
"unexpected chunk count {}: {:?}",
chunks.len(),
chunks.iter().map(|c| &c.content).collect::<Vec<_>>()
);
}
#[test]
fn test_chunk_xml_well_formed_unchanged() {
let content = "<?xml version=\"1.0\"?>\n<library>\n <book id=\"1\">\n <title>A</title>\n </book>\n <book id=\"2\">\n <title>B</title>\n </book>\n <magazine>\n <title>C</title>\n </magazine>\n</library>\n";
let chunks = chunk_xml("data.xml", content);
let names: Vec<_> = chunks
.iter()
.filter_map(|c| c.function_name.clone())
.collect();
assert!(
names.iter().filter(|n| *n == "book").count() >= 2,
"well-formed regression: book count wrong; names={names:?}"
);
assert!(
names.iter().any(|n| n == "magazine"),
"well-formed regression: magazine missing; names={names:?}"
);
for c in &chunks {
assert_eq!(c.language.as_deref(), Some("xml"));
}
}
#[test]
fn test_chunk_document_dispatch() {
let md_content = "# Hello\n\nworld\n";
let (md_chunks, _) = chunk_ast("readme.md", md_content);
assert!(md_chunks
.iter()
.any(|c| c.language.as_deref() == Some("markdown")));
let yaml_content = "key: value\n";
let (yaml_chunks, _) = chunk_ast("conf.yml", yaml_content);
assert!(yaml_chunks
.iter()
.any(|c| c.language.as_deref() == Some("yaml")));
let toml_content = "[section]\nx = 1\n";
let (toml_chunks, _) = chunk_ast("a.toml", toml_content);
assert!(toml_chunks
.iter()
.any(|c| c.language.as_deref() == Some("toml")));
}
#[test]
fn test_nlp_code_refs() {
let src = r#"
/// Wraps the `CodeIndexer` to expose hybrid search.
fn make() {}
"#;
let (chunks, _) = chunk_ast("d.rs", src);
let f = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("make"))
.unwrap();
assert!(
f.nlp_code_refs.iter().any(|k| k == "CodeIndexer"),
"code_refs={:?}",
f.nlp_code_refs
);
}
#[test]
fn test_entity_external_crate() {
let src = r#"
use usearch::Index;
fn f() {}
"#;
let (_chunks, ents) = chunk_ast("u.rs", src);
let exts: Vec<&str> = ents
.iter()
.filter(|e| e.entity_type == crate::core::entity::EntityType::ExternalCrate)
.map(|e| e.text.as_str())
.collect();
assert!(exts.contains(&"usearch"), "external_crates={exts:?}");
}
#[test]
fn test_entity_error_variant() {
let src = r#"
fn f() -> Result<(), anyhow::Error> {
anyhow::bail!("index not found");
}
"#;
let (_chunks, ents) = chunk_ast("e.rs", src);
let any_err = ents
.iter()
.any(|e| e.entity_type == crate::core::entity::EntityType::ErrorVariant);
assert!(
any_err,
"expected at least one ErrorVariant entity, got {ents:#?}"
);
}
#[test]
fn test_csharp_chunking() {
let src = r#"
namespace MyApp {
class Foo {
public void Bar() { Baz(); this.Qux(); }
public Foo() {}
}
interface IThing { void Do(); }
}
"#;
let (chunks, _) = chunk_ast("a.cs", src);
let classes: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Class)
.collect();
assert!(
classes
.iter()
.any(|c| c.function_name.as_deref() == Some("Foo")),
"expected class Foo, got {chunks:#?}"
);
let traits: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Trait)
.collect();
assert!(
traits
.iter()
.any(|c| c.function_name.as_deref() == Some("IThing")),
"expected interface IThing as Trait"
);
let bar = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Bar"))
.expect("Bar method chunk");
assert_eq!(bar.chunk_type, ChunkType::Method);
assert!(
bar.calls.contains(&"Baz".to_string()),
"calls={:?}",
bar.calls
);
assert!(
bar.calls.contains(&"Qux".to_string()),
"calls={:?}",
bar.calls
);
}
#[test]
fn test_kotlin_chunking() {
let src = r#"
class Foo {
fun bar() { baz(); this.qux() }
}
object Singleton {
fun run() { other() }
}
"#;
let (chunks, _) = chunk_ast("a.kt", src);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("Foo") && c.chunk_type == ChunkType::Class),
"expected class Foo, got {chunks:#?}"
);
let bar = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("bar"))
.expect("bar method chunk");
assert_eq!(bar.chunk_type, ChunkType::Method);
assert!(
bar.calls.contains(&"baz".to_string()),
"calls={:?}",
bar.calls
);
assert!(
bar.calls.contains(&"qux".to_string()),
"calls={:?}",
bar.calls
);
}
#[test]
fn test_swift_chunking() {
let src = r#"
class Foo {
func bar() { baz(); self.qux() }
init() {}
}
struct S {}
enum E { case a }
protocol P { func d() }
extension Foo { func ext() {} }
"#;
let (chunks, _) = chunk_ast("a.swift", src);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("Foo") && c.chunk_type == ChunkType::Class),
"expected class Foo, got {chunks:#?}"
);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("S") && c.chunk_type == ChunkType::Struct),
"expected struct S"
);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("E") && c.chunk_type == ChunkType::Enum),
"expected enum E"
);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("P") && c.chunk_type == ChunkType::Trait),
"expected protocol P as Trait"
);
assert!(
chunks
.iter()
.any(|c| c.chunk_type == ChunkType::Module
&& c.function_name.as_deref() == Some("Foo")),
"expected extension Foo as Module"
);
let bar = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("bar"))
.expect("bar method chunk");
assert!(
bar.calls.contains(&"baz".to_string()),
"calls={:?}",
bar.calls
);
assert!(
bar.calls.contains(&"qux".to_string()),
"calls={:?}",
bar.calls
);
}
#[test]
fn test_nlp_keywords_from_doc_comments() {
let src = r#"
/// Implements the RRF fusion algorithm.
fn fuse() {}
"#;
let (chunks, _) = chunk_ast("d.rs", src);
let f = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("fuse"))
.unwrap();
assert!(
f.nlp_keywords.iter().any(|k| k == "RRF"),
"keywords={:?}",
f.nlp_keywords
);
assert!(
f.nlp_keywords.iter().any(|k| k == "Implements"),
"keywords={:?}",
f.nlp_keywords
);
}
#[test]
fn test_scala_method_qualified_name() {
let src = r#"
class Foo extends Bar with Mixin {
def bar(): Unit = baz()
}
object O {
def run(): Unit = other()
}
def freefn(): Unit = ()
"#;
let (chunks, _) = chunk_ast("a.scala", src);
let bar = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo::bar"))
.expect("expected qualified method Foo::bar, got: {chunks:#?}");
assert_eq!(bar.chunk_type, ChunkType::Method);
let run = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("O::run"))
.expect("expected qualified method O::run");
assert_eq!(run.chunk_type, ChunkType::Method);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("freefn")
&& c.chunk_type == ChunkType::Function),
"expected unqualified Function freefn, got {chunks:#?}"
);
}
#[test]
fn test_scala_caller_scoped_call_edges() {
let src = r#"
class Foo {
def bar(): Unit = {
baz()
this.qux()
}
}
"#;
let (chunks, _) = chunk_ast("a.scala", src);
let bar = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo::bar"))
.expect("Foo::bar chunk");
assert!(
bar.calls.contains(&"baz".to_string()),
"calls={:?}",
bar.calls
);
assert!(
bar.calls.contains(&"qux".to_string()),
"calls={:?}",
bar.calls
);
}
#[test]
fn test_scala_extends_and_with_emit_inherits() {
let src = r#"
class Foo extends Bar with Mixin with Other {
def m(): Unit = ()
}
"#;
let (chunks, _) = chunk_ast("a.scala", src);
let foo = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo") && c.chunk_type == ChunkType::Class)
.expect("Foo class chunk");
for parent in ["Bar", "Mixin", "Other"] {
assert!(
foo.inherits_from.iter().any(|p| p == parent),
"expected parent {parent} in inherits_from={:?}",
foo.inherits_from
);
}
}
#[test]
fn test_scala_symbol_graph_resolves_caller() {
use crate::core::symbol_graph::SymbolGraph;
let src = r#"
class Foo {
def bar(): Unit = baz()
}
def baz(): Unit = ()
"#;
let (chunks, _) = chunk_ast("s.scala", src);
let tuples: Vec<_> = chunks
.iter()
.map(|c| {
(
c.id.clone(),
c.file.clone(),
c.function_name.clone(),
c.calls.clone(),
c.inherits_from.clone(),
c.chunk_type.clone(),
)
})
.collect();
let g = SymbolGraph::build_from_chunks(&tuples);
let callers = g.callers_of("baz", 1);
assert!(
callers.iter().any(|(s, _)| s == "Foo::bar"),
"callers={callers:?}"
);
}
#[test]
fn test_php_method_qualified_name() {
let src = r#"<?php
class Foo extends Bar implements I1, I2 {
public function doIt(): void {
$this->helper();
}
}
function freefn(): void {}
"#;
let (chunks, _) = chunk_ast("a.php", src);
let doit = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo::doIt"))
.expect("expected qualified Foo::doIt, got: {chunks:#?}");
assert_eq!(doit.chunk_type, ChunkType::Method);
assert!(
chunks
.iter()
.any(|c| c.function_name.as_deref() == Some("freefn")
&& c.chunk_type == ChunkType::Function),
"expected unqualified Function freefn"
);
}
#[test]
fn test_php_caller_scoped_call_edges() {
let src = r#"<?php
class Foo {
public function doIt(): void {
$this->helper();
Foo::staticCall();
regularFunc();
}
}
"#;
let (chunks, _) = chunk_ast("a.php", src);
let doit = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo::doIt"))
.expect("Foo::doIt chunk");
for callee in ["helper", "staticCall", "regularFunc"] {
assert!(
doit.calls.iter().any(|c| c == callee),
"expected callee {callee} in calls={:?}",
doit.calls
);
}
}
#[test]
fn test_php_implements_and_extends_emit_inherits() {
let src = r#"<?php
class Foo extends Bar implements I1, I2 {}
"#;
let (chunks, _) = chunk_ast("a.php", src);
let foo = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Foo") && c.chunk_type == ChunkType::Class)
.expect("Foo class chunk");
for parent in ["Bar", "I1", "I2"] {
assert!(
foo.inherits_from.iter().any(|p| p == parent),
"expected parent {parent} in inherits_from={:?}",
foo.inherits_from
);
}
}
#[test]
fn test_php_interface_extends_emits_inherits() {
let src = r#"<?php
interface Child extends P1, P2 {}
"#;
let (chunks, _) = chunk_ast("a.php", src);
let child = chunks
.iter()
.find(|c| c.function_name.as_deref() == Some("Child") && c.chunk_type == ChunkType::Trait)
.expect("Child interface (chunked as Trait)");
for parent in ["P1", "P2"] {
assert!(
child.inherits_from.iter().any(|p| p == parent),
"expected parent {parent} in inherits_from={:?}",
child.inherits_from
);
}
}
#[test]
fn test_php_symbol_graph_resolves_caller() {
use crate::core::symbol_graph::SymbolGraph;
let src = r#"<?php
class Foo {
public function doIt(): void {
$this->helper();
}
public function helper(): void {}
}
"#;
let (chunks, _) = chunk_ast("p.php", src);
let tuples: Vec<_> = chunks
.iter()
.map(|c| {
(
c.id.clone(),
c.file.clone(),
c.function_name.clone(),
c.calls.clone(),
c.inherits_from.clone(),
c.chunk_type.clone(),
)
})
.collect();
let g = SymbolGraph::build_from_chunks(&tuples);
let callers = g.callers_of("Foo::helper", 1);
assert!(
callers.iter().any(|(s, _)| s == "Foo::doIt"),
"callers={callers:?}"
);
}
#[test]
fn test_rust_pub_const_chunking_produces_n_constant_chunks() {
let src = r#"
pub const ALPHA: u32 = 1;
pub const BRUSILOV_EPOCH: u64 = 1_000_000;
pub const MAX_BATCH_SIZE: usize = 256;
pub const KIKUCHI_MAX_DEPTH: usize = 8;
pub const HNSW_EF_CONSTRUCTION: usize = 200;
pub const DEFAULT_TOP_K: usize = 10;
pub const BM25_K1: f32 = 1.5;
pub const BM25_B: f32 = 0.75;
"#;
let (chunks, _) = chunk_ast("constants.rs", src);
let const_chunks: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Constant)
.collect();
assert_eq!(
const_chunks.len(),
8,
"expected 8 Constant chunks (one per pub const), got {}: {:#?}",
const_chunks.len(),
chunks
);
for c in &const_chunks {
assert!(
c.function_name.is_some(),
"expected non-null function_name for constant chunk {c:#?}"
);
}
let names: Vec<_> = const_chunks
.iter()
.filter_map(|c| c.function_name.as_deref())
.collect();
assert!(
names.contains(&"BRUSILOV_EPOCH"),
"expected BRUSILOV_EPOCH in names: {names:?}"
);
assert!(
names.contains(&"MAX_BATCH_SIZE"),
"expected MAX_BATCH_SIZE in names: {names:?}"
);
}
#[test]
fn test_rust_mixed_const_and_fn_chunking() {
let src = r#"
pub const FOO: u32 = 42;
pub fn do_something() -> u32 {
FOO + 1
}
pub const BAR: &str = "hello";
"#;
let (chunks, _) = chunk_ast("mixed.rs", src);
let const_chunks: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Constant)
.collect();
assert_eq!(
const_chunks.len(),
2,
"expected 2 Constant chunks, got {const_chunks:#?}"
);
let const_names: Vec<_> = const_chunks
.iter()
.filter_map(|c| c.function_name.as_deref())
.collect();
assert!(
const_names.contains(&"FOO"),
"expected FOO: {const_names:?}"
);
assert!(
const_names.contains(&"BAR"),
"expected BAR: {const_names:?}"
);
let fn_chunks: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Function)
.collect();
assert_eq!(
fn_chunks.len(),
1,
"expected 1 Function chunk, got {fn_chunks:#?}"
);
assert_eq!(
fn_chunks[0].function_name.as_deref(),
Some("do_something"),
"fn chunk name mismatch"
);
}
#[test]
fn test_rust_pub_static_treated_as_constant() {
let src = r#"
pub static GREETING: &str = "hello";
pub static MAX_RETRIES: u32 = 3;
"#;
let (chunks, _) = chunk_ast("statics.rs", src);
let const_chunks: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Constant)
.collect();
assert_eq!(
const_chunks.len(),
2,
"expected 2 Constant chunks for pub static, got {const_chunks:#?}"
);
let names: Vec<_> = const_chunks
.iter()
.filter_map(|c| c.function_name.as_deref())
.collect();
assert!(names.contains(&"GREETING"), "expected GREETING: {names:?}");
assert!(
names.contains(&"MAX_RETRIES"),
"expected MAX_RETRIES: {names:?}"
);
}
#[test]
fn test_rust_private_const_does_not_get_constant_chunk() {
let src = r#"
const INTERNAL_LIMIT: usize = 100;
const PRIVATE_KEY: &str = "secret";
"#;
let (chunks, _) = chunk_ast("private.rs", src);
let const_chunks: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Constant)
.collect();
assert!(
const_chunks.is_empty(),
"expected no Constant chunks for private consts, got {const_chunks:#?}"
);
}
#[test]
fn test_rust_no_const_regression_on_function_chunks() {
let src = r#"
fn alpha() {}
fn beta() -> i32 { 1 }
fn gamma(x: i32) -> i32 { x + 1 }
"#;
let (chunks, _) = chunk_ast("no_const.rs", src);
let fns: Vec<&RawChunk> = chunks
.iter()
.filter(|c| c.chunk_type == ChunkType::Function)
.collect();
assert_eq!(fns.len(), 3, "expected 3 Function chunks, got {fns:#?}");
assert!(
chunks.iter().all(|c| c.chunk_type != ChunkType::Constant),
"unexpected Constant chunk in function-only file: {chunks:#?}"
);
}
#[test]
fn test_rust_symbol_graph_resolves_caller() {
use crate::core::symbol_graph::SymbolGraph;
let src = "fn alpha() { beta(); }\nfn beta() {}\n";
let (chunks, _) = chunk_ast("a.rs", src);
let tuples: Vec<_> = chunks
.iter()
.map(|c| {
(
c.id.clone(),
c.file.clone(),
c.function_name.clone(),
c.calls.clone(),
c.inherits_from.clone(),
c.chunk_type.clone(),
)
})
.collect();
let g = SymbolGraph::build_from_chunks(&tuples);
assert!(
g.node_count() >= 2,
"expected >= 2 symbol nodes for alpha+beta, got {} (chunks={:#?})",
g.node_count(),
chunks
.iter()
.map(|c| (c.function_name.clone(), c.calls.clone()))
.collect::<Vec<_>>(),
);
let callers = g.callers_of("beta", 1);
assert!(
callers.iter().any(|(s, _)| s == "alpha"),
"expected alpha among callers of beta, got {callers:?}"
);
}