use axum::{
body::Body,
http::{Request, StatusCode},
};
use http_body_util::BodyExt;
use sloc_web::make_test_router_with_readonly_key;
use tower::ServiceExt;
const FULL: &str = "full-access-key-0123456789";
const RO: &str = "read-only-key-0123456789";
#[tokio::test]
async fn readonly_key_authenticates_a_safe_get() {
let app = make_test_router_with_readonly_key(FULL, RO);
let resp = app
.oneshot(
Request::get("/api-docs")
.header("authorization", format!("Bearer {RO}"))
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(
resp.status(),
StatusCode::OK,
"read-only key must authenticate a safe GET"
);
}
#[tokio::test]
async fn readonly_key_via_x_api_key_header_also_authenticates_get() {
let app = make_test_router_with_readonly_key(FULL, RO);
let resp = app
.oneshot(
Request::get("/api-docs")
.header("x-api-key", RO)
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(resp.status(), StatusCode::OK);
}
#[tokio::test]
async fn readonly_key_is_forbidden_on_a_state_changing_post() {
let app = make_test_router_with_readonly_key(FULL, RO);
let resp = app
.oneshot(
Request::post("/analyze")
.header("x-api-key", RO)
.header("content-type", "application/x-www-form-urlencoded")
.body(Body::from("path=."))
.unwrap(),
)
.await
.unwrap();
assert_eq!(
resp.status(),
StatusCode::FORBIDDEN,
"read-only credential must be 403 on a POST"
);
let bytes = resp.into_body().collect().await.unwrap().to_bytes();
let body = String::from_utf8_lossy(&bytes);
assert!(
body.to_lowercase().contains("read-only"),
"403 body should explain the read-only restriction: {body}"
);
}
#[tokio::test]
async fn full_access_key_passes_auth_on_a_post() {
let app = make_test_router_with_readonly_key(FULL, RO);
let resp = app
.oneshot(
Request::post("/analyze")
.header("authorization", format!("Bearer {FULL}"))
.header("content-type", "application/x-www-form-urlencoded")
.body(Body::from("path=."))
.unwrap(),
)
.await
.unwrap();
assert_ne!(resp.status(), StatusCode::UNAUTHORIZED);
assert_ne!(
resp.status(),
StatusCode::FORBIDDEN,
"full-access key must not be method-restricted"
);
}
#[tokio::test]
async fn no_credential_is_rejected() {
let app = make_test_router_with_readonly_key(FULL, RO);
let resp = app
.oneshot(Request::get("/api-docs").body(Body::empty()).unwrap())
.await
.unwrap();
let status = resp.status();
assert!(
status == StatusCode::UNAUTHORIZED || status.is_redirection(),
"no credential must be 401 or redirect, got {status}"
);
}