fslite-conformance 0.1.0

A transport-independent, async virtual filesystem with a SQLite-backed persistent backend, HTTP adapter, and CLI.
Documentation
use bytes::Bytes;
use fslite_core::{FsError, RequestContext, VirtualPath, WriteOptions, WriteSource};
use futures::StreamExt;

use crate::ConformanceFactory;

pub(crate) async fn run(factory: &dyn ConformanceFactory) {
    write_and_read_round_trip(factory).await;
    replacement_bumps_revision(factory).await;
    interrupted_write_preserves_prior_content(factory).await;
}

async fn write_and_read_round_trip(factory: &dyn ConformanceFactory) {
    let fs = factory.fresh().await;
    let workspace_id = factory.workspace(&*fs).await;
    let ctx = RequestContext::trusted(workspace_id);
    let path = VirtualPath::parse("/f").unwrap();

    fs.write(
        &ctx,
        &path,
        WriteSource::from_bytes(b"hello".to_vec()),
        Default::default(),
    )
    .await
    .unwrap();

    let read = fs.read(&ctx, &path, Default::default()).await.unwrap();
    let mut stream = read.into_stream();
    let mut collected = Vec::new();
    while let Some(chunk) = stream.next().await {
        collected.extend_from_slice(&chunk.unwrap());
    }
    assert_eq!(collected, b"hello");
}

async fn replacement_bumps_revision(factory: &dyn ConformanceFactory) {
    let fs = factory.fresh().await;
    let workspace_id = factory.workspace(&*fs).await;
    let ctx = RequestContext::trusted(workspace_id);
    let path = VirtualPath::parse("/f").unwrap();

    let first = fs
        .write(
            &ctx,
            &path,
            WriteSource::from_bytes(b"a".to_vec()),
            Default::default(),
        )
        .await
        .unwrap();
    let second = fs
        .write(
            &ctx,
            &path,
            WriteSource::from_bytes(b"bb".to_vec()),
            WriteOptions::replace(),
        )
        .await
        .unwrap();

    assert!(second.revision.get() > first.revision.get());
    assert_eq!(second.logical_size, 2);
}

async fn interrupted_write_preserves_prior_content(factory: &dyn ConformanceFactory) {
    let fs = factory.fresh().await;
    let workspace_id = factory.workspace(&*fs).await;
    let ctx = RequestContext::trusted(workspace_id);
    let path = VirtualPath::parse("/f").unwrap();

    let first = fs
        .write(
            &ctx,
            &path,
            WriteSource::from_bytes(b"old".to_vec()),
            Default::default(),
        )
        .await
        .unwrap();

    let failing = WriteSource::new(futures::stream::iter([
        Ok(Bytes::from_static(b"new")),
        Err(FsError::internal_storage_failure(
            "simulated stream failure",
        )),
    ]));
    assert!(
        fs.write(&ctx, &path, failing, WriteOptions::replace())
            .await
            .is_err()
    );

    let node = fs.stat(&ctx, &path, Default::default()).await.unwrap();
    assert_eq!(node.revision, first.revision);
}