use axum::http::Method;
use mockforge_http::build_router;
use reqwest::Client;
use serde_json::json;
use std::net::SocketAddr;
use std::time::Duration;
use tokio::net::TcpListener;
use tokio::time::timeout;
#[tokio::test]
async fn test_malformed_json_request_body() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let malformed_bodies = vec![
"{ invalid json }",
"{'key': 'value'}", "{key: value}", "{\"key\": }", "{\"key\":}", "{\"key\"}", "{", "}", "null", "undefined", ];
for body in malformed_bodies {
let response = client
.post(format!("{}/api/test", base_url))
.header("Content-Type", "application/json")
.body(body)
.send()
.await;
if let Ok(resp) = response {
assert!(resp.status().is_client_error() || resp.status().is_server_error());
}
}
drop(server);
}
#[tokio::test]
async fn test_very_large_request_body() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let large_body = "a".repeat(10_000_000);
let large_json = json!({"data": large_body});
let response = client
.post(format!("{}/api/test", base_url))
.header("Content-Type", "application/json")
.json(&large_json)
.timeout(Duration::from_secs(30))
.send()
.await;
let _ = response;
drop(server);
}
#[tokio::test]
async fn test_malformed_headers() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::builder().danger_accept_invalid_certs(true).build().unwrap();
let base_url = format!("http://{}", addr);
let response = client
.get(format!("{}/health", base_url))
.header("Content-Type", "application/json; charset=utf-8")
.send()
.await;
assert!(response.is_ok());
drop(server);
}
#[tokio::test]
async fn test_invalid_http_methods() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let methods = vec!["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
for method_str in methods {
let method = Method::from_bytes(method_str.as_bytes()).unwrap();
let response = client.request(method, format!("{}/api/test", base_url)).send().await;
let _ = response;
}
drop(server);
}
#[tokio::test]
async fn test_malformed_query_parameters() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let malformed_queries = vec![
"?",
"?=",
"?key=",
"?=value",
"?key=value&",
"?key=value&&key2=value2",
"?key=value&key2",
"?%",
"?%XX", "?key=value%", ];
for query in malformed_queries {
let url = format!("{}/api/test{}", base_url, query);
let response = client.get(&url).send().await;
let _ = response;
}
drop(server);
}
#[tokio::test]
async fn test_malformed_path_parameters() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let malformed_paths = vec![
"/api//users",
"/api/users//",
"/api//users//",
"/api/users/",
"/api/users/%",
"/api/users/%XX",
"/api/users/../../../etc/passwd",
"/api/users/..",
"/api/users/./test",
];
for path in malformed_paths {
let url = format!("{}{}", base_url, path);
let response = client.get(&url).send().await;
let _ = response;
}
drop(server);
}
#[tokio::test]
async fn test_concurrent_requests_during_shutdown() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server_handle = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let mut handles = vec![];
for _i in 0..10 {
let url = format!("{}/health", base_url);
let client_clone = client.clone();
let handle = tokio::spawn(async move {
for _ in 0..10 {
let _ = client_clone.get(&url).send().await;
}
});
handles.push(handle);
}
tokio::time::sleep(Duration::from_millis(50)).await;
drop(server_handle);
for handle in handles {
let _ = timeout(Duration::from_secs(5), handle).await;
}
}
#[tokio::test]
async fn test_timeout_handling() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::builder().timeout(Duration::from_millis(100)).build().unwrap();
let base_url = format!("http://{}", addr);
let response =
timeout(Duration::from_secs(1), client.get(format!("{}/health", base_url)).send()).await;
let _ = response;
drop(server);
}
#[tokio::test]
async fn test_missing_content_type_header() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let response = client.post(format!("{}/api/test", base_url)).body("raw body data").send().await;
let _ = response;
drop(server);
}
#[tokio::test]
async fn test_unicode_in_paths_and_headers() {
let router = build_router(None, None, None).await;
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let server = tokio::spawn(async move {
axum::serve(listener, router.into_make_service_with_connect_info::<SocketAddr>())
.await
.unwrap()
});
tokio::time::sleep(Duration::from_millis(100)).await;
let client = Client::new();
let base_url = format!("http://{}", addr);
let unicode_paths = vec!["/api/测试", "/api/тест", "/api/テスト", "/api/🎉"];
for path in unicode_paths {
let url = format!("{}{}", base_url, path);
let response = client.get(&url).send().await;
let _ = response;
}
drop(server);
}