#[cfg(feature = "http")]
mod http_tests {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use sdforge::http::build;
use tower::ServiceExt;
#[tokio::test]
async fn test_http_server_builds() {
let app = build();
assert!(
!std::ptr::eq(&app, std::ptr::null()),
"HTTP app should build successfully"
);
}
#[test]
fn test_http_build_sync() {
let app = build();
assert!(
!std::ptr::eq(&app, std::ptr::null()),
"HTTP sync build should succeed"
);
}
#[tokio::test]
async fn test_basic_router_get_endpoint() {
async fn handler() -> &'static str {
"Hello, Integration Test!"
}
let app = Router::new().route("/test", get(handler));
let response = app
.oneshot(Request::builder().uri("/test").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), 1024)
.await
.unwrap();
assert_eq!(&body[..], b"Hello, Integration Test!");
}
#[tokio::test]
async fn test_multiple_routes() {
async fn hello() -> &'static str {
"Hello"
}
async fn world() -> &'static str {
"World"
}
let app = Router::new()
.route("/hello", get(hello))
.route("/world", get(world));
let response1 = app
.clone()
.oneshot(
Request::builder()
.uri("/hello")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response1.status(), StatusCode::OK);
let response2 = app
.oneshot(
Request::builder()
.uri("/world")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response2.status(), StatusCode::OK);
}
#[tokio::test]
async fn test_404_for_nonexistent_route() {
let app = Router::new();
let response = app
.oneshot(
Request::builder()
.uri("/nonexistent")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_http_request_performance() {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use tower::ServiceExt;
async fn handler() -> &'static str {
"OK"
}
let app = Router::new().route("/perf", get(handler));
let start = std::time::Instant::now();
for _ in 0..1000 {
let response = app
.clone()
.oneshot(Request::builder().uri("/perf").body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
}
let elapsed = start.elapsed();
assert!(elapsed < std::time::Duration::from_millis(500));
}
#[tokio::test]
async fn test_concurrent_http_requests() {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use tower::ServiceExt;
async fn handler() -> &'static str {
"Concurrent OK"
}
let app = Router::new().route("/concurrent", get(handler));
let mut handles = vec![];
for i in 0..20 {
let app_clone = app.clone();
let handle = tokio::spawn(async move {
let response = app_clone
.oneshot(
Request::builder()
.uri("/concurrent")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
(i, response.status())
});
handles.push(handle);
}
for handle in handles {
let (idx, status) = handle.await.unwrap();
assert_eq!(status, StatusCode::OK, "Request {} should succeed", idx);
}
}
#[tokio::test]
async fn test_large_response_body() {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use tower::ServiceExt;
async fn handler() -> String {
"x".repeat(100_000) }
let app = Router::new().route("/large", get(handler));
let response = app
.oneshot(
Request::builder()
.uri("/large")
.body(Body::empty())
.unwrap(),
)
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), 200_000)
.await
.unwrap();
assert_eq!(body.len(), 100_000);
}
#[tokio::test]
async fn test_multiple_route_patterns() {
use axum::{
body::Body,
http::{Request, StatusCode},
routing::get,
Router,
};
use tower::ServiceExt;
async fn h1() -> &'static str {
"Route 1"
}
async fn h2() -> &'static str {
"Route 2"
}
async fn h3() -> &'static str {
"Route 3"
}
async fn h4() -> &'static str {
"Route 4"
}
async fn h5() -> &'static str {
"Route 5"
}
let app = Router::new()
.route("/api/v1/resource1", get(h1))
.route("/api/v1/resource2", get(h2))
.route("/api/v2/resource1", get(h3))
.route("/api/v2/resource2", get(h4))
.route("/health", get(h5));
let routes = vec![
("/api/v1/resource1", "Route 1"),
("/api/v1/resource2", "Route 2"),
("/api/v2/resource1", "Route 3"),
("/api/v2/resource2", "Route 4"),
("/health", "Route 5"),
];
for (path, expected) in routes {
let response = app
.clone()
.oneshot(Request::builder().uri(path).body(Body::empty()).unwrap())
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
let body = axum::body::to_bytes(response.into_body(), 1024)
.await
.unwrap();
assert_eq!(&body[..], expected.as_bytes());
}
}
}
#[cfg(all(feature = "http", feature = "timestamp"))]
mod timestamp_tests {
use sdforge::http::build;
#[test]
fn test_timestamp_feature_enabled() {
let app = build();
assert!(
!std::ptr::eq(&app, std::ptr::null()),
"HTTP app with timestamp should build"
);
}
}
#[cfg(all(feature = "http", not(feature = "timestamp")))]
mod no_timestamp_tests {
use sdforge::http::build;
#[test]
fn test_timestamp_feature_disabled() {
let app = build();
assert!(
!std::ptr::eq(&app, std::ptr::null()),
"HTTP app without timestamp should build"
);
}
}
#[cfg(all(feature = "http", feature = "streaming"))]
mod streaming_tests {
use sdforge::http::build;
#[test]
fn test_streaming_feature_enabled() {
let app = build();
assert!(
!std::ptr::eq(&app, std::ptr::null()),
"HTTP app with streaming should build"
);
}
}