use super::super::router;
use super::test_state;
use axum::body::Body;
use axum::http::{Request, StatusCode};
use tower::util::ServiceExt;
#[tokio::test]
async fn rpc_rejects_cross_origin_write() {
let app = router().with_state(test_state());
let resp = app
.oneshot(
Request::builder()
.method("POST")
.uri("/rpc")
.header("origin", "http://evil.example.com")
.header("content-type", "application/json")
.body(Body::from(
r#"{"jsonrpc":"2.0","id":1,"method":"tools/list"}"#,
))
.unwrap(),
)
.await
.unwrap();
assert_eq!(
resp.status(),
StatusCode::FORBIDDEN,
"cross-origin POST /rpc must be rejected by the write guard"
);
}
#[tokio::test]
async fn rpc_allows_loopback_and_missing_origin() {
let body = || Body::from(r#"{"jsonrpc":"2.0","id":1,"method":"tools/list"}"#);
let app = router().with_state(test_state());
let loopback = app
.oneshot(
Request::builder()
.method("POST")
.uri("/rpc")
.header("origin", "http://127.0.0.1:7070")
.header("content-type", "application/json")
.body(body())
.unwrap(),
)
.await
.unwrap();
assert_ne!(
loopback.status(),
StatusCode::FORBIDDEN,
"loopback-origin POST /rpc must pass the guard"
);
let app = router().with_state(test_state());
let missing = app
.oneshot(
Request::builder()
.method("POST")
.uri("/rpc")
.header("content-type", "application/json")
.body(body())
.unwrap(),
)
.await
.unwrap();
assert_ne!(
missing.status(),
StatusCode::FORBIDDEN,
"missing-Origin POST /rpc (server-side caller) must pass the guard"
);
}
#[tokio::test]
async fn read_route_allows_cross_origin() {
let app = router().with_state(test_state());
let resp = app
.oneshot(
Request::builder()
.method("GET")
.uri("/api/v1/status")
.header("origin", "http://evil.example.com")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_ne!(
resp.status(),
StatusCode::FORBIDDEN,
"cross-origin GET /api/v1/status must not be blocked by the write guard"
);
}