rta-core 0.1.0

Core analyzers and report model for the Rust Technical Audit Toolkit
Documentation
use crate::{analyzers::Analyzer, collector::RepositorySnapshot, model::TestingMaturity};

pub struct TestingAnalyzer;

impl Analyzer<TestingMaturity> for TestingAnalyzer {
    fn analyze(&self, snapshot: &RepositorySnapshot) -> TestingMaturity {
        let mut unit_test_files = 0;
        let mut integration_test_files = 0;
        let mut test_function_count = 0;

        for file in snapshot.rust_files() {
            let content = file.content.as_deref().unwrap_or_default();
            let has_unit_tests = content.contains("#[cfg(test)]") || content.contains("#[test]");
            if has_unit_tests {
                unit_test_files += 1;
                test_function_count += content.matches("#[test]").count();
                test_function_count += content.matches("#[tokio::test]").count();
            }
            if file.relative_path.starts_with("tests/") || file.relative_path.contains("/tests/") {
                integration_test_files += 1;
            }
        }

        let has_tests = unit_test_files > 0 || integration_test_files > 0;
        let testing_structure = if !has_tests {
            "No Rust test structure detected.".to_string()
        } else if integration_test_files > 0 && unit_test_files > 0 {
            "Unit and integration testing structures detected.".to_string()
        } else if integration_test_files > 0 {
            "Integration tests detected; unit test coverage should be reviewed.".to_string()
        } else {
            "Unit tests detected; integration test coverage should be reviewed.".to_string()
        };

        let mut score = if has_tests { 55_i32 } else { 20_i32 };
        score += (unit_test_files as i32 * 6).min(20);
        score += (integration_test_files as i32 * 10).min(20);
        score += (test_function_count as i32).min(15);

        TestingMaturity {
            has_tests,
            unit_test_files,
            integration_test_files,
            test_function_count,
            testing_structure,
            score: score.clamp(0, 100) as u8,
        }
    }
}