#![allow(clippy::unwrap_used)]
use std::sync::Arc;
use axum::body::Body;
use fraiseql_core::schema::CompiledSchema;
use fraiseql_test_utils::failing_adapter::FailingAdapter;
use http::{Request, StatusCode};
use tower::ServiceExt;
use crate::{server::Server, server_config::ServerConfig};
const PERSISTED_DOC: &str = "{ users { id } }";
async fn persisted_only_server(dir: &tempfile::TempDir) -> Server {
use sha2::Digest as _;
let hash = hex::encode(sha2::Sha256::digest(PERSISTED_DOC.as_bytes()));
let manifest = serde_json::json!({
"version": 1,
"documents": { format!("sha256:{hash}"): PERSISTED_DOC }
});
let manifest_path = dir.path().join("manifest.json");
std::fs::write(&manifest_path, serde_json::to_string(&manifest).unwrap()).unwrap();
let mut schema = CompiledSchema::new();
let security = fraiseql_core::schema::SecurityConfig {
persisted_queries_only: true,
trusted_documents: Some(fraiseql_core::schema::TrustedDocumentsConfig {
enabled: true,
mode: fraiseql_core::schema::TrustedDocumentMode::Permissive,
manifest_path: Some(manifest_path.to_str().unwrap().to_string()),
..Default::default()
}),
..fraiseql_core::schema::SecurityConfig::default()
};
schema.security = Some(security);
let config = ServerConfig {
cors_enabled: false,
enable_http_query: true,
..ServerConfig::default()
};
Box::pin(Server::new(config, schema, Arc::new(FailingAdapter::new()), None))
.await
.expect("Server::new should succeed with a trusted-documents manifest")
}
async fn adhoc_status(server: &Server, method: &str) -> StatusCode {
let state = server.build_app_state();
let app = server.build_graphql_router(&state);
let request = if method == "GET" {
let uri = format!("/graphql?query={}", urlencoding::encode("{ adhoc { id } }"));
Request::builder().method("GET").uri(uri).body(Body::empty()).unwrap()
} else {
Request::builder()
.method(method)
.uri("/graphql")
.header("content-type", "application/json")
.body(Body::from(r#"{"query":"{ adhoc { id } }"}"#))
.unwrap()
};
app.oneshot(request).await.unwrap().status()
}
#[tokio::test]
async fn adhoc_operations_are_refused_on_every_http_method() {
let dir = tempfile::tempdir().unwrap();
let server = persisted_only_server(&dir).await;
for method in ["POST", "GET", "QUERY"] {
assert_eq!(
adhoc_status(&server, method).await,
StatusCode::BAD_REQUEST,
"persisted-only mode must refuse an ad-hoc document over {method}"
);
}
}
#[tokio::test]
async fn persisted_document_id_still_dispatches() {
use sha2::Digest as _;
let dir = tempfile::tempdir().unwrap();
let server = persisted_only_server(&dir).await;
let state = server.build_app_state();
let app = server.build_graphql_router(&state);
let hash = hex::encode(sha2::Sha256::digest(PERSISTED_DOC.as_bytes()));
let body = serde_json::json!({ "documentId": format!("sha256:{hash}") });
let request = Request::builder()
.method("POST")
.uri("/graphql")
.header("content-type", "application/json")
.body(Body::from(body.to_string()))
.unwrap();
let status = app.oneshot(request).await.unwrap().status();
assert_ne!(
status,
StatusCode::BAD_REQUEST,
"an allow-listed documentId must dispatch in persisted-only mode"
);
}