repopilot 0.9.0

Local-first CLI for repository audit, architecture risk detection, baseline tracking, and CI-friendly code review.
Documentation
use repopilot::scan::config::ScanConfig;
use repopilot::scan::scanner::{collect_scan_facts, scan_path_with_config};
use std::fs;
use tempfile::tempdir;

/// Verifies that the parallel scan pipeline produces the same counts as the
/// sequential collect path when run on the same directory.
#[test]
fn parallel_scan_matches_sequential_file_counts() {
    let temp = tempdir().unwrap();

    for i in 0..20 {
        fs::write(
            temp.path().join(format!("file_{i}.rs")),
            format!("fn func_{i}() {{}}\n"),
        )
        .unwrap();
    }
    fs::write(temp.path().join("script.ts"), "export const x = 1;\n").unwrap();

    let sequential = collect_scan_facts(temp.path()).unwrap();
    let parallel = scan_path_with_config(temp.path(), &ScanConfig::default()).unwrap();

    assert_eq!(
        sequential.files_count, parallel.files_count,
        "file count must match between sequential and parallel paths"
    );
    assert_eq!(
        sequential.lines_of_code, parallel.lines_of_code,
        "LOC count must match"
    );
    assert_eq!(
        sequential.languages.len(),
        parallel.languages.len(),
        "detected language count must match"
    );
}

/// Verifies that scan_duration_us is recorded. Even a trivial scan takes at least
/// a few microseconds, so the field must be non-zero.
#[test]
fn scan_duration_is_recorded() {
    let temp = tempdir().unwrap();
    // Write enough files that the scan takes a measurable number of microseconds.
    for i in 0..10 {
        fs::write(
            temp.path().join(format!("file_{i}.rs")),
            format!("fn func_{i}() {{}}\n"),
        )
        .unwrap();
    }

    let summary = scan_path_with_config(temp.path(), &ScanConfig::default()).unwrap();

    assert!(
        summary.scan_duration_us > 0,
        "scan_duration_us should be non-zero"
    );
}

/// Verifies that parallel scan handles an empty directory cleanly without panicking.
#[test]
fn parallel_scan_empty_directory() {
    let temp = tempdir().unwrap();
    let summary = scan_path_with_config(temp.path(), &ScanConfig::default()).unwrap();

    assert_eq!(summary.files_count, 0);
    assert_eq!(summary.lines_of_code, 0);
    // Project-level audits (e.g. missing-test-folder) may still produce findings
    // for an empty directory — that is expected behaviour.
}