api_spa.rs
⎇
Raw
1//! SPA serving: static assets, client-route fallback, API 404s, method
2//! checks. Uses $FBNG_DIST (the dev-mode asset directory) via a tempdir so
3//! the test is independent of any built frontend.
4
5mod common;
6
7use axum::http::StatusCode;
8use common::*;
9
10#[tokio::test]
11async fn spa_fallback_and_api_guards() {
12 let env = Env::new().await;
13 let c = Client::new(env.app.clone());
14
15 // Unknown /api/ endpoints get a plain 404, not the SPA page.
16 let r = c.get("/api/unknown/endpoint").await;
17 assert_eq!(r.status, StatusCode::NOT_FOUND);
18 assert_eq!(r.text(), "unknown endpoint");
19
20 // Non-GET to a non-API path → 405.
21 let r = c
22 .raw(axum::http::Method::POST, "/some/page", &[], b"x".to_vec())
23 .await;
24 assert_eq!(r.status, StatusCode::METHOD_NOT_ALLOWED);
25
26 // Point the dev asset dir at a controlled tempdir.
27 let dist = tempfile::tempdir().unwrap();
28 std::fs::write(dist.path().join("index.html"), "DIST-INDEX").unwrap();
29 std::fs::write(dist.path().join("app.css"), "body{}").unwrap();
30 std::env::set_var("FBNG_DIST", dist.path());
31
32 // Root serves index.html.
33 let r = c.get("/").await;
34 assert_eq!(r.status, StatusCode::OK);
35 assert_eq!(r.text(), "DIST-INDEX");
36 assert_eq!(r.header("content-type").as_deref(), Some("text/html"));
37 assert_eq!(r.header("cache-control").as_deref(), Some("no-cache"));
38
39 // A known asset is served with the right type.
40 let r = c.get("/app.css").await;
41 assert_eq!(r.status, StatusCode::OK);
42 assert_eq!(r.text(), "body{}");
43 assert_eq!(r.header("content-type").as_deref(), Some("text/css"));
44
45 // Unknown paths fall back to index.html (SPA client routes, deep links).
46 let r = c.get("/some/deep/client/route").await;
47 assert_eq!(r.status, StatusCode::OK);
48 assert_eq!(r.text(), "DIST-INDEX");
49 assert_eq!(r.header("cache-control").as_deref(), Some("no-cache"));
50 let r = c.get("/s/abc123token/deeper/path").await;
51 assert_eq!(r.status, StatusCode::OK);
52 assert_eq!(r.text(), "DIST-INDEX");
53
54 // Now with an *empty* dist dir → the friendly "build the frontend" hint.
55 let empty = tempfile::tempdir().unwrap();
56 std::env::set_var("FBNG_DIST", empty.path());
57 let r = c.get("/").await;
58 assert_eq!(r.status, StatusCode::OK);
59 assert!(r.text().contains("frontend has not been built"));
60
61 std::env::remove_var("FBNG_DIST");
62}
63