refactor: modularize codebase and add 404 unit tests (#13)
All checks were successful
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Detect Changes (push) Successful in 5s
CI / Tests (push) Successful in 5m15s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Successful in 2s
All checks were successful
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Detect Changes (push) Successful in 5s
CI / Tests (push) Successful in 5m15s
CI / Deploy Agent (push) Successful in 2s
CI / Deploy Dashboard (push) Successful in 2s
CI / Deploy Docs (push) Has been skipped
CI / Deploy MCP (push) Successful in 2s
This commit was merged in pull request #13.
This commit is contained in:
@@ -128,3 +128,186 @@ impl SymbolIndex {
|
||||
Ok(results)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use compliance_core::models::graph::CodeNodeKind;
|
||||
|
||||
fn make_node(
|
||||
qualified_name: &str,
|
||||
name: &str,
|
||||
kind: CodeNodeKind,
|
||||
file_path: &str,
|
||||
language: &str,
|
||||
) -> CodeNode {
|
||||
CodeNode {
|
||||
id: None,
|
||||
repo_id: "test".to_string(),
|
||||
graph_build_id: "build1".to_string(),
|
||||
qualified_name: qualified_name.to_string(),
|
||||
name: name.to_string(),
|
||||
kind,
|
||||
file_path: file_path.to_string(),
|
||||
start_line: 1,
|
||||
end_line: 10,
|
||||
language: language.to_string(),
|
||||
community_id: None,
|
||||
is_entry_point: false,
|
||||
graph_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_creates_index() {
|
||||
let index = SymbolIndex::new();
|
||||
assert!(index.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_empty_nodes() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let result = index.index_nodes(&[]);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_index_and_search_single_node() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![make_node(
|
||||
"src/main.rs::main",
|
||||
"main",
|
||||
CodeNodeKind::Function,
|
||||
"src/main.rs",
|
||||
"rust",
|
||||
)];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("main", 10).unwrap();
|
||||
assert!(!results.is_empty());
|
||||
assert_eq!(results[0].name, "main");
|
||||
assert_eq!(results[0].qualified_name, "src/main.rs::main");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_no_results() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![make_node(
|
||||
"src/main.rs::foo",
|
||||
"foo",
|
||||
CodeNodeKind::Function,
|
||||
"src/main.rs",
|
||||
"rust",
|
||||
)];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("zzzznonexistent", 10).unwrap();
|
||||
assert!(results.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_multiple_nodes() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![
|
||||
make_node(
|
||||
"a.rs::handle_request",
|
||||
"handle_request",
|
||||
CodeNodeKind::Function,
|
||||
"a.rs",
|
||||
"rust",
|
||||
),
|
||||
make_node(
|
||||
"b.rs::handle_response",
|
||||
"handle_response",
|
||||
CodeNodeKind::Function,
|
||||
"b.rs",
|
||||
"rust",
|
||||
),
|
||||
make_node(
|
||||
"c.rs::process_data",
|
||||
"process_data",
|
||||
CodeNodeKind::Function,
|
||||
"c.rs",
|
||||
"rust",
|
||||
),
|
||||
];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("handle", 10).unwrap();
|
||||
assert!(results.len() >= 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_limit() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let mut nodes = Vec::new();
|
||||
for i in 0..20 {
|
||||
nodes.push(make_node(
|
||||
&format!("mod::func_{i}"),
|
||||
&format!("func_{i}"),
|
||||
CodeNodeKind::Function,
|
||||
"mod.rs",
|
||||
"rust",
|
||||
));
|
||||
}
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("func", 5).unwrap();
|
||||
assert!(results.len() <= 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_has_score() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![make_node(
|
||||
"src/lib.rs::compute",
|
||||
"compute",
|
||||
CodeNodeKind::Function,
|
||||
"src/lib.rs",
|
||||
"rust",
|
||||
)];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("compute", 10).unwrap();
|
||||
assert!(!results.is_empty());
|
||||
assert!(results[0].score > 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_result_fields() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![make_node(
|
||||
"src/app.py::MyClass",
|
||||
"MyClass",
|
||||
CodeNodeKind::Class,
|
||||
"src/app.py",
|
||||
"python",
|
||||
)];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
let results = index.search("MyClass", 10).unwrap();
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].name, "MyClass");
|
||||
assert_eq!(results[0].kind, "class");
|
||||
assert_eq!(results[0].file_path, "src/app.py");
|
||||
assert_eq!(results[0].language, "python");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_search_empty_query() {
|
||||
let index = SymbolIndex::new().unwrap();
|
||||
let nodes = vec![make_node(
|
||||
"src/lib.rs::foo",
|
||||
"foo",
|
||||
CodeNodeKind::Function,
|
||||
"src/lib.rs",
|
||||
"rust",
|
||||
)];
|
||||
index.index_nodes(&nodes).unwrap();
|
||||
|
||||
// Empty query may parse error or return empty - both acceptable
|
||||
let result = index.search("", 10);
|
||||
// Just verify it doesn't panic
|
||||
let _ = result;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user