refactor: modularize codebase and add 404 unit tests (#13)
CI / Format (push) Successful in 4s
CI / Clippy (push) Successful in 4m19s
CI / Security Audit (push) Successful in 1m44s
CI / Tests (push) Successful in 5m15s
CI / Detect Changes (push) Successful in 5s
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:
2026-03-13 08:03:45 +00:00
parent acc5b86aa4
commit 3bb690e5bb
89 changed files with 11884 additions and 6046 deletions
+208
View File
@@ -363,6 +363,214 @@ impl RustParser {
}
}
#[cfg(test)]
mod tests {
use super::*;
use compliance_core::traits::graph_builder::LanguageParser;
use std::path::PathBuf;
fn parse_rust(source: &str) -> ParseOutput {
let parser = RustParser::new();
parser
.parse_file(&PathBuf::from("test.rs"), source, "repo1", "build1")
.unwrap()
}
#[test]
fn test_extract_use_path_simple() {
let parser = RustParser::new();
assert_eq!(
parser.extract_use_path("use std::collections::HashMap;"),
Some("std::collections::HashMap".to_string())
);
}
#[test]
fn test_extract_use_path_nested() {
let parser = RustParser::new();
assert_eq!(
parser.extract_use_path("use crate::models::graph::CodeNode;"),
Some("crate::models::graph::CodeNode".to_string())
);
}
#[test]
fn test_extract_use_path_no_prefix() {
let parser = RustParser::new();
assert_eq!(parser.extract_use_path("let x = 5;"), None);
}
#[test]
fn test_extract_use_path_empty() {
let parser = RustParser::new();
assert_eq!(parser.extract_use_path(""), None);
}
#[test]
fn test_parse_function() {
let output = parse_rust("fn hello() {\n let x = 1;\n}\n");
let fn_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Function)
.collect();
assert_eq!(fn_nodes.len(), 1);
assert_eq!(fn_nodes[0].name, "hello");
assert!(fn_nodes[0].qualified_name.contains("hello"));
}
#[test]
fn test_parse_struct() {
let output = parse_rust("struct Foo {\n x: i32,\n}\n");
let struct_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Struct)
.collect();
assert_eq!(struct_nodes.len(), 1);
assert_eq!(struct_nodes[0].name, "Foo");
}
#[test]
fn test_parse_enum() {
let output = parse_rust("enum Color {\n Red,\n Blue,\n}\n");
let enum_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Enum)
.collect();
assert_eq!(enum_nodes.len(), 1);
assert_eq!(enum_nodes[0].name, "Color");
}
#[test]
fn test_parse_trait() {
let output = parse_rust("trait Drawable {\n fn draw(&self);\n}\n");
let trait_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Trait)
.collect();
assert_eq!(trait_nodes.len(), 1);
assert_eq!(trait_nodes[0].name, "Drawable");
}
#[test]
fn test_parse_file_node_always_created() {
let output = parse_rust("");
let file_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::File)
.collect();
assert_eq!(file_nodes.len(), 1);
assert_eq!(file_nodes[0].language, "rust");
}
#[test]
fn test_parse_multiple_functions() {
let source = "fn foo() {}\nfn bar() {}\nfn baz() {}\n";
let output = parse_rust(source);
let fn_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Function)
.collect();
assert_eq!(fn_nodes.len(), 3);
}
#[test]
fn test_parse_main_is_entry_point() {
let output = parse_rust("fn main() {\n println!(\"hi\");\n}\n");
let main_node = output.nodes.iter().find(|n| n.name == "main").unwrap();
assert!(main_node.is_entry_point);
}
#[test]
fn test_parse_pub_fn_is_entry_point() {
let output = parse_rust("pub fn handler() {}\n");
let node = output.nodes.iter().find(|n| n.name == "handler").unwrap();
assert!(node.is_entry_point);
}
#[test]
fn test_parse_private_fn_is_not_entry_point() {
let output = parse_rust("fn helper() {}\n");
let node = output.nodes.iter().find(|n| n.name == "helper").unwrap();
assert!(!node.is_entry_point);
}
#[test]
fn test_parse_function_calls_create_edges() {
let source = "fn caller() {\n callee();\n}\nfn callee() {}\n";
let output = parse_rust(source);
let call_edges: Vec<_> = output
.edges
.iter()
.filter(|e| e.kind == CodeEdgeKind::Calls)
.collect();
assert!(!call_edges.is_empty());
assert!(call_edges.iter().any(|e| e.target.contains("callee")));
}
#[test]
fn test_parse_use_declaration_creates_import_edge() {
let source = "use std::collections::HashMap;\nfn foo() {}\n";
let output = parse_rust(source);
let import_edges: Vec<_> = output
.edges
.iter()
.filter(|e| e.kind == CodeEdgeKind::Imports)
.collect();
assert!(!import_edges.is_empty());
}
#[test]
fn test_parse_impl_methods() {
let source = "struct Foo {}\nimpl Foo {\n fn do_thing(&self) {}\n}\n";
let output = parse_rust(source);
let fn_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Function)
.collect();
assert_eq!(fn_nodes.len(), 1);
assert_eq!(fn_nodes[0].name, "do_thing");
// Method should be qualified under the impl type
assert!(fn_nodes[0].qualified_name.contains("Foo"));
}
#[test]
fn test_parse_mod_item() {
let source = "mod inner {\n fn nested() {}\n}\n";
let output = parse_rust(source);
let mod_nodes: Vec<_> = output
.nodes
.iter()
.filter(|n| n.kind == CodeNodeKind::Module)
.collect();
assert_eq!(mod_nodes.len(), 1);
assert_eq!(mod_nodes[0].name, "inner");
}
#[test]
fn test_parse_line_numbers() {
let source = "fn first() {}\n\n\nfn second() {}\n";
let output = parse_rust(source);
let first = output.nodes.iter().find(|n| n.name == "first").unwrap();
let second = output.nodes.iter().find(|n| n.name == "second").unwrap();
assert_eq!(first.start_line, 1);
assert!(second.start_line > first.start_line);
}
#[test]
fn test_language_and_extensions() {
let parser = RustParser::new();
assert_eq!(parser.language(), "rust");
assert_eq!(parser.extensions(), &["rs"]);
}
}
impl LanguageParser for RustParser {
fn language(&self) -> &str {
"rust"