haematite-cli 0.21.7

The haematite command: serve a store, its door and Studio, answer MCP, query with jq, and host the store's assistant seat
Documentation
use std::io::Read;

use flate2::read::GzDecoder;

use super::{Packed, gzip, hashed, takes_gzip};

type TestResult = Result<(), Box<dyn std::error::Error>>;

/// Fails while a file that can change under its name is kept for good, or a
/// content-hashed asset is not: only `/assets/` names ending `-` and the
/// eight-character hash before their extension are hashed.
#[test]
fn only_a_content_hashed_asset_is_hashed() {
    for path in [
        "/assets/index-BdF3x9kZ.js",
        "/assets/graphmother.esm-Ab_d-234.js",
        "/assets/haematite_query_bg-0aZ9_-xY.wasm",
        "/assets/index-C0ffee12.css",
    ] {
        assert!(hashed(path), "{path} was not taken as hashed");
    }
    for path in [
        "/index.html",
        "/app-BdF3x9kZ.js",
        "/assets/logo.svg",
        "/assets/index-short.js",
        "/assets/-BdF3x9kZ.js",
        "/assets/index-BdF3x9k!.js",
        "/assets/index-BdF3x9kZ",
    ] {
        assert!(!hashed(path), "{path} was taken as hashed");
    }
}

/// Fails while gzip is sent to a browser that did not ask for it, or refused
/// one that did: named, or `*`, with a quality above zero.
#[test]
fn gzip_is_taken_only_when_the_browser_asks_for_it() {
    for accept in [
        "gzip",
        "gzip, deflate, br",
        "br;q=1.0, GZIP;q=0.5",
        "x-gzip",
        "*",
    ] {
        assert!(takes_gzip(Some(accept)), "{accept} was refused gzip");
    }
    for accept in [
        None,
        Some(""),
        Some("br"),
        Some("identity"),
        Some("gzip;q=0"),
        Some("*, gzip;q=0.000"),
    ] {
        assert!(!takes_gzip(accept), "{accept:?} was given gzip");
    }
}

/// The gzipped bytes decode back to the file; a hashed file is answered from
/// memory once compressed, and any other is read again.
#[test]
fn a_hashed_file_is_compressed_once_and_another_read_again() -> TestResult {
    let files = tempfile::tempdir()?;
    let path = files.path().join("index-BdF3x9kZ.js");
    let text = "export const studio = 1;\n".repeat(64);
    std::fs::write(&path, &text)?;
    let packed = Packed::default();
    let first = packed.gzipped(&path, true)?;
    let mut decoded = String::new();
    GzDecoder::new(&first[..]).read_to_string(&mut decoded)?;
    assert_eq!(decoded, text);
    assert_eq!(&first[..], gzip(text.as_bytes())?.as_slice());
    std::fs::remove_file(&path)?;
    assert_eq!(
        packed.gzipped(&path, true)?,
        first,
        "a hashed file was read again"
    );
    assert!(
        packed.gzipped(&path, false).is_err(),
        "a file that can change was answered from memory"
    );
    Ok(())
}