veer 0.1.2

Inertia.js v3 server-side protocol superset for Rust
Documentation
#![cfg(feature = "embed")]

use axum::{body::Body, Router};
use http::Request;
use http_body_util::BodyExt;
use std::borrow::Cow;
use tower::ServiceExt;
use veer::EmbeddedAssets;

fn assets() -> EmbeddedAssets {
    EmbeddedAssets::new(|p: &str| match p {
        "assets/app-AAA.js" => Some(Cow::Borrowed(b"console.log(1)" as &[u8])),
        _ => None,
    })
}

fn app() -> Router {
    Router::new().nest_service("/build", assets())
}

#[tokio::test]
async fn serves_known_asset_with_type_and_cache() {
    let resp = app()
        .oneshot(
            Request::builder()
                .uri("/build/assets/app-AAA.js")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(resp.status(), 200);
    assert_eq!(
        resp.headers().get("content-type").unwrap(),
        "text/javascript"
    );
    assert_eq!(
        resp.headers().get("cache-control").unwrap(),
        "public, max-age=31536000, immutable"
    );
    let body = resp.into_body().collect().await.unwrap().to_bytes();
    assert_eq!(&body[..], b"console.log(1)");
}

#[tokio::test]
async fn unknown_asset_is_404() {
    let resp = app()
        .oneshot(
            Request::builder()
                .uri("/build/nope.js")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(resp.status(), 404);
}

#[tokio::test]
async fn head_has_length_but_empty_body() {
    let resp = app()
        .oneshot(
            Request::builder()
                .method("HEAD")
                .uri("/build/assets/app-AAA.js")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(resp.status(), 200);
    assert_eq!(resp.headers().get("content-length").unwrap(), "14");
    let body = resp.into_body().collect().await.unwrap().to_bytes();
    assert!(body.is_empty());
}

#[tokio::test]
async fn mime_override_takes_effect() {
    let a =
        EmbeddedAssets::new(|p: &str| (p == "x.custom").then_some(Cow::Borrowed(b"hi" as &[u8])))
            .mime("custom", "application/x-custom");
    let app = Router::new().nest_service("/build", a);
    let resp = app
        .oneshot(
            Request::builder()
                .uri("/build/x.custom")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(
        resp.headers().get("content-type").unwrap(),
        "application/x-custom"
    );
}

#[tokio::test]
async fn non_get_head_method_is_405_with_allow_header() {
    let resp = app()
        .oneshot(
            Request::builder()
                .method("POST")
                .uri("/build/assets/app-AAA.js")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(resp.status(), 405);
    assert_eq!(resp.headers().get("allow").unwrap(), "GET, HEAD");
}

#[tokio::test]
async fn invalid_mime_override_falls_back_instead_of_panicking() {
    let a = EmbeddedAssets::new(|p: &str| (p == "x.bad").then_some(Cow::Borrowed(b"hi" as &[u8])))
        .mime("bad", "text/bad\r\ninjected: 1");
    let app = Router::new().nest_service("/build", a);
    let resp = app
        .oneshot(
            Request::builder()
                .uri("/build/x.bad")
                .body(Body::empty())
                .unwrap(),
        )
        .await
        .unwrap();
    assert_eq!(resp.status(), 200);
    assert_eq!(
        resp.headers().get("content-type").unwrap(),
        "application/octet-stream"
    );
}