//! SPA serving: static assets, client-route fallback, API 404s, method //! checks. Uses $FBNG_DIST (the dev-mode asset directory) via a tempdir so //! the test is independent of any built frontend. mod common; use axum::http::StatusCode; use common::*; #[tokio::test] async fn spa_fallback_and_api_guards() { let env = Env::new().await; let c = Client::new(env.app.clone()); // Unknown /api/ endpoints get a plain 404, not the SPA page. let r = c.get("/api/unknown/endpoint").await; assert_eq!(r.status, StatusCode::NOT_FOUND); assert_eq!(r.text(), "unknown endpoint"); // Non-GET to a non-API path → 405. let r = c .raw(axum::http::Method::POST, "/some/page", &[], b"x".to_vec()) .await; assert_eq!(r.status, StatusCode::METHOD_NOT_ALLOWED); // Point the dev asset dir at a controlled tempdir. let dist = tempfile::tempdir().unwrap(); std::fs::write(dist.path().join("index.html"), "DIST-INDEX").unwrap(); std::fs::write(dist.path().join("app.css"), "body{}").unwrap(); std::env::set_var("FBNG_DIST", dist.path()); // Root serves index.html. let r = c.get("/").await; assert_eq!(r.status, StatusCode::OK); assert_eq!(r.text(), "DIST-INDEX"); assert_eq!(r.header("content-type").as_deref(), Some("text/html")); assert_eq!(r.header("cache-control").as_deref(), Some("no-cache")); // A known asset is served with the right type. let r = c.get("/app.css").await; assert_eq!(r.status, StatusCode::OK); assert_eq!(r.text(), "body{}"); assert_eq!(r.header("content-type").as_deref(), Some("text/css")); // Unknown paths fall back to index.html (SPA client routes, deep links). let r = c.get("/some/deep/client/route").await; assert_eq!(r.status, StatusCode::OK); assert_eq!(r.text(), "DIST-INDEX"); assert_eq!(r.header("cache-control").as_deref(), Some("no-cache")); let r = c.get("/s/abc123token/deeper/path").await; assert_eq!(r.status, StatusCode::OK); assert_eq!(r.text(), "DIST-INDEX"); // Now with an *empty* dist dir → the friendly "build the frontend" hint. let empty = tempfile::tempdir().unwrap(); std::env::set_var("FBNG_DIST", empty.path()); let r = c.get("/").await; assert_eq!(r.status, StatusCode::OK); assert!(r.text().contains("frontend has not been built")); std::env::remove_var("FBNG_DIST"); }