#![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)]
use fraiseql_server::{
routes::graphql::GraphQLRequest,
validation::{ComplexityValidationError, RequestValidator},
};
fn standard_profile_validator() -> RequestValidator {
RequestValidator::new()
.with_max_depth(15)
.with_max_complexity(1000)
.with_depth_validation(true)
.with_complexity_validation(true)
}
fn regulated_profile_validator() -> RequestValidator {
RequestValidator::new()
.with_max_depth(10)
.with_max_complexity(500)
.with_depth_validation(true)
.with_complexity_validation(true)
}
fn restricted_profile_validator() -> RequestValidator {
RequestValidator::new()
.with_max_depth(5)
.with_max_complexity(250)
.with_depth_validation(true)
.with_complexity_validation(true)
}
#[test]
fn test_standard_profile_allows_deep_queries() {
let validator = standard_profile_validator();
let deep_query = "{
posts {
id
author {
id
profile {
bio
settings {
theme {
dark {
mode
}
}
}
}
}
}
}";
validator
.validate_query(deep_query)
.unwrap_or_else(|e| panic!("STANDARD profile should allow depth 12, got: {e}"));
}
#[test]
fn test_standard_profile_rejects_excessive_depth() {
let validator = standard_profile_validator();
let excessive_query = "{ a { b { c { d { e { f { g { h { i { j { k { l { m { n { o { p } } } } } } } } } } } } } } } }";
let result = validator.validate_query(excessive_query);
match result {
Err(ComplexityValidationError::QueryTooDeep {
max_depth,
actual_depth,
}) => {
assert_eq!(max_depth, 15);
assert!(actual_depth > 15, "actual_depth ({actual_depth}) should exceed max (15)");
},
other => panic!(
"STANDARD profile should reject excessive depth with QueryTooDeep, got: {other:?}"
),
}
}
#[test]
fn test_regulated_profile_rejects_deep_queries() {
let validator = regulated_profile_validator();
let deep_query = "{ a { b { c { d { e { f { g { h { i { j { k } } } } } } } } } } }";
let result = validator.validate_query(deep_query);
match result {
Err(ComplexityValidationError::QueryTooDeep {
max_depth,
actual_depth,
}) => {
assert_eq!(max_depth, 10);
assert!(actual_depth > 10, "actual_depth ({actual_depth}) should exceed max (10)");
},
other => {
panic!("REGULATED profile should reject depth > 10 with QueryTooDeep, got: {other:?}")
},
}
}
#[test]
fn test_regulated_profile_allows_boundary_depth() {
let validator = regulated_profile_validator();
let boundary_query = "{
posts {
id
author {
id
profile {
bio
settings {
theme {
dark {
mode
}
}
}
}
}
}
}";
validator
.validate_query(boundary_query)
.unwrap_or_else(|e| panic!("REGULATED profile should allow depth 10, got: {e}"));
}
#[test]
fn test_restricted_profile_max_depth_enforcement() {
let validator = restricted_profile_validator();
let at_limit = "{ a { b { c { d { e } } } } }";
validator
.validate_query(at_limit)
.unwrap_or_else(|e| panic!("RESTRICTED profile should allow depth 5, got: {e}"));
let over_limit = "{ a { b { c { d { e { f } } } } } }";
assert!(
matches!(
validator.validate_query(over_limit),
Err(ComplexityValidationError::QueryTooDeep { .. })
),
"RESTRICTED profile should reject depth 6 with QueryTooDeep"
);
}
#[test]
fn test_standard_profile_complexity_limit() {
let validator = standard_profile_validator();
let simple = "{ posts { id title } }";
validator
.validate_query(simple)
.unwrap_or_else(|e| panic!("STANDARD profile should allow simple query, got: {e}"));
let moderate =
"{ posts { id title author { id name email } comments { id text user { id name } } } }";
validator
.validate_query(moderate)
.unwrap_or_else(|e| panic!("STANDARD profile should allow moderate complexity, got: {e}"));
}
#[test]
fn test_regulated_profile_complexity_limit() {
let validator = regulated_profile_validator();
let simple = "{ posts { id title } }";
validator
.validate_query(simple)
.unwrap_or_else(|e| panic!("REGULATED profile should allow simple query, got: {e}"));
let moderate = "{ posts { id title author { id name } } }";
validator
.validate_query(moderate)
.unwrap_or_else(|e| panic!("REGULATED profile should allow moderate complexity, got: {e}"));
}
#[test]
fn test_restricted_profile_complexity_limit() {
let validator = restricted_profile_validator();
let simple = "{ posts { id title } }";
validator
.validate_query(simple)
.unwrap_or_else(|e| panic!("RESTRICTED profile should allow simple query, got: {e}"));
let light = "{ posts { id title author { id } } }";
validator
.validate_query(light)
.unwrap_or_else(|e| panic!("RESTRICTED profile should allow light complexity, got: {e}"));
}
#[test]
fn test_profile_query_validator_builder_pattern() {
let standard = RequestValidator::new().with_max_depth(15).with_max_complexity(1000);
let regulated = RequestValidator::new().with_max_depth(10).with_max_complexity(500);
let restricted = RequestValidator::new().with_max_depth(5).with_max_complexity(250);
standard
.validate_query("{ posts { id } }")
.unwrap_or_else(|e| panic!("standard validator should accept simple query, got: {e}"));
regulated
.validate_query("{ posts { id } }")
.unwrap_or_else(|e| panic!("regulated validator should accept simple query, got: {e}"));
restricted
.validate_query("{ posts { id } }")
.unwrap_or_else(|e| panic!("restricted validator should accept simple query, got: {e}"));
}
#[test]
fn test_disabling_depth_validation() {
let validator = RequestValidator::new()
.with_max_depth(5)
.with_depth_validation(false) .with_complexity_validation(true);
let excessive = "{
a { b { c { d { e { f { g { h { i { j } } } } } } } } }
}";
validator
.validate_query(excessive)
.unwrap_or_else(|e| panic!("depth validation disabled, should allow any depth, got: {e}"));
}
#[test]
fn test_disabling_complexity_validation() {
let validator = RequestValidator::new()
.with_max_depth(15)
.with_max_complexity(10)
.with_complexity_validation(false);
let complex = "{ posts { id title author { id name } comments { id text user { id } } } }";
validator.validate_query(complex).unwrap_or_else(|e| {
panic!("complexity validation disabled, should allow any complexity, got: {e}")
});
}
#[test]
fn test_graphql_request_structure_with_profiles() {
let request = GraphQLRequest {
query: Some("{ posts { id title author { id name } } }".to_string()),
variables: None,
operation_name: None,
extensions: None,
document_id: None,
};
let validator = regulated_profile_validator();
validator.validate_query(request.query.as_deref().unwrap()).unwrap_or_else(|e| {
panic!("REGULATED profile should allow structured request query, got: {e}")
});
}
#[test]
fn test_profile_limits_with_variables() {
let validator = restricted_profile_validator();
let query_with_vars = "query($id: ID!, $limit: Int!) {
post(id: $id) {
id
title
author {
id
name
}
}
}";
validator.validate_query(query_with_vars).unwrap_or_else(|e| {
panic!("RESTRICTED profile should allow query with variables, got: {e}")
});
}
#[test]
fn test_error_message_contains_profile_limits() {
let validator = regulated_profile_validator();
let excessive_depth = "{
a { b { c { d { e { f { g { h { i { j { k } } } } } } } } } }
}";
let result = validator.validate_query(excessive_depth);
match result {
Err(ComplexityValidationError::QueryTooDeep {
max_depth,
actual_depth,
}) => {
assert_eq!(max_depth, 10, "Error should show REGULATED profile limit of 10");
assert!(actual_depth > 10, "actual_depth ({actual_depth}) should exceed max (10)");
},
other => {
panic!("Expected QueryTooDeep error with REGULATED profile limits, got: {other:?}")
},
}
}
#[test]
fn test_multiple_validators_independent() {
let strict = restricted_profile_validator();
let lenient = standard_profile_validator();
let moderate_query = "{
posts {
id
author {
id
profile {
bio
settings {
theme {
dark
}
}
}
}
}
}";
lenient
.validate_query(moderate_query)
.unwrap_or_else(|e| panic!("STANDARD profile should allow moderate query, got: {e}"));
assert!(
matches!(
strict.validate_query(moderate_query),
Err(ComplexityValidationError::QueryTooDeep { .. })
),
"RESTRICTED profile should reject moderate query with QueryTooDeep"
);
}