dupe-core 0.1.0

Cross-language duplicate code detection library using Tree-sitter and Rabin-Karp
Documentation
//! Snapshot testing for dupe-core output formats
//!
//! Uses the `insta` crate to capture and compare output snapshots.
//! If output format changes, tests will fail until snapshots are reviewed and updated.

#[cfg(test)]
mod tests {
    use crate::{Scanner, DuplicateMatch};
    use insta::{assert_json_snapshot, assert_debug_snapshot};
    use std::path::PathBuf;

    #[test]
    fn snapshot_duplicate_match_json() {
        let dup = DuplicateMatch {
            file1: "src/main.rs".to_string(),
            file2: "src/lib.rs".to_string(),
            start_line1: 10,
            start_line2: 25,
            length: 50,
            similarity: 0.95,
            hash: 0x123456789ABCDEF,
        };

        assert_json_snapshot!(dup, @r###"
        {
          "file1": "src/main.rs",
          "file2": "src/lib.rs",
          "start_line1": 10,
          "start_line2": 25,
          "length": 50,
          "similarity": 0.95,
          "hash": 81985529216486895
        }
        "###);
    }

    #[test]
    fn snapshot_empty_scan_result() {
        let scanner = Scanner::new().unwrap();
        let paths = vec![PathBuf::from("nonexistent")];
        
        let result = scanner.scan(paths);
        
        // Should succeed with 0 files scanned
        assert!(result.is_ok());
        let report = result.unwrap();
        
        assert_debug_snapshot!(report, @"
        Report {
            files_scanned: 0,
            functions_analyzed: 0,
            duplicates: [],
            stats: ScanStats {
                total_lines: 0,
                total_tokens: 0,
                unique_hashes: 0,
                duration_ms: 0,
            },
        }
        ");
    }

    #[test]
    fn snapshot_test_duplicates_report() {
        use std::path::Path;
        
        // Only run if test_duplicates exists
        if !Path::new("test_duplicates").exists() {
            return;
        }
        
        let scanner = Scanner::with_config(3, 0.70).unwrap();
        let paths = vec![PathBuf::from("test_duplicates")];
        
        let result = scanner.scan(paths);
        assert!(result.is_ok());
        
        let report = result.unwrap();
        
        // Snapshot the structure (not exact values which may vary)
        assert!(report.files_scanned >= 1, "Should scan at least 1 file");
        assert!(report.functions_analyzed >= 1, "Should analyze at least 1 function");
        
        // Snapshot a sample duplicate if found
        if let Some(dup) = report.duplicates.first() {
            assert_json_snapshot!("sample_duplicate", dup, {
                ".hash" => "[hash]",  // Hash values are dynamic
            });
        }
    }

    #[test]
    fn snapshot_scanner_configuration() {
        let scanner = Scanner::with_config(100, 0.95);
        
        // Scanner doesn't implement Debug, so just verify it was created
        assert!(scanner.is_ok());
    }

    #[test]
    fn snapshot_multiple_duplicates() {
        let duplicates = vec![
            DuplicateMatch {
                file1: "a.rs".to_string(),
                file2: "b.rs".to_string(),
                start_line1: 1,
                start_line2: 1,
                length: 10,
                similarity: 1.0,
                hash: 0xAAA,
            },
            DuplicateMatch {
                file1: "a.rs".to_string(),
                file2: "c.rs".to_string(),
                start_line1: 5,
                start_line2: 10,
                length: 15,
                similarity: 0.92,
                hash: 0xBBB,
            },
        ];

        assert_json_snapshot!(duplicates, {
            "[].hash" => "[hash]",  // Redact dynamic hashes
        });
    }
}