code-dupes 0.2.1

A multi-language CLI tool that detects duplicate and near-duplicate code blocks using AST normalization
mod common;

use common::{code_dupes, fixture_path};
use predicates::prelude::*;

#[test]
fn sub_function_detects_duplicate_branches() {
    code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--sub-function",
            "report",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("Sub-function Exact Duplicates"))
        .stdout(predicate::str::contains("if-then branch"))
        .stdout(predicate::str::contains("match arm"))
        .stdout(predicate::str::contains("for body"));
}

#[test]
fn sub_function_shows_parent_names() {
    code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--sub-function",
            "report",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("in handle_positive"))
        .stdout(predicate::str::contains("in process_value"))
        .stdout(predicate::str::contains("in classify_number"))
        .stdout(predicate::str::contains("in describe_value"));
}

#[test]
fn sub_function_stats_shown() {
    code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--sub-function",
            "stats",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("Sub-function exact: 3 groups"));
}

#[test]
fn sub_function_json_stats() {
    let output = code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--sub-function",
            "--format",
            "json",
            "stats",
        ])
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();
    let text = String::from_utf8(output).unwrap();
    let parsed: serde_json::Value = serde_json::from_str(&text).unwrap();
    assert_eq!(parsed["sub_exact_groups"].as_u64().unwrap(), 3);
    assert_eq!(parsed["sub_exact_units"].as_u64().unwrap(), 6);
}

#[test]
fn without_sub_function_flag_no_sub_sections() {
    code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "report",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("Exact Duplicates"))
        .stdout(predicate::str::contains("Sub-function").not());
}

#[test]
fn without_sub_function_json_no_sub_fields() {
    let output = code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--format",
            "json",
            "stats",
        ])
        .assert()
        .success()
        .get_output()
        .stdout
        .clone();
    let text = String::from_utf8(output).unwrap();
    let parsed: serde_json::Value = serde_json::from_str(&text).unwrap();
    assert!(parsed.get("sub_exact_groups").is_none());
    assert!(parsed.get("sub_near_groups").is_none());
}

#[test]
fn sub_function_min_sub_nodes_filters() {
    code_dupes()
        .args([
            "--path",
            fixture_path("sub_function_dupes").to_str().unwrap(),
            "--sub-function",
            "--min-sub-nodes",
            "1000",
            "stats",
        ])
        .assert()
        .success()
        .stdout(predicate::str::contains("Sub-function").not());
}