#![allow(clippy::unwrap_used)] #![allow(clippy::cast_precision_loss)] #![allow(clippy::cast_sign_loss)] #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_possible_wrap)] #![allow(clippy::cast_lossless)] #![allow(clippy::missing_panics_doc)] #![allow(clippy::missing_errors_doc)] #![allow(missing_docs)] #![allow(clippy::items_after_statements)] #![allow(clippy::used_underscore_binding)] #![allow(clippy::needless_pass_by_value)]
mod test_helpers;
use std::env;
use reqwest::StatusCode;
use test_helpers::*;
fn get_test_base_url() -> String {
env::var("FRAISEQL_TEST_URL").unwrap_or_else(|_| "http://localhost:8000".to_string())
}
fn fraiseql_server_available() -> bool {
env::var("FRAISEQL_TEST_URL").is_ok()
}
#[tokio::test]
async fn test_health_endpoint_responds() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client.get(format!("{}/health", base_url)).send().await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::OK);
let json = resp
.json::<serde_json::Value>()
.await
.expect("health response should be valid JSON");
assert_health_response(&json);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_metrics_endpoint_responds() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let token = get_metrics_token();
let response = client
.get(format!("{}/metrics", base_url))
.header("Authorization", format!("Bearer {}", token))
.send()
.await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::OK);
let content_type = resp
.headers()
.get("content-type")
.expect("metrics response should have Content-Type header");
let ct_str = content_type.to_str().unwrap();
assert!(
ct_str.contains("text/plain") || ct_str.contains("application/openmetrics"),
"metrics Content-Type should be Prometheus format, got: {ct_str}"
);
let text = resp.text().await.expect("metrics response should have a body");
assert!(
text.contains("fraiseql_graphql_queries_total"),
"metrics body should contain Prometheus metric names"
);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_metrics_json_endpoint_responds() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let token = get_metrics_token();
let response = client
.get(format!("{}/metrics/json", base_url))
.header("Authorization", format!("Bearer {}", token))
.send()
.await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::OK);
let json = resp
.json::<serde_json::Value>()
.await
.expect("metrics JSON response should be valid JSON");
assert_metrics_response(&json);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_metrics_endpoint_requires_auth() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client.get(format!("{}/metrics", base_url)).send().await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::UNAUTHORIZED);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_metrics_endpoint_rejects_invalid_token() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client
.get(format!("{}/metrics", base_url))
.header("Authorization", "Bearer wrong-token")
.send()
.await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::FORBIDDEN);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_invalid_path_returns_404() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client.get(format!("{}/invalid/path", base_url)).send().await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::NOT_FOUND);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_graphql_endpoint_accepts_post() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let request = create_graphql_request("{ users { id name } }", None, None);
let response = client.post(format!("{}/graphql", base_url)).json(&request).send().await;
match response {
Ok(resp) => {
assert_eq!(resp.status(), StatusCode::OK);
let json = resp
.json::<serde_json::Value>()
.await
.expect("GraphQL response should be valid JSON");
assert_graphql_response(&json);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_graphql_endpoint_rejects_get() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client.get(format!("{}/graphql", base_url)).send().await;
match response {
Ok(resp) => {
assert_ne!(resp.status(), StatusCode::OK);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_response_headers_correct() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let request = create_graphql_request("{ users { id name } }", None, None);
let response = client.post(format!("{}/graphql", base_url)).json(&request).send().await;
match response {
Ok(resp) => {
let content_type = resp.headers().get("content-type");
assert!(content_type.is_some());
if let Some(ct) = content_type {
let ct_str = ct.to_str().unwrap_or("");
assert!(ct_str.contains("application/json"));
}
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_empty_query_returns_error() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let request = create_graphql_request("", None, None);
let response = client.post(format!("{}/graphql", base_url)).json(&request).send().await;
match response {
Ok(resp) => {
let status = resp.status();
assert!(
status == StatusCode::OK || status == StatusCode::BAD_REQUEST,
"Expected OK or BAD_REQUEST, got {}",
status
);
let body = resp.json::<serde_json::Value>().await;
if let Ok(json) = body {
if status == StatusCode::OK {
assert!(json.get("errors").is_some());
}
}
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_malformed_json_returns_error() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client
.post(format!("{}/graphql", base_url))
.body("{invalid json")
.header("Content-Type", "application/json")
.send()
.await;
match response {
Ok(resp) => {
assert!(resp.status().is_client_error());
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_introspection_endpoint_responds() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let response = client.post(format!("{}/introspection", base_url)).send().await;
match response {
Ok(resp) => {
let status = resp.status();
assert!(
status == StatusCode::OK
|| status == StatusCode::BAD_REQUEST
|| status == StatusCode::METHOD_NOT_ALLOWED,
"Introspection should return 200, 400, or 405; got {status}"
);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}
#[tokio::test]
async fn test_concurrent_health_requests() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let futures: Vec<_> = (0..10)
.map(|_| {
let client = client.clone();
let url = format!("{}/health", base_url);
async move { client.get(url).send().await }
})
.collect();
let results = futures::future::join_all(futures).await;
let successful = results.iter().filter(|r| r.is_ok()).count();
assert_eq!(
successful, 10,
"all concurrent health requests should succeed, got {successful}/10"
);
}
#[tokio::test]
async fn test_content_type_consistency() {
if !fraiseql_server_available() {
eprintln!("skipped: FRAISEQL_TEST_URL not set");
return;
}
let client = create_test_client();
let base_url = get_test_base_url();
let request = create_graphql_request("{ users { id name } }", None, None);
let response = client.post(format!("{}/graphql", base_url)).json(&request).send().await;
match response {
Ok(resp) => {
let content_type = resp
.headers()
.get("content-type")
.expect("GraphQL response should have Content-Type header");
let ct_str = content_type.to_str().unwrap();
assert!(
ct_str.contains("application/json"),
"GraphQL response Content-Type should be application/json, got: {ct_str}"
);
},
Err(e) => {
eprintln!("Warning: Could not connect to server: {}", e);
},
}
}