mini-static 0.17.0

A secure, async static file server with streaming, traversal protection, and connection limits.
Documentation
use std::fs;
use std::time::Duration;

use mini_static::{CssOptions, CssTool, JsOptions, JsTool, Server};
use tempfile::TempDir;

// These integration tests exercise `SourcePipeline` only through configurations that
// never actually spawn the configured CSS/JS tool (bundle=false, minify=false is a
// pure passthrough copy, and bundle mode with no CSS sources present short-circuits
// before invoking anything) — so they pass whether or not `lightningcss`/`esbuild` are
// installed on the test runner's PATH. Coverage of real bundling/minification output
// lives in `css.rs`/`js.rs`'s own unit tests (via a test-only echo tool) and in
// `tests/real_tools.rs` (gated behind `--ignored`, requires the real binaries).

#[tokio::test]
async fn css_source_folder_passthrough_mirrors_into_output() {
    let src = TempDir::new().unwrap();
    let out = TempDir::new().unwrap();
    let source_css = "body { color: red; }\n";
    fs::write(src.path().join("app.css"), source_css).unwrap();

    let server = Server::new(out.path())
        .unwrap()
        .with_source_folder(src.path())
        .unwrap()
        .with_css_tool(CssTool::LightningCss, CssOptions::new());
    let (_port, handle) = server.run_ephemeral().await.unwrap();

    tokio::time::sleep(Duration::from_millis(200)).await;

    let out_css = fs::read_to_string(out.path().join("app.css")).unwrap();
    assert_eq!(
        out_css, source_css,
        "with bundle=false and minify=false, CSS must mirror through unchanged"
    );

    handle.shutdown().await;
}

#[tokio::test]
async fn js_source_folder_passthrough_mirrors_into_output() {
    let src = TempDir::new().unwrap();
    let out = TempDir::new().unwrap();
    let source_js = "const greeting = 'hello';\n";
    fs::write(src.path().join("app.js"), source_js).unwrap();

    let server = Server::new(out.path())
        .unwrap()
        .with_source_folder(src.path())
        .unwrap()
        .with_js_tool(JsTool::Esbuild, JsOptions::new())
        .unwrap();
    let (_port, handle) = server.run_ephemeral().await.unwrap();

    tokio::time::sleep(Duration::from_millis(200)).await;

    let out_js = fs::read_to_string(out.path().join("app.js")).unwrap();
    assert_eq!(
        out_js, source_js,
        "with bundle=false and minify=false, JS must mirror through unchanged"
    );

    handle.shutdown().await;
}

#[tokio::test]
async fn js_source_change_rebuilds_the_changed_file() {
    let src = TempDir::new().unwrap();
    let out = TempDir::new().unwrap();
    fs::write(src.path().join("app.js"), "const value = 1;\n").unwrap();

    let server = Server::new(out.path())
        .unwrap()
        .with_live_reload()
        .with_source_folder(src.path())
        .unwrap()
        .with_js_tool(JsTool::Esbuild, JsOptions::new())
        .unwrap();
    let (_port, handle) = server.run_ephemeral().await.unwrap();

    // Let the watcher's first 500ms poll pass so the write below is seen as a change.
    tokio::time::sleep(Duration::from_millis(800)).await;
    fs::write(src.path().join("app.js"), "const value = 2;\n").unwrap();
    tokio::time::sleep(Duration::from_millis(1500)).await;

    let out_js = fs::read_to_string(out.path().join("app.js")).unwrap();
    assert!(
        out_js.contains("value = 2"),
        "expected the changed source to be re-mirrored into the output, got: {out_js}"
    );

    handle.shutdown().await;
}

// `malformed_js_source_degrades_to_raw_output` and the CSS bundle prune tests aren't
// covered here: `minify(true)`/`bundle(true)` make the tool required, so
// `Server::run_on`'s startup PATH-probe now fails fast if `esbuild`/`lightningcss`
// aren't installed, before the pipeline ever runs — this environment can't reach
// those code paths through the public `Server` API without the real binaries. That
// behavior is covered unconditionally (via a test-only tool) by
// `css::tests`/`js::tests`/`source::tests` in the crate's own unit test suite, and
// end-to-end with the real binaries in `tests/real_tools.rs` (`--ignored`).