qubit-fs 0.1.0

Pluggable filesystem abstraction for Rust
Documentation
use std::sync::{
    Arc,
    Mutex,
};

use qubit_fs::{
    AtomicityRequirement,
    CreateDirOptions,
    FileSystem,
    FsPath,
    ManagedTempDir,
    PersistOptions,
    TempDir,
    TempResource,
};

use crate::common::{
    MockFs,
    MockState,
};

#[test]
fn test_managed_temp_dir_cleanup_removes_directory() {
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::default());
    let path = FsPath::parse("/manual-dir.tmp").expect("path should parse");
    fs.create_dir(&path, &CreateDirOptions::default())
        .expect("dir should be created");

    Box::new(ManagedTempDir::new(fs.clone(), path.clone()))
        .cleanup()
        .expect("cleanup should succeed");

    assert!(!fs.exists(&path).expect("dir should be gone"));
}

#[test]
fn test_managed_temp_dir_keep_returns_path_without_cleanup() {
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::default());
    let path = FsPath::parse("/manual-dir.tmp").expect("path should parse");
    fs.create_dir(&path, &CreateDirOptions::default())
        .expect("dir should be created");

    let kept = Box::new(ManagedTempDir::new(fs, path.clone()))
        .keep()
        .expect("keep should succeed");

    assert_eq!(path, kept);
}

#[test]
fn test_managed_temp_dir_persist_renames_when_supported() {
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::default());
    let path = FsPath::parse("/rename-dir.tmp").expect("path should parse");
    fs.create_dir(&path, &CreateDirOptions::default())
        .expect("dir should be created");

    Box::new(ManagedTempDir::new(fs.clone(), path))
        .persist(
            &FsPath::parse("/renamed-dir").expect("path should parse"),
            &PersistOptions::default(),
        )
        .expect("persist should rename");

    assert!(
        fs.exists(&FsPath::parse("/renamed-dir").expect("path should parse"))
            .expect("target should exist"),
    );
}

#[test]
fn test_managed_temp_dir_persist_copy_deletes_when_rename_unsupported_and_allowed() {
    let state = Arc::new(Mutex::new(MockState::default()));
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::with_state(state.clone()));
    state.lock().expect("state lock should succeed").fail_rename_unsupported = true;
    let path = FsPath::parse("/copy-dir.tmp").expect("path should parse");
    fs.create_dir(&path, &CreateDirOptions::default())
        .expect("dir should be created");

    Box::new(ManagedTempDir::new(fs, path))
        .persist(
            &FsPath::parse("/copy-dir").expect("path should parse"),
            &PersistOptions {
                allow_copy_delete: true,
                atomic: AtomicityRequirement::BestEffort,
                ..PersistOptions::default()
            },
        )
        .expect("persist should copy-delete");
}

#[test]
fn test_managed_temp_dir_drop_runs_best_effort_cleanup() {
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::default());
    let path = FsPath::parse("/drop-dir.tmp").expect("path should parse");
    fs.create_dir(&path, &CreateDirOptions::default())
        .expect("dir should be created");

    drop(ManagedTempDir::new(fs.clone(), path.clone()));

    assert!(!fs.exists(&path).expect("drop cleanup should run"));
}

#[test]
fn test_managed_temp_dir_cleanup_and_persist_return_errors() {
    let state = Arc::new(Mutex::new(MockState::default()));
    let fs: Arc<dyn FileSystem> = Arc::new(MockFs::with_state(state.clone()));
    let cleanup_path = FsPath::parse("/cleanup-dir-error.tmp").expect("path should parse");
    fs.create_dir(&cleanup_path, &CreateDirOptions::default())
        .expect("dir should be created");
    state.lock().expect("state lock should succeed").fail_delete = true;
    assert!(
        Box::new(ManagedTempDir::new(fs.clone(), cleanup_path))
            .cleanup()
            .is_err(),
    );
    state.lock().expect("state lock should succeed").fail_delete = false;

    state.lock().expect("state lock should succeed").fail_rename_unsupported = true;
    let unsupported_path = FsPath::parse("/unsupported-dir.tmp").expect("path should parse");
    fs.create_dir(&unsupported_path, &CreateDirOptions::default())
        .expect("dir should be created");
    assert!(
        Box::new(ManagedTempDir::new(fs.clone(), unsupported_path))
            .persist(
                &FsPath::parse("/unsupported-dir").expect("path should parse"),
                &PersistOptions::default(),
            )
            .is_err(),
    );
}