ic-host-fs 0.27.2

Local host filesystem reads, durable publication and file locking
Documentation
use super::*;
use crate::test_support::fixture;
use std::fs;

const SUFFIX: &[u8] = b"\n# caller-selected cache\n";

#[test]
fn valid_tags_preserve_signature_only_crlf_and_large_trailers() {
    let fixture = fixture();
    let tag = fixture.path().join("CACHEDIR.TAG");
    for contents in [
        SIGNATURE.to_owned(),
        format!("{SIGNATURE}\r\n# another application\r\n"),
        format!("{SIGNATURE}\n# {}\n", "comment".repeat(100_000)),
    ] {
        fs::write(&tag, &contents).unwrap();
        ensure(fixture.path(), SUFFIX, 0o600).unwrap();
        assert_eq!(fs::read_to_string(&tag).unwrap(), contents);
    }
}

#[test]
fn missing_and_invalid_tags_are_replaced_with_exact_caller_bytes() {
    let fixture = fixture();
    let tag = fixture.path().join("CACHEDIR.TAG");
    for contents in [
        None,
        Some(""),
        Some(&SIGNATURE[..42]),
        Some("Signature: incorrect"),
    ] {
        if let Some(contents) = contents {
            fs::write(&tag, contents).unwrap();
        }
        ensure(fixture.path(), SUFFIX, 0o600).unwrap();
        assert_eq!(
            fs::read(&tag).unwrap(),
            [SIGNATURE.as_bytes(), SUFFIX].concat()
        );
        #[cfg(unix)]
        {
            use std::os::unix::fs::PermissionsExt as _;
            assert_eq!(
                fs::metadata(&tag).unwrap().permissions().mode() & 0o777,
                0o600
            );
        }
    }
}

#[test]
#[cfg(unix)]
fn symlink_tags_are_replaced_without_changing_referents() {
    let fixture = fixture();
    let referent = fixture.path().join("other-application-tag");
    let contents = format!("{SIGNATURE}\n# Preserve this file\n");
    fs::write(&referent, &contents).unwrap();
    let tag = fixture.path().join("CACHEDIR.TAG");
    std::os::unix::fs::symlink(&referent, &tag).unwrap();
    ensure(fixture.path(), SUFFIX, 0o600).unwrap();
    assert!(fs::symlink_metadata(&tag).unwrap().file_type().is_file());
    assert_eq!(fs::read_to_string(&referent).unwrap(), contents);
    assert_eq!(
        fs::read(&tag).unwrap(),
        [SIGNATURE.as_bytes(), SUFFIX].concat()
    );
}

#[test]
fn directory_refusal_retains_the_original_publication_error() {
    let fixture = fixture();
    let tag = fixture.path().join("CACHEDIR.TAG");
    fs::create_dir(&tag).unwrap();
    let error = ensure(fixture.path(), SUFFIX, 0o600).unwrap_err();
    assert!(matches!(
        error,
        NamedWriteError::BeforePublication {
            cleanup_error: None,
            ..
        }
    ));
    assert!(tag.is_dir());
}

#[test]
fn missing_cache_root_uses_durable_parent_creation() {
    let fixture = fixture();
    let root = fixture.path().join("missing/cache");
    ensure(&root, SUFFIX, 0o600).unwrap();
    assert_eq!(
        fs::read(root.join("CACHEDIR.TAG")).unwrap(),
        [SIGNATURE.as_bytes(), SUFFIX].concat()
    );
}