use std::sync::Arc;
use axum::body::Body;
use http::Method;
use http::Request;
use http::StatusCode;
use http_body_util::BodyExt;
use serde_json::Value;
use tempfile::tempdir;
use tower::Service;
use super::*;
fn create_test_router() -> Router {
let temp_dir = tempdir().expect("Failed to create temp dir");
let output_directory = temp_dir.path();
create_router(
output_directory,
Arc::from("test: config"),
Arc::new("type Query { test: String }".to_string()),
)
}
async fn make_request(
router: &mut Router,
method: Method,
uri: &str,
) -> (StatusCode, String, http::HeaderMap) {
let request = Request::builder()
.method(method)
.uri(uri)
.body(Body::empty())
.expect("Failed to build request");
let response = Service::call(router, request)
.await
.expect("Request failed");
let status = response.status();
let headers = response.headers().clone();
let body_bytes = response
.into_body()
.collect()
.await
.expect("Failed to read body")
.to_bytes();
let body = String::from_utf8_lossy(&body_bytes).to_string();
(status, body, headers)
}
#[tokio::test]
async fn test_dashboard_endpoint_returns_html() {
let mut router = create_test_router();
let (status, body, headers) = make_request(&mut router, Method::GET, "/").await;
assert_eq!(status, StatusCode::OK);
assert!(body.contains("<!DOCTYPE html>") || body.contains("<html"));
assert!(
headers
.get(http::header::CONTENT_TYPE)
.unwrap()
.to_str()
.unwrap()
.contains("text/html")
);
}
#[tokio::test]
async fn test_system_info_endpoint() {
let mut router = create_test_router();
let (status, body, headers) = make_request(&mut router, Method::GET, "/system_info.txt").await;
assert_eq!(status, StatusCode::OK);
assert!(body.contains("SYSTEM INFORMATION"));
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"text/plain; charset=utf-8"
);
}
#[tokio::test]
async fn test_router_config_endpoint() {
let mut router = create_test_router();
let (status, body, headers) =
make_request(&mut router, Method::GET, "/router_config.yaml").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "test: config");
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"text/yaml; charset=utf-8"
);
}
#[tokio::test]
async fn test_supergraph_schema_endpoint() {
let mut router = create_test_router();
let (status, body, headers) =
make_request(&mut router, Method::GET, "/supergraph.graphql").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "type Query { test: String }");
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"text/plain; charset=utf-8"
);
}
#[tokio::test]
async fn test_memory_status_endpoint_returns_json() {
let mut router = create_test_router();
let (status, body, headers) = make_request(&mut router, Method::GET, "/memory/status").await;
assert!(status == StatusCode::OK || status == StatusCode::NOT_IMPLEMENTED);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON: {}", body);
if status == StatusCode::OK {
let json = json.unwrap();
assert!(json.get("status").is_some());
}
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"application/json"
);
}
#[tokio::test]
async fn test_memory_start_endpoint() {
let mut router = create_test_router();
let (status, body, _) = make_request(&mut router, Method::POST, "/memory/start").await;
assert!(
status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR
|| status == StatusCode::NOT_IMPLEMENTED
);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON");
}
#[tokio::test]
async fn test_memory_stop_endpoint() {
let mut router = create_test_router();
let (status, body, _) = make_request(&mut router, Method::POST, "/memory/stop").await;
assert!(
status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR
|| status == StatusCode::NOT_IMPLEMENTED
);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON");
}
#[tokio::test]
async fn test_memory_dump_endpoint() {
let mut router = create_test_router();
let (status, body, _) = make_request(&mut router, Method::POST, "/memory/dump").await;
assert!(
status == StatusCode::OK
|| status == StatusCode::INTERNAL_SERVER_ERROR
|| status == StatusCode::NOT_IMPLEMENTED
);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON");
}
#[tokio::test]
async fn test_memory_list_dumps_endpoint() {
let mut router = create_test_router();
let (status, body, _) = make_request(&mut router, Method::GET, "/memory/dumps").await;
assert!(status == StatusCode::OK || status == StatusCode::NOT_IMPLEMENTED);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON");
}
#[tokio::test]
async fn test_memory_clear_dumps_endpoint() {
let mut router = create_test_router();
let (status, body, _) = make_request(&mut router, Method::DELETE, "/memory/dumps").await;
assert!(status == StatusCode::OK || status == StatusCode::NOT_IMPLEMENTED);
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(json.is_ok(), "Response should be valid JSON");
}
#[tokio::test]
async fn test_memory_download_dump_not_found() {
let mut router = create_test_router();
let (status, _, _) =
make_request(&mut router, Method::GET, "/memory/dumps/nonexistent.prof").await;
assert!(status == StatusCode::NOT_FOUND || status == StatusCode::NOT_IMPLEMENTED);
}
#[tokio::test]
async fn test_memory_download_dump_invalid_filename() {
let mut router = create_test_router();
let (status, _, _) = make_request(
&mut router,
Method::GET,
"/memory/dumps/../../../etc/passwd",
)
.await;
assert_eq!(status, StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_memory_delete_dump_invalid_filename() {
let mut router = create_test_router();
let (status, _, _) =
make_request(&mut router, Method::DELETE, "/memory/dumps/../secret.txt").await;
assert_eq!(status, StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_export_endpoint_returns_archive() {
let mut router = create_test_router();
let (status, body, headers) = make_request(&mut router, Method::GET, "/export").await;
assert_eq!(status, StatusCode::OK);
assert!(!body.is_empty(), "Export should return data");
let content_type = headers.get(http::header::CONTENT_TYPE).unwrap();
assert!(content_type.to_str().unwrap().contains("gzip"));
let content_disposition = headers.get(http::header::CONTENT_DISPOSITION);
assert!(content_disposition.is_some());
let disposition_value = content_disposition.unwrap().to_str().unwrap();
assert!(disposition_value.contains("attachment"));
assert!(disposition_value.contains("filename"));
assert!(disposition_value.contains("router-diagnostics"));
}
#[tokio::test]
async fn test_fallback_serves_javascript_resources() {
let mut router = create_test_router();
let (status, body, headers) =
make_request(&mut router, Method::GET, "/backtrace-processor.js").await;
assert_eq!(status, StatusCode::OK);
assert!(!body.is_empty());
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"application/javascript; charset=utf-8"
);
}
#[tokio::test]
async fn test_fallback_serves_css_resources() {
let mut router = create_test_router();
let (status, body, headers) = make_request(&mut router, Method::GET, "/styles.css").await;
assert_eq!(status, StatusCode::OK);
assert!(!body.is_empty());
assert_eq!(
headers.get(http::header::CONTENT_TYPE).unwrap(),
"text/css; charset=utf-8"
);
}
#[tokio::test]
async fn test_fallback_returns_404_for_unknown_resources() {
let mut router = create_test_router();
let (status, _, _) = make_request(&mut router, Method::GET, "/unknown-file.js").await;
assert_eq!(status, StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_invalid_route_returns_404() {
let mut router = create_test_router();
let (status, _, _) = make_request(&mut router, Method::GET, "/invalid/route").await;
assert_eq!(status, StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_wrong_http_method_on_post_endpoint() {
let mut router = create_test_router();
let (status, _, _) = make_request(&mut router, Method::GET, "/memory/start").await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
}
#[tokio::test]
async fn test_wrong_http_method_on_get_endpoint() {
let mut router = create_test_router();
let (status, _, _) = make_request(&mut router, Method::POST, "/system_info.txt").await;
assert_eq!(status, StatusCode::METHOD_NOT_ALLOWED);
}
#[test]
fn test_result_to_response_success() {
let result: Result<&str, String> = Ok("success");
let response = result_to_response(result, "Error message");
assert_eq!(response.status(), StatusCode::OK);
}
#[test]
fn test_result_to_response_error() {
let result: Result<&str, String> = Err("test error".to_string());
let response = result_to_response(result, "Operation failed");
assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_not_found_response() {
let response = not_found_response("File not found");
assert_eq!(response.status(), StatusCode::NOT_FOUND);
}
#[tokio::test]
async fn test_router_with_empty_config() {
let temp_dir = tempdir().expect("Failed to create temp dir");
let router = create_router(temp_dir.path(), Arc::from(""), Arc::new(String::new()));
let mut router_service = router;
let (status, body, _) =
make_request(&mut router_service, Method::GET, "/router_config.yaml").await;
assert_eq!(status, StatusCode::OK);
assert_eq!(body, "");
}
#[tokio::test]
async fn test_json_responses_are_valid_json() {
let mut router = create_test_router();
let json_endpoints = vec![
(Method::GET, "/memory/status"),
(Method::GET, "/memory/dumps"),
(Method::POST, "/memory/start"),
(Method::POST, "/memory/stop"),
(Method::POST, "/memory/dump"),
(Method::DELETE, "/memory/dumps"),
];
for (method, endpoint) in json_endpoints {
let (status, body, _) = make_request(&mut router, method, endpoint).await;
if status == StatusCode::OK || status == StatusCode::INTERNAL_SERVER_ERROR {
let json: Result<Value, _> = serde_json::from_str(&body);
assert!(
json.is_ok(),
"Endpoint {} should return valid JSON, got: {}",
endpoint,
body
);
}
}
}