LoVisual/backend/gateway/tests/proxy.rs

107 lines
3.1 KiB
Rust

mod common;
use axum::http::StatusCode;
async fn server() -> axum_test::TestServer {
let accounts = common::spawn_echo().await;
let configs = common::spawn_echo().await;
let app = gateway::build_app(&common::config(&accounts, &configs), common::no_devices());
axum_test::TestServer::new(app)
}
#[tokio::test]
async fn health_is_ok() {
server().await.get("/health").await.assert_status_ok();
}
#[tokio::test]
async fn forwards_method_path_query_and_body() {
let res = server().await.put("/configs/2?x=1").text("payload").await;
res.assert_status_ok();
let echo: serde_json::Value = res.json();
assert_eq!(echo["method"], "PUT");
assert_eq!(echo["path"], "/configs/2");
assert_eq!(echo["query"], "x=1");
assert_eq!(echo["body"], "payload");
}
#[tokio::test]
async fn adds_internal_key_and_strips_spoofed_identity() {
let res = server()
.await
.post("/auth/login")
.add_header("x-lovisual-account-id", uuid::Uuid::new_v4().to_string())
.add_header("x-lovisual-internal-key", "spoofed")
.await;
let echo: serde_json::Value = res.json();
assert_eq!(echo["internal_key"], common::INTERNAL_KEY);
assert!(echo["account_id"].is_null());
}
#[tokio::test]
async fn unknown_prefix_is_404() {
server()
.await
.get("/nope")
.await
.assert_status(StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn dead_upstream_is_502() {
let app = gateway::build_app(
&common::config("http://127.0.0.1:1", "http://127.0.0.1:1"),
common::no_devices(),
);
axum_test::TestServer::new(app)
.get("/me")
.await
.assert_status(StatusCode::BAD_GATEWAY);
}
#[tokio::test]
async fn oversized_body_is_413() {
let big = "x".repeat(6 * 1024 * 1024 + 1);
server()
.await
.put("/configs/1")
.text(big)
.await
.assert_status(StatusCode::PAYLOAD_TOO_LARGE);
}
#[tokio::test]
async fn cors_preflight_allows_only_the_site_origin() {
let app = gateway::build_app(
&common::config("http://127.0.0.1:1", "http://127.0.0.1:1"),
common::no_devices(),
);
let s = axum_test::TestServer::new(app);
let ok = s
.method(axum::http::Method::OPTIONS, "/auth/login")
.add_header("origin", "http://localhost:5173")
.add_header("access-control-request-method", "POST")
.await;
assert_eq!(
ok.header("access-control-allow-origin"),
"http://localhost:5173"
);
assert_eq!(ok.header("access-control-allow-credentials"), "true");
let evil = s
.method(axum::http::Method::OPTIONS, "/auth/login")
.add_header("origin", "https://evil.example")
.add_header("access-control-request-method", "POST")
.await;
assert!(evil.maybe_header("access-control-allow-origin").is_none());
}
#[tokio::test]
async fn cors_exposes_retry_after_so_js_can_read_it() {
let s = server().await;
let res = s
.post("/auth/login")
.add_header("origin", "http://localhost:5173")
.await;
assert_eq!(res.header("access-control-expose-headers"), "retry-after");
}