rebecca-cli 0.1.0

Windows-first cleanup CLI for caches, app leftovers, and project artifacts.
mod common;

#[test]
fn scan_json_lists_builtin_rules() {
    let output = common::command::rebecca()
        .args(["scan", "--format", "json"])
        .output()
        .unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );
    let value: serde_json::Value = common::support::api_data(&output.stdout);
    let rules = value.as_array().expect("scan output should be an array");
    let ids = rules
        .iter()
        .map(|rule| rule["id"].as_str().expect("rule id should be a string"))
        .collect::<std::collections::BTreeSet<_>>();

    assert_eq!(
        ids,
        common::steam::BUILTIN_RULE_IDS.iter().copied().collect()
    );

    let steam_cache = rules
        .iter()
        .find(|rule| rule["id"] == "windows.steam-cache")
        .expect("steam cache rule should exist");
    assert_eq!(steam_cache["provenance"]["source"], "owned");
    assert_eq!(steam_cache["provenance"]["license"], "project-owned");
}

#[test]
fn scan_json_filters_by_category_and_rule() {
    let output = common::command::rebecca()
        .args([
            "scan",
            "--format",
            "json",
            "--category",
            "browser",
            "--rule",
            "windows.firefox-profile-cache",
        ])
        .output()
        .unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );

    let value: serde_json::Value = common::support::api_data(&output.stdout);
    let rules = value.as_array().expect("scan output should be an array");

    assert_eq!(rules.len(), 1);
    assert_eq!(rules[0]["id"], "windows.firefox-profile-cache");
    assert_eq!(rules[0]["category"], "browser");
}

#[test]
fn scan_human_output_groups_rules_by_category() {
    let output = common::command::rebecca().args(["scan"]).output().unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );

    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("- browser ("));
    assert!(stdout.contains("  - windows.chrome-cache [safe] Google Chrome cache"));
    assert!(stdout.contains("  - windows.firefox-profile-cache [safe] Firefox profile cache"));
    assert!(stdout.contains("  - windows.postman-cache [safe] Postman cache"));
    assert!(stdout.contains("  - windows.notion-cache [safe] Notion cache"));
    assert!(stdout.contains("  - windows.figma-cache [safe] Figma cache"));
    assert!(stdout.contains("  - windows.slack-cache [safe] Slack cache"));
    assert!(stdout.contains("- development ("));
    assert!(stdout.contains("  - windows.npm-cache [moderate] npm cache"));
    assert!(stdout.contains("  - windows.ccache-cache [moderate] ccache compiler cache"));
    assert!(stdout.contains("  - windows.sccache-cache [moderate] sccache compiler cache"));
    for expected in common::steam::HUMAN_SCAN_LINES {
        assert!(stdout.contains(expected), "{expected}");
    }
}

#[test]
fn scan_human_output_filters_by_category() {
    let output = common::command::rebecca()
        .args(["scan", "--category", "browser"])
        .output()
        .unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );

    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("Rebecca rules: "));
    assert!(stdout.contains("- browser ("));
    assert!(!stdout.contains("- development ("));
}

#[test]
fn scan_unknown_category_returns_clear_error() {
    let output = common::command::rebecca()
        .args(["scan", "--category", "missing"])
        .output()
        .unwrap();

    assert!(!output.status.success());
    assert!(common::support::stderr(&output).contains("invalid category"));
}