liteboxfs 0.1.0

A modern POSIX filesystem in a SQLite database
Documentation
#![cfg(all(feature = "fs", target_os = "linux"))]

use std::time::SystemTime;

use xpct::{be_ge, be_le, be_ok, be_some, equal, expect, match_pattern, pattern};

use crate::{
    Connection, Error,
    file_metadata::{FileKind, FileMode, Gid, Owner, Uid},
    settings::Settings,
};

fn open() -> crate::Result<Connection> {
    Connection::open_for_testing(&Settings::default())
}

#[test]
fn creates_a_regular_file() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let file = fs.create_temp(Owner::ROOT)?;
        expect!(file.kind()).to(equal(&FileKind::Regular));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn has_no_path() -> crate::Result<()> {
    let mut conn = open()?;

    conn.exec(|fs| {
        let mut file = fs.create_temp(Owner::ROOT)?;
        expect!(file.link_count()).to(be_ok()).to(equal(0u32));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn owner_is_set() -> crate::Result<()> {
    let mut conn = open()?;

    let owner = Owner {
        user: Uid::from(1000),
        group: Gid::from(1001),
    };

    conn.exec(|fs| {
        let mut file = fs.create_temp(owner)?;
        let metadata = file.metadata()?;
        expect!(metadata.user()).to(equal(Uid::from(1000)));
        expect!(metadata.group()).to(equal(Gid::from(1001)));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn mode_respects_default_umask() -> crate::Result<()> {
    let mut conn = open()?;

    // Default umask is OTHER_W (0o002); regular file default is 0o666.
    // 0o666 & !0o002 = 0o664.
    let expected = FileMode::OWNER_R
        | FileMode::OWNER_W
        | FileMode::GROUP_R
        | FileMode::GROUP_W
        | FileMode::OTHER_R;

    conn.exec(|fs| {
        let mut file = fs.create_temp(Owner::ROOT)?;
        let metadata = file.metadata()?;
        expect!(metadata.mode()).to(equal(expected));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn mode_respects_custom_umask() -> crate::Result<()> {
    let mut conn = open()?;

    // Umask 0o022 gives 0o666 & !0o022 = 0o644.
    let expected = FileMode::OWNER_R | FileMode::OWNER_W | FileMode::GROUP_R | FileMode::OTHER_R;

    conn.exec(|fs| {
        fs.set_umask(FileMode::GROUP_W | FileMode::OTHER_W);
        let mut file = fs.create_temp(Owner::ROOT)?;
        let metadata = file.metadata()?;
        expect!(metadata.mode()).to(equal(expected));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn timestamps_are_set_to_now() -> crate::Result<()> {
    let mut conn = open()?;

    let before = SystemTime::now();

    conn.exec(|fs| {
        let mut file = fs.create_temp(Owner::ROOT)?;
        let metadata = file.metadata()?;

        let after = SystemTime::now();

        expect!(metadata.accessed()).to(be_ge(before));
        expect!(metadata.accessed()).to(be_le(after));
        expect!(metadata.modified()).to(be_ge(before));
        expect!(metadata.modified()).to(be_le(after));
        expect!(metadata.changed()).to(be_ge(before));
        expect!(metadata.changed()).to(be_le(after));
        expect!(metadata.created()).to(be_some()).to(be_ge(before));
        expect!(metadata.created()).to(be_some()).to(be_le(after));

        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn deleted_when_dropped() -> crate::Result<()> {
    let mut conn = open()?;

    let file_id = conn.exec(|fs| {
        let file = fs.create_temp(Owner::ROOT)?;
        crate::Result::Ok(file.file_id())
    })?;

    conn.exec(|fs| {
        expect!(fs.open(file_id)).to(match_pattern(pattern!(Err(Error::FileNotFound { .. }))));
        crate::Result::Ok(())
    })?;

    Ok(())
}

#[test]
fn multiple_calls_return_distinct_file_ids() -> crate::Result<()> {
    let mut conn = open()?;

    let file_id_1 = conn.exec(|fs| crate::Result::Ok(fs.create_temp(Owner::ROOT)?.file_id()))?;
    let file_id_2 = conn.exec(|fs| crate::Result::Ok(fs.create_temp(Owner::ROOT)?.file_id()))?;

    expect!(file_id_1).to_not(equal(file_id_2));

    Ok(())
}