diff --git a/src/commands/index.rs b/src/commands/index.rs index 16cd4142..f2ece876 100644 --- a/src/commands/index.rs +++ b/src/commands/index.rs @@ -129,3 +129,30 @@ pub fn build_index( Ok(()) } + +#[test] +fn build_index_creates_db_and_registers_files() { + let mut cfg = Config::default(); + cfg.performance.worker_threads = Some(1); + cfg.performance.channel_multiplier = 1; + cfg.performance.batch_size = 2; + + let td = tempfile::tempdir().unwrap(); + let project_dir = td.path().join("proj"); + fs::create_dir(&project_dir).unwrap(); + let f_txt = project_dir.join("readme.txt"); + fs::write(&f_txt, "hello").unwrap(); + + let db_path = td.path().join("proj.sqlite"); + + build_index("proj", &project_dir, &db_path, &cfg).expect("index build should succeed"); + + // ── Assert ──────────────────────────────────────────────────────────────── + assert!(db_path.is_file(), "SQLite file must exist"); + + let pool = Indexer::init(&db_path).unwrap(); + let idx = Indexer::from_pool("proj", &pool).unwrap(); + let files = idx.get_files("proj").unwrap(); + assert_eq!(files.len(), 1, "exactly one file indexed"); + assert_eq!(files[0], f_txt); +} diff --git a/src/commands/scan.rs b/src/commands/scan.rs index 6db5db73..00b46d90 100644 --- a/src/commands/scan.rs +++ b/src/commands/scan.rs @@ -165,3 +165,35 @@ pub fn scan_with_index_parallel( Ok(diags) } + +#[test] +fn scan_with_index_parallel_uses_existing_index_without_rescanning() { + let mut cfg = Config::default(); + cfg.performance.worker_threads = Some(1); + cfg.performance.channel_multiplier = 1; + cfg.performance.batch_size = 2; + + let td = tempfile::tempdir().unwrap(); + let project_dir = td.path().join("proj"); + std::fs::create_dir(&project_dir).unwrap(); + std::fs::write(project_dir.join("foo.txt"), "abc").unwrap(); + + let (project_name, db_path) = get_project_info(&project_dir, td.path()).unwrap(); + crate::commands::index::build_index(&project_name, &project_dir, &db_path, &cfg).unwrap(); + + let pool = Indexer::init(&db_path).unwrap(); + + assert_eq!( + Indexer::from_pool(&project_name, &pool) + .unwrap() + .get_files(&project_name) + .unwrap() + .len(), + 1 + ); + + let diags = scan_with_index_parallel(&project_name, Arc::clone(&pool), &cfg) + .expect("scan should succeed"); + + assert!(diags.is_empty()); +} diff --git a/src/patterns/mod.rs b/src/patterns/mod.rs index 55ce40bc..c429eed6 100644 --- a/src/patterns/mod.rs +++ b/src/patterns/mod.rs @@ -60,6 +60,7 @@ impl FromStr for Severity { /// One AST pattern with a tree-sitter query and meta-data. #[derive(Debug, Clone, Serialize)] +#[derive(PartialEq)] pub struct Pattern { /// Unique identifier (snake-case preferred). pub id: &'static str, @@ -113,3 +114,35 @@ pub fn load(lang: &str) -> Vec { let key = lang.to_ascii_lowercase(); REGISTRY.get(key.as_str()).copied().unwrap_or(&[]).to_vec() } + +#[test] +fn severity_as_db_str_roundtrip() { + for &s in &[Severity::High, Severity::Medium, Severity::Low] { + let db = s.as_db_str(); + assert!(matches!(db, "HIGH" | "MEDIUM" | "LOW")); + + assert_eq!(db.parse::().unwrap(), s); + assert_eq!(db.to_lowercase().parse::().unwrap(), s); + } +} + +#[test] +fn severity_display_contains_uppercase_name() { + assert!(Severity::High.to_string().contains("HIGH")); + assert!(Severity::Medium.to_string().contains("MEDIUM")); + assert!(Severity::Low.to_string().contains("LOW")); +} + +#[test] +fn load_returns_correct_pattern_slices() { + let rust = load("rust"); + assert!(!rust.is_empty(), "Rust patterns should be loaded"); + + let ts = load("typescript"); + let tsx = load("tsx"); + assert_eq!(ts, tsx, "alias ‘tsx’ must map to TypeScript patterns"); + + assert_eq!(load("RUST"), rust); + + assert!(load("brainfuck").is_empty()); +}