rebecca 0.2.0

Windows-first cleanup CLI and Rust library surface for Rebecca cleanup planning, rules, and platform adapters.
Documentation
use std::fs;

mod common;
#[path = "common/isolated.rs"]
mod isolated;

#[test]
fn cache_purge_json_defaults_to_preview_without_deleting() {
    let temp = tempfile::tempdir().unwrap();
    let cache_dir = temp.path().join("rebecca-cache");
    fs::create_dir_all(cache_dir.join("nested")).unwrap();
    fs::write(cache_dir.join("cache.bin"), b"abc").unwrap();
    fs::write(cache_dir.join("nested").join("nested.bin"), b"de").unwrap();

    let output = isolated::isolated_rebecca(&temp)
        .args(["cache", "purge", "--format", "json"])
        .output()
        .unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );
    assert!(cache_dir.join("cache.bin").exists());

    let value: serde_json::Value = common::support::api_data(&output.stdout);
    assert_eq!(value["mode"], "dry-run");
    assert_eq!(value["deleted"], false);
    assert_eq!(value["cache_dir_lifecycle"], "rebuildable-cache");
    assert_eq!(value["cache_dir_retention"], "rebuildable");
    assert_eq!(value["cache_dir_exists"], true);
    assert_eq!(value["preserves_cache_dir"], true);
    assert_eq!(value["summary"]["total_entries"], 2);
    assert_eq!(value["summary"]["would_delete_entries"], 2);
    assert_eq!(value["summary"]["deleted_entries"], 0);
    assert_eq!(value["summary"]["estimated_bytes"], 5);
    assert_eq!(value["summary"]["reclaimed_bytes"], 0);
    assert!(
        value["summary"]["issue_matrix"]
            .as_array()
            .unwrap()
            .is_empty()
    );
}

#[test]
fn cache_purge_human_output_reports_scope_and_status_counts() {
    let temp = tempfile::tempdir().unwrap();
    let cache_dir = temp.path().join("rebecca-cache");
    fs::create_dir_all(cache_dir.join("nested")).unwrap();
    fs::write(cache_dir.join("cache.bin"), b"abc").unwrap();
    fs::write(cache_dir.join("nested").join("nested.bin"), b"de").unwrap();

    let output = isolated::isolated_rebecca(&temp)
        .args(["cache", "purge"])
        .output()
        .unwrap();

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

    let stdout = String::from_utf8_lossy(&output.stdout);
    assert!(stdout.contains("Lifecycle: rebuildable cache (rebuildable)"));
    assert!(stdout.contains("Cache directory exists: yes"));
    assert!(stdout.contains("Preserves cache directory: yes"));
    assert!(stdout.contains("Entry status: 2 would delete, 0 deleted, 0 skipped, 0 failed"));
    assert!(stdout.contains("Run with --yes to purge these rebuildable cache entries."));
}

#[test]
fn cache_purge_yes_deletes_direct_contents_but_keeps_cache_dir() {
    let temp = tempfile::tempdir().unwrap();
    let cache_dir = temp.path().join("rebecca-cache");
    fs::create_dir_all(cache_dir.join("nested")).unwrap();
    fs::write(cache_dir.join("cache.bin"), b"abc").unwrap();
    fs::write(cache_dir.join("nested").join("nested.bin"), b"de").unwrap();

    let output = isolated::isolated_rebecca(&temp)
        .args(["cache", "purge", "--yes", "--format", "json"])
        .output()
        .unwrap();

    assert!(
        output.status.success(),
        "stderr: {}",
        common::support::stderr(&output)
    );
    assert!(cache_dir.exists());
    assert_eq!(fs::read_dir(&cache_dir).unwrap().count(), 0);

    let value: serde_json::Value = common::support::api_data(&output.stdout);
    assert_eq!(value["mode"], "delete");
    assert_eq!(value["deleted"], true);
    assert_eq!(value["cache_dir_exists"], true);
    assert_eq!(value["preserves_cache_dir"], true);
    assert_eq!(value["summary"]["deleted_entries"], 2);
    assert_eq!(value["summary"]["reclaimed_bytes"], 5);
    assert!(
        value["summary"]["issue_matrix"]
            .as_array()
            .unwrap()
            .is_empty()
    );
}

#[test]
fn cache_purge_rejects_overlap_with_state_dir() {
    let temp = tempfile::tempdir().unwrap();
    let state_dir = temp.path().join("rebecca-state");
    fs::create_dir_all(&state_dir).unwrap();

    let output = isolated::isolated_rebecca(&temp)
        .env("REBECCA_CACHE_DIR", &state_dir)
        .args(["cache", "purge", "--format", "json"])
        .output()
        .unwrap();

    assert!(!output.status.success());

    let stderr = common::support::stderr(&output);
    assert!(stderr.contains("overlaps preserved"));
    assert!(stderr.contains("State dir"));
}