use axum::{
body::Body,
http::{header, Request, StatusCode},
};
use tower::ServiceExt;
use datasynth_server::grpc::service::{default_generator_config, SynthService};
use datasynth_server::rest::create_router;
fn test_router() -> axum::Router {
let config = default_generator_config();
let service = SynthService::new(config);
create_router(service)
}
#[tokio::test]
async fn test_metrics_websocket_endpoint_exists() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("GET")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Non-upgrade request to WebSocket endpoint should fail"
);
}
#[tokio::test]
async fn test_events_websocket_endpoint_exists() {
let router = test_router();
let request = Request::builder()
.uri("/ws/events")
.method("GET")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Non-upgrade request to WebSocket endpoint should fail"
);
}
#[tokio::test]
async fn test_metrics_websocket_requires_upgrade_header() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("GET")
.header(header::CONNECTION, "keep-alive")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Should require upgrade header"
);
}
#[tokio::test]
async fn test_events_websocket_requires_upgrade_header() {
let router = test_router();
let request = Request::builder()
.uri("/ws/events")
.method("GET")
.header(header::CONNECTION, "keep-alive")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Should require upgrade header"
);
}
#[tokio::test]
async fn test_websocket_partial_upgrade_headers_rejected() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("GET")
.header(header::CONNECTION, "upgrade")
.header(header::UPGRADE, "websocket")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Should require complete WebSocket headers"
);
}
#[tokio::test]
async fn test_metrics_websocket_rejects_post() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("POST")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert_eq!(
response.status(),
StatusCode::METHOD_NOT_ALLOWED,
"POST should not be allowed on WebSocket endpoint"
);
}
#[tokio::test]
async fn test_events_websocket_rejects_post() {
let router = test_router();
let request = Request::builder()
.uri("/ws/events")
.method("POST")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert_eq!(
response.status(),
StatusCode::METHOD_NOT_ALLOWED,
"POST should not be allowed on WebSocket endpoint"
);
}
#[tokio::test]
async fn test_metrics_websocket_rejects_put() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("PUT")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert_eq!(
response.status(),
StatusCode::METHOD_NOT_ALLOWED,
"PUT should not be allowed on WebSocket endpoint"
);
}
#[tokio::test]
async fn test_events_websocket_rejects_delete() {
let router = test_router();
let request = Request::builder()
.uri("/ws/events")
.method("DELETE")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert_eq!(
response.status(),
StatusCode::METHOD_NOT_ALLOWED,
"DELETE should not be allowed on WebSocket endpoint"
);
}
#[tokio::test]
async fn test_metrics_websocket_upgrade_request_recognized() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("GET")
.header(header::CONNECTION, "upgrade")
.header(header::UPGRADE, "websocket")
.header("sec-websocket-version", "13")
.header("sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ==")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status() == StatusCode::SWITCHING_PROTOCOLS
|| response.status() == StatusCode::UPGRADE_REQUIRED,
"WebSocket upgrade request should be recognized (got {})",
response.status()
);
}
#[tokio::test]
async fn test_events_websocket_upgrade_request_recognized() {
let router = test_router();
let request = Request::builder()
.uri("/ws/events")
.method("GET")
.header(header::CONNECTION, "upgrade")
.header(header::UPGRADE, "websocket")
.header("sec-websocket-version", "13")
.header("sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ==")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status() == StatusCode::SWITCHING_PROTOCOLS
|| response.status() == StatusCode::UPGRADE_REQUIRED,
"WebSocket upgrade request should be recognized (got {})",
response.status()
);
}
#[tokio::test]
async fn test_websocket_wrong_version_rejected() {
let router = test_router();
let request = Request::builder()
.uri("/ws/metrics")
.method("GET")
.header(header::CONNECTION, "upgrade")
.header(header::UPGRADE, "websocket")
.header("sec-websocket-version", "8")
.header("sec-websocket-key", "dGhlIHNhbXBsZSBub25jZQ==")
.body(Body::empty())
.unwrap();
let response = router.oneshot(request).await.unwrap();
assert!(
response.status().is_client_error(),
"Should reject old WebSocket version"
);
}